华恩JAVA班第65天

来源:互联网 发布:网络贷款平台哪个靠谱 编辑:程序博客网 时间:2024/06/05 07:22

华恩JAVA班第65天

 

JDBC 是一种低级 API ,是高级 API 的基础。JDBC 是个“低级”接口,也就是说,它用于直接调用 SQL命令。在这方面它的功能极佳,并比其它的数据库连接 API易于使用,但它同时也被设计为一种基础接口,在它之上可以建立高级接口和工具。 

JDBC最大的特点是独立于具体的关系数据库,要通过JDBC来存取某一特定的数据库,必须有相应的JDBCDriver,这往往是生产数据库的厂商提供,是连接JDBCAPI与具体数据库的桥梁。 

JDBC驱动程序的四种类型: 
1.      第一种类型的驱动程序的实现是通过将JDBC的调用全部委托给其它编程接口来实现的,比如ODBC。这种类型的驱动程序需要安装本地代码库,即依赖于本地的程序,所以便携性较差。比如JDBC-ODBC桥驱动程序。 
调用顺序: 
Application->JDBC-ODBC->JDBC-ODBC Library->ODBCDriver->DB 
适用于快速的原型系统和没有提供JDBC驱动的数据库,如Access. 

2.      第二种类型的驱动程序的实现是部分基于Java语言的。即该驱动程序一部分是用Java语言编写,其它部分委托本地的数据库的客户端代码来实现。同类型1的驱动一样,该类型的驱动程序也依赖本地的程序,所以便携性较差. 
调用顺序: 
Application->JDBC Driver2->Native DataBaseLibrary->DB 

3.      第三种类型的驱动程序的实现是全部基于JAVA语言的。该类型的驱动程序通常由某个中间件服务器提供,这样客户端程序可以使用数据库无关的协议和中间件服务器进行通信,中间件服务器再将客户端的JDBC调用转发给数据库进行处理 
调用顺序: 
Application->JDBC Driver3->Java middleware->JDBCDriver->DB 
4.      第四种类型的驱动程序的实现是全部基于JAVA语言的。该类型的驱动程序中包含了特定数据库的访问协议,使得客户端可以直接和数据库进行通信 
调用顺序: 
Application->JDBC Driver4->database Engine->DB 


JDBC 相关的类: 
DriverManager:驱动程序管理器。这个是一个实现类,它是一个工厂类,用来生产Driver对象的,这个类的结构设计模式为工厂方法。 

Driver:这是驱动程序对象的接口,它指向一个实实在在的数据库驱动程序对象,那么这个数据库驱动程序对象是从哪里来的呢? 
DriverManager工厂中有个方法:getDriver(StringURL),通过这个方法可以得到驱动程序对象,这个方法是在各个数据库厂商按JDBC规范设计的数据库驱动程序包里的类中静态实现的,也就是在静态块中。 

Connection:这个接口可以制向一个数据库连接对象,那么如何得到这个连接对象呢? 
是通过DriverManager工厂中的getConnection(String URL)方法得到的 

Statement:用于执行静态的SQL语句的接口,通过Connection中的createStatement方法得到的 

Resultset:用于指向结果集对象的接口,结果集对象是通过Statement中的execute等方法得到的 

二、jdbc的典型使用方法 

JDBC 操作数据库的一般步骤: 
1) 注册驱动(应用程序启动只做一次) 
2) 建立连接(Connection) 
3) 创建执行sql的语句(Statement, PreStatement) 
4) 执行语句(增删改查) 
5) 处理执行结果(ResultSet) 
6) 释放资源 

1. 注册驱动 
从上面的介绍可以看出,程序要与任何数据库连接都要有相应的驱动程序。这个驱动程序需要注册到能被驱动程序管理器(DriverManager)找到。 
DriverManager类包含一列Driver类,它们已通过调用方法DriverManager.registerDriver对自己进行了注册。所有Driver类都必须包含有一个静态部分。它创建该类的实例,然后在加载该实例时DriverManager类进行注册。这样,用户正常情况下将不会直接调用DriverManager.registerDriver;而是在加载驱动程序时由驱动程序自动调用。 

有三种方式注册驱动,其中第一种是推荐的方式,另两种仅作了解: 
1)最常用的方式(这将显式地加载驱动程序类。由于这与外部设置无关,因此推荐使用这种加载驱动程序的方法) 
Class.forName(“com.mysql.jdbc.Driver”); // 以mysql驱动做示例。 
2)直接创建对象 
newcom.mysql.jdbc.Driver();//创建driver对象,加载数据库驱动  
Stringurl="jdbc:mysql://localhost:3306/databasename";//数据库连接子协议  
Connectionconn=DriverManager.getConnection(url,"username","password");  
不推荐理由:由newcom.mysql.jdbc.Driver()可以知道,这里需要创建一个类的实例。创建类的实例就需要在java文件中将该类通过import导入,否则就会报错,即采用这种方式,程序在编译的时候不能脱离驱动类包,为程序切换到其他数据库带来麻烦 

3) 设置系统属性 
System.setProperty("jdbc.driver","com.mysql.jdbc.Driver");//系统属性指定数据库驱动  
Stringurl="jdbc:mysql://localhost:3306/databasename";//数据库连接子协议  
Connectionconn=DriverManager.getConnection(url,"username","password");  
可以同时导入多个jdbc驱动,中间用冒号“:”分开,比如: 
System.setProperty("jdbc.drivers","XXXDriver:XXXDriver:XXXDriver"); 
这样就一次注册了三个数据库驱动。 

注:新加载的 Driver 类都要通过调用 DriverManager.registerDriver()类进行自我注册,但实际使用中都不需要显式调用,因为这三种方式都采用自己的方式自动调用了这个注册函数。比如第二种方式: 
com.mysql.jdbc.Driver类的静态代码快里面已经进行了修改的操作 
static {  
        try {  
               java.sql.DriverManager.registerDriver(newDriver());  
           } catch (SQLException E) 
 
                    throw new RuntimeException("Can't registerdriver!");  
               
       
DriverManager是一个驱动管理器,内部有一个驱动注册表,map结构,可以向其注册多个驱动。 

2. 建立连接 
Connection 对象代表与数据库的连接。连接过程包括所执行的 SQL语句和在该连接上所返回的结果。一个应用程序可与单个数据库有一个或多个连接,或者可与许多数据库有连接。打开连接与数据库建立连接的标准方法是调用DriverManager.getConnection方法。该方法接受含有某个URL 的字符串。DriverManager 类(即所谓的 JDBC管理层)将尝试找到可与那个 URL所代表的数据库进行连接的驱动程序。DriverManager 类存有已注册的 Driver 类的清单。当调用方法getConnection 时,它将检查清单中的每个驱动程序,直到找到可与URL 中指定的数据库进行连接的驱动程序为止。Driver的方法connect 使用这个 URL来建立实际的连接。 
建立连接方法: 
Stringurl="jdbc:mysql://localhost:3306/databasename";//数据库连接子协议  
Connectionconn=DriverManager.getConnection(url,"username","password");  

Jdbc url规范: 
Jdbc 的url遵循一定的规范,jdbc:< 子协议 >:< 子名称 > 
JDBC URL 的三个部分可分解如下: 
(1)jdbc --协议。JDBC URL 中的协议总是 jdbc。 

(2)<子协议> -- 驱动程序名或数据库连接机制(这种机制可由一个或多个驱动程序支持)的名称。子协议名的典型示例是"odbc",该名称是为用于指定 ODBC 风格的数据资源名称的 URL 专门保留的。例如,为了通过JDBC-ODBC桥来访问某个数据库,可以用如下所示的 URL: 
jdbc:odbc:fred 
  本例中,子协议为 "odbc",子名称 "fred" 是本地ODBC 数据资源。 

   如果要用网络命名服务(这样 JDBC URL 中的数据库名称不必是实际名称),则命名服务可以作为子协议。例如,可用如下所示的URL :jdbc:dcenaming:accounts-payable本例中,该 URL 指定了本地 DCE命名服务应该将数据库名称 "accounts-payable" 解析为更为具体的可用于连接真实数据库的名称。 

(3)<子名称> --一种标识数据库的方法。子名称可以依不同的子协议而变化。它还可以有子名称的子名称(含有驱动程序编程员所选的任何内部语法)。使用子名称的目的是为定位数据库提供足够的信息。前例中,因为 ODBC 将提供其余部份的信息,因此用 "fred"就已足够。然而,位于远程服务器上的数据库需要更多的信息。例如,如果数据库是通过Internet 来访问的,则在 JDBC URL中应将网络地址作为子名称的一部份包括进去,且必须遵循如下所示的标准 URL 命名约定://主机名:端口/子协议假设 "dbnet"是个用于将某个主机连接到 Internet 上的协议,则 JDBC URL 类似: 

   jdbc:dbnet://wombat:356/fred 

特殊的"odbc" 子协议 
子协议odbc 是一种特殊情况。它是为用于指定 ODBC 风格的数据资源名称的 URL而保留的,并具有下列特性:允许在子名称(数据资源名称)后面指定任意多个属性值。odbc 子协议的完整语法为:jdbc:odbc:< 数据资源名称 >[;< 属性名 >=< 属性值>]* 
  因此,以下都是合法的 jdbc:odbc 名称: 
jdbc:odbc:qeor7jdbc:odbc:wombat 
jdbc:odbc:wombat;CacheSize=20;ExtensionCase=LOWER 
jdbc:odbc:qeora;UID=kgh;PWD=fooey 
一些典型db的配置示例可以参考: 
http://blog.csdn.net/ring0hx/article/details/6152528 

注:oracle中连接和会话的概念,jdbc中的Connection对应oracle中的会话,而不是连接。 
Oracle连接又分两种,从jdbcurl可以看出配置的不同,一种是thin连接,一种是OCI,区别在性能和对集群的支持上,可以参考: 
http://wyf289283641.iteye.com/blog/1911274
 

3.创建statement 
Statement 对象用于将 SQL 语句发送到数据库中。实际上有三种 Statement 对象,它们都作为在给定连接上执行 SQL语句的包容器:Statement、PreparedStatement(它从 Statement 继承而来)和CallableStatement(它从 PreparedStatement 继承而来)。它们都专用于发送特定类型的 SQL语句: 
(1)Statement 对象用于执行不带参数的简单 SQL 语句; 
(2)PreparedStatement 对象用于执行带或不带 IN 参数的预编译 SQL语句;能有效防止sql注入和较好的执行效率。 
注:所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。可以通过使用PreparedStatement,绑定变量等方式防止sql注入。具体相关内容需要查询专业书籍或其他资料。 
(3)CallableStatement 对象用于执行对数据库有存储过程的调用。 

下面分别是三种statement的创建方法: 
1)Statement创建 
Connection con = DriverManager.getConnection(url, "sunny",""); 
Statement stmt = con.createStatement(); 

2)PreparedStatement创建 
PreparedStatement 实例包含已编译的 SQL 语句。这就是使语句“准备好”。包含于 PreparedStatement对象中的 SQL 语句可具有一个或多个 IN 参数。IN参数的值在SQL 语句创建时未被指定。相反的,该语句为每个 IN参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的setXXX方法来提供。 
由于 PreparedStatement 对象已预编译过,所以其执行速度要快于 Statement 对象。因此,多次执行的 SQL语句经常创建为 PreparedStatement 对象,以提高效率。 
 PreparedStatement pstmt = con.prepareStatement("UPDATE table4 SETm = ? WHERE x = ?"); 
pstmt.setLong(1, 123456789); //1代表占位符的顺序,第一个占位符。 
pstmt.setLong(2, 100000000); 

3) CallableStatement ,这个用到的场景不多,本人没有用过。可以参考: 
http://dev.yesky.com/SoftChannel/72342371961929728/20040917/1855399.shtml 

4.处理结果 
ResultSet 包含符合 SQL 语句中条件的所有行,并且它通过一套 get 方法(这些 get方法可以访问当前行中的不同列)提供了对这些行中数据的访问。ResultSet.next 方法用于移动到 ResultSet中的下一行,使下一行成为当前行。 
java.sql.Statement stmt = conn.createStatement(); 
ResultSet r = stmt.executeQuery("SELECT a, b, c FROMTable1"); 
while (r.next()) 
{ 
 // 打印当前行的值。 
 int i = r.getInt("a"); 
 String s = r.getString("b"); 
 float f = r.getFloat("c"); 
 System.out.println("ROW = " + i + " " + s + " " + f); 
} 
ResultSet 维护指向其当前数据行的光标。每调用一次 next 方法,光标向下移动一行。最初它位于第一行之前,因此第一次调用next 将把光标置于第一行上,使它成为当前行。随着每次调用 next 导致光标向下移动一行,按照从上至下的次序获取ResultSet行。 

  在 ResultSet 对象或其父辈 Statement 对象关闭之前,光标一直保持有效。 

  在 SQL中,结果表的光标是有名字的。如果数据库允许定位更新或定位删除,则需要将光标的名字作为参数提供给更新或删除命令。可通过调用方法getCursorName获得光标名。 

5.释放资源 
数据库使用完毕需要释放资源,这些资源都是系统有限的宝贵资源,必须释放。释放jdbc资源的顺序RestltSet,Statement,Connection,基本是保证用完就关闭。其中Connection最重要,一定要关闭(线程池里连接的创建和关闭由池来管理,不需要程序员干预生命周期)。Connection关闭后ResultSet,Statement会自动关闭(但资源不会立即释放)。 
Connection的使用原则是尽量晚创建,尽量早释放。 
在关闭资源异常时,应该将资源赋null,确保资源最大可能的被释放。(GC去回收)
 

6.处理事务 
数据库的事务特性ACID,隔离级别这些本篇先不梳理了,后面梳理oracle时会单独整理。我们在使用时用的最多的是自动提交的设置。 
Connection conn = DriverManager.getConnection(); 
conn.setAutocommit(false); // 关闭自动提交,这种是推荐的做法 
//数据库操作 
 
conn.commit();//前面关闭了自动提交,这里就要自己控制提交或回滚(conn.rollback()) 
可以参考: 
http://blog.csdn.net/chenyongsuda/article/details/5641412 

事务还可分为本地事务和分布式事务,可以参考: 
http://jackyin5918.iteye.com/blog/1922042 

三、DataSource和JNDI(Java Naming and Directory) 

1.从上面的内容可以看出,如果要与数据库交互必须要获取数据库连接,上面是通过DriverManager.getConnection("url")获取的,需要把url传进去硬编码。DataSource是sun提供的替代DriverManager的方法,类全名是javax.sql.DataSource。javadoc描述的挺清楚: 
* A factory for connections to the physical data source thatthis 
* DataSource object represents.  An alternative tothe 
* DriverManager facility, a DataSource object 
* is the preferred means of getting a connection. An object thatimplements 
* the DataSource interface will typically be 
* registered with a naming service based on the 
* Java Naming and Directory (JNDI) API. 
通过DataSource获取Connection比DriverManager更灵活,它可以结合JNDI在配置文件里配置数据库的url,当数据库属性改变时不需要修改代码,只要修改配置文件就可以了。另一个优点就是对数据库连接池的支持了。比如spring的JdbcTemplate就是用DataSource来获取连接。 

2.JNDI(Java Naming and DirectoryInterface)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDISPI的实现,由管理者将JNDIAPI映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。集群JNDI实现了高可靠性JNDI[8],通过服务器的集群,保证了JNDI的负载平衡和错误恢复。在全局共享的方式下,集群中的一个应用服务器保证本地JNDI树的独立性,并拥有全局的JNDI树。每个应用服务器在把部署的服务对象绑定到自己本地的JNDI树的同时,还绑定到一个共享的全局JNDI树,实现全局JNDI和自身JNDI的联系。 
JNDI里最常用的类就是InitialContext(javax.naming.InitialContext). 
这样的描述仍然不能理解它的作用,只能通过使用去理解,其实所有的程序都是这样,实践,实践,实践。 

3.DataSource和JNDI结合的例子 
在Spring框架中有如下3种获得DataSource对象的方法: 
1)从JNDI获得DataSource. 
2)从第三方的连接池获得DataSource. 
3)使用DriverManagerDataSource获得DataSource. 
下面分别给出示例: 
1)从JNDI获得DataSource. 
step1、Spring引用JNDI数据源配置信息: 
 
     
java:comp/env/jcptDataSourceJNDI 
     
 
jcptDataSourceJNDI是tomcat或者其他应用服务器配置的JNDI. 

step2、关于JNDI的配置(tomcat): 
修改tomcat目录conf/context.xml文件: 
 
  maxActive="100" maxIdle="30"maxWait="10"  username="tysp" 
  password="12345678"driverClassName="oracle.jdbc.driver.OracleDriver" 
         url="jdbc:oracle:thin:@192.168.1.35:1521:orcl"/> 

step3、通过JNDI获取DataSource: 
Context context = new InitialContext(); 
DataSource ds =(DataSource)context.lookup("java:comp/env/jcptDataSourceJNDI"); 

2)使用DBCP连接池获取    
要在Spring中使用DBCP连接池,需要引入commons-collections.jar、commons-dbcp.jar和commons-pool.jar。 

 
     
     
     
     
     
     
     
     
 

 
     
         
     

     
       
          org.hibernate.dialect.Oracle9Dialect 
    
   true 
          true 
       
     
     
 
     
 

3)使用DriverManagerDataSource    
 
    
       oracle.jdbc.driver.OracleDriver 
    
    
  jdbc:oracle:thin:@192.168.1.35:orcl 
    
    
      or_meal 
    
    
       or_meal 
    
 

 

更多信息可以参见同学富晓磊的博客:http://blog.sina.com.cn/u/1798827371

原创粉丝点击