如果说上一章介绍了 Java 6 中的一个新成员,它本来就存在,但是没有被加入进 JDK。那么这一章,我们将关注在 JDBC 4.0 中又增加了哪些新功能以及与之相对应的新 API。
在 JDBC 4.0 之前,编写 JDBC 程序都需要加上以下这句有点丑陋的代码:
Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance(); |
Java.sql.DriverManager
的内部实现机制决定了这样代码的出现。只有先通过 Class.forName
找到特定驱动的 class 文件,DriverManager.getConnection
方法才能顺利地获得 Java 应用和数据库的连接。这样的代码为编写程序增加了不必要的负担,JDK 的开发者也意识到了这一点。从 Java 6 开始,应用程序不再需要显式地加载驱动程序了,DriverManager 开始能够自动地承担这项任务。作为试验,我们可以将 清单 1 中的相关代码删除,重新编译后在 JRE 6.0 下运行,结果和原先的程序一样。
好 奇的读者也许会问,DriverManager 为什么能够做到自动加载呢?这就要归功于一种被称为 Service Provider 的新机制。熟悉 Java 安全编程的程序员可能对其已经是司空见惯,而它现在又出现在 JDBC 模块中。JDBC 4.0 的规范规定,所有 JDBC 4.0 的驱动 jar 文件必须包含一个 java.sql.Driver
,它位于 jar 文件的 META-INF/services 目录下。这个文件里每一行便描述了一个对应的驱动类。其实,编写这个文件的方式和编写一个只有关键字(key)而没有值(value)的 properties 文件类似。同样地,‘#’之后的文字被认为是注释。有了这样的描述,DriverManager 就可以从当前在 CLASSPATH 中的驱动文件中找到,它应该去加载哪些类。而如果我们在 CLASSPATH 里没有任何 JDBC 4.0 的驱动文件的情况下,调用 清单 8 中的代码会输出一个 sun.jdbc.odbc.JdbcOdbcDriver
类型的对象。而仔细浏览 JDK 6 的目录,这个类型正是在 %JAVA_HOME%/jre/lib/resources.jar
的 META-INF/services 目录下的 java.sql.Driver
文件中描述的。也就是说,这是 JDK 中默认的驱动。而如果开发人员想使得自己的驱动也能够被 DriverManager 找到,只需要将对应的 jar 文件加入到 CLASSPATH 中就可以了。当然,对于那些 JDBC 4.0 之前的驱动文件,我们还是只能显式地去加载了。
Enumeration<Driver> drivers = DriverManager.getDrivers(); while(drivers.hasMoreElements()) { System.out.println(drivers.nextElement()); } |
熟悉 DB2、Oracle 等大型 DBMS 的人一定不会对 ROWID 这个概念陌生:它是数据表中一个“隐藏”的列,是每一行独一无二的标识,表明这一行的物理或者逻辑位置。由于 ROWID 类型的广泛使用,Java SE 6 中新增了 java.sql.RowId
的数据类型,允许 JDBC 程序能够访问 SQL 中的 ROWID 类型。诚然,不是所有的 DBMS 都支持 ROWID 类型。即使支持,不同的 ROWID 也会有不同的生命周期。因此使用 DatabaseMetaData.getRowIdLifetime
来判断类型的生命周期不失为一项良好的实践经验。我们在 清单 1 的程序获得连接之后增加以下代码,便可以了解 ROWID 类型的支持情况。
DatabaseMetaData meta = conn.getMetaData(); System.out.println(meta.getRowIdLifetime()); |
Java SE 6 的 API 规范中,java.sql.RowIdLifetime
规定了 5 种不同的生命周期:ROWID_UNSUPPORTED
、ROWID_VALID_FOREVER
、ROWID_VALID_OTHER
、ROWID_VALID_SESSION
和 ROWID_VALID_TRANSACTION
。从字面上不难理解它们表示了不支持 ROWID、ROWID 永远有效等等。具体的信息,还可以参看相关的 JavaDoc。读者可以尝试着连接 Derby 进行试验,会发现运行结果是 ROWID_UNSUPPORTED
,即 Derby 并不支持 ROWID。
既然提供了新的数据类型,那么一些相应的获取、更新数据表内容的新 API 也在 Java 6 中被添加进来。和其它已有的类型一样,在得到 ResultSet
或者 CallableStatement
之后,调用 get/set/update 方法得到/设置/更新 RowId 对象,示例的代码如 清单 10 所示。
// Initialize a PreparedStatement PreparedStatement pstmt = connection.prepareStatement( "SELECT rowid, name, score FROM hellotable WHERE rowid = ?"); // Bind rowid into prepared statement. pstmt.setRowId(1, rowid); // Execute the statement ResultSet rset = pstmt.executeQuery(); // List the records while(rs.next()) { RowId id = rs.getRowId(1); // get the immutable rowid object String name = rs.getString(2); int score = rs.getInt(3); } |
鉴于不同 DBMS 的不同实现,RowID 对象通常在不同的数据源(datasource)之间并不是可移植的。因此 JDBC 4.0 的 API 规范并不建议从连接 A 取出一个 RowID 对象,将它用在连接 B 中,以避免不同系统的差异而带来的难以解释的错误。而至于像 Derby 这样不支持 RowId 的 DBMS,程序将直接在 setRowId 方法处抛出 SQLFeatureNotSupportedException
。
SQL:2003 标准引入了 SQL/XML,作为 SQL 标准的扩展。SQL/XML 定义了 SQL 语言怎样和 XML 交互:如何创建 XML 数据;如何在 SQL 语句中嵌入 XQuery 表达式等等。作为 JDBC 4.0 的一部分,Java 6 增加了 java.sql.SQLXML
的类型。JDBC 应用程序可以利用该类型初始化、读取、存储 XML 数据。java.sql.Connection.createSQLXML
方法就可以创建一个空白的 SQLXML 对象。当获得这个对象之后,便可以利用 setString
、setBinaryStream
、setCharacterStream
或者 setResult
等方法来初始化所表示的 XML 数据。以 setCharacterStream
为例,清单 11 表示了一个 SQLXML 对象如何获取 java.io.Writer
对象,从外部的 XML 文件中逐行读取内容,从而完成初始化。
清单 11. 利用 setCharacterStream 方法来初始化 SQLXML 对象
SQLXML xml = con.createSQLXML(); Writer writer = xml.setCharacterStream(); BufferedReader reader = new BufferedReader(new FileReader("test.xml")); String line= null; while((line = reader.readLine() != null) { writer.write(line); } |
由于 SQLXML 对象有可能与各种外部的资源有联系,并且在一个事务中一直持有这些资源。为了防止应用程序耗尽资源,Java 6 提供了 free 方法来释放其资源。类似的设计在 java.sql.Array
、Clob
中都有出现。
至于如何使用 SQLXML 与数据库进行交互,其方法与其它的类型都十分相似。可以参照 RowId 一节 中的例子在 Java SE 6 的 API 规范中找到 SQLXML 中对应的 get/set/update 方法构建类似的程序,此处不再赘述。
在 Java SE 6 之前,有关 JDBC 的异常类型不超过 10 个。这似乎已经不足以描述日渐复杂的数据库异常情况。因此,Java SE 6 的设计人员对以 java.sql.SQLException
为根的异常体系作了大幅度的改进。首先,SQLException 新实现了 Iterable<Throwable>
接口。清单 12 实现了 清单 1 程序的异常处理机制。这样简洁地遍历了每一个 SQLException 和它潜在的原因(cause)。
清单 12. SQLException 的 for-each loop
// Java 6 code catch (Throwable e) { if (e instanceof SQLException) { for(Throwable ex : (SQLException)e ){ System.err.println(ex.toString()); } } } |
此外,图 4 表示了全部的 SQLException 异常体系。除去原有的 SQLException 的子类,Java 6 中新增的异常类被分为 3 种:SQLReoverableException
、SQLNonTransientException
、SQLTransientException
。在 SQLNonTransientException
和 SQLTransientException
之下还有若干子类,详细地区分了 JDBC 程序中可能出现的各种错误情况。大多数子类都会有对应的标准 SQLState
值,很好地将 SQL 标准和 Java 6 类库结合在一起。
在众多的异常类中,比较常见的有 SQLFeatureNotSupportedException
,用来表示 JDBC 驱动不支持某项 JDBC 的特性。例如在 Derby 下运行 清单 10 中的程序,就可以发现 Derby 的驱动并不支持 RowId 的特性。另外值得一提的是,SQLClientInfoException
直接继承自 SQLException,表示当一些客户端的属性不能被设置在一个数据库连接时所发生的异常。
![]() ![]() |
![]()
|
在本文中,我们已经向读者介绍了 Java SE 6 中 JDBC 最重要的一些新特性:它们包括嵌在 JDK 中的 Java DB (Derby)和 JDBC 4.0 的一部分。当然,还有很多本文还没有覆盖到的新特性。比如增加了对 SQL 语言中 NCHAR
、NVARCHAR
、LONGNVARCHAR
和 NCLOB
类型的支持;在数据库连接池的环境下为管理 Statement
对象提供更多灵活、便利的方法等。
此 外,在 Java SE 6 的 beta 版中,曾经将 Annotation Query 的特性包含进来。这项特性定义了一系列 Query 和 DataSet 接口,程序员可以通过撰写一些 Annotation 来自定义查询并获得定制的数据集结果。但是,由于这一特性的参考实现最终不能满足 JDK 的质量需求,Sun 公司忍痛割爱,取消了在 Java SE 6 中发布其的计划。我们有理由相信,在以后的 JDK 版本中,这一特性以及更多新的功能将被包含进来,利用 Java 语言构建数据库的应用也会变得更为自然、顺畅。
jdbc 连接数据库大全
http://www.javaresearch.org/article/51200.htm
Class.forName - 加载数据库驱动程序
//access数据库直连用ODBC的
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver") ;
String url="jdbc:odbc:Driver={MicroSoft Access Driver
(*.mdb)};DBQ="+application.getRealPath("/Data/ReportDemo.mdb");
Connection conn = DriverManager.getConnection(url,"","");
Statement stmtNew=conn.createStatement() ;
//Java DB Derby
Class.forName("org.apache.derby.jdbc.ClientDriver");
java.sql.DriverManager.getConnection("jdbc:derby://localhost:1527/myeclipse","classiccars","classiccars");
//连接oracle数据库
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
String url="jdbc:oracle:thin:@localhost:1521:orcl"; //orcl为你的数据库SID
//连接SQL Server数据库
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();
String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=TestDB"; //TestDB为你的数据库名
//连接DB2数据库
Class.forName("com.ibm.db2.jdbc.app.DB2Driver").newInstance();
String url="jdbc:db2://localhost:5000/TestDB"; //TestDB为你的数据库名
JDBC URL :jdbc:db2://tdurden:50000/SAMPLE
Driver Class field: com.ibm.db2.jcc.DB2Driver
//连接Informix数据库
Class.forName("com.informix.jdbc.IfxDriver").newInstance();
String url="jdbc:informix-sqli://localhost:1533/TestDB:INFORMIXSERVER=myserver");
//连接Sybase数据库
Class.forName("com.sybase.jdbc.SybDriver").newInstance();
String url="jdbc:sybase:Tds:localhost:5007/TestDB";
Properties sysProps=System.getProperties();
SysProps.put("user","asima");
SysProps.put("password","admin");
//连接MySQL数据库
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
String url="jdbc:mysql://localhost/TestDB?user=asima&password=admin&useUnicode=true&
characterEncoding=8859_1"
Class.forName("com.mysql.jdbc.Driver").netInstance();
String url = "jdbc:mysql://localhost/ssh?user=root&password=admin&userUnicode=true&characterEncoding=UTF-8"; //ssh 为数据库名
//连接PostgreSQL数据库
Class.forName("org.postgresql.Driver").newInstance();
String url="jdbc:postgresql://localhost/TestDB";
//使用JDBC-ODBC桥
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
String url="jdbc:odbc:TestOdbcName";
String user="asima";
String password="admin";
Connection conn=DriverManager.getConnection(url,user,password); //与DBMS建立连接
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql="select * from TestTable";
ResultSet rs=stmt.executeQuery(sql);
while (rs.next())
{
......
}
rs.close();
stmt.close();
conn.close();
Oracle.java
package com.javaeye.lindows.database; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class Oracle { /** * @author Lindows */ private static Connection conn = null; private static Statement stmt = null; private static ResultSet rs = null; public static void main(String[] args) { try { Class.forName("oracle.jdbc.driver.OracleDriver"); conn = DriverManager.getConnection( "jdbc:oracle:thin:@localhost:1521:orcl", "scott", "tiger"); System.out.println("conn ok..."); stmt = conn.createStatement(); rs = stmt.executeQuery("select * from dept"); while (rs.next()) { String deptno = rs.getString(1); String dname = rs.getString(2); String loc = rs.getString(3); System.out.println("\t部门编号:" + deptno + " \t部门名称:" + dname + " \t地点:" + loc); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { try { if (rs != null) { rs.close(); } if (ps != null) { stmt.close(); } if (conn != null) { conn.close(); } } catch (SQLException e2) { e2.printStackTrace(); } } } }
sdfsadfasdf
oracle.java 补充
finally { try { if (rs != null) { rs.close(); } } catch (Exception e) { //这里放可以log类打印到后台 } try { if (ps != null) { ps.close(); } } catch (Exception e) { //这里放可以log类打印到后台 } try { closeConnection(); } catch (SQLException e1) { //这里放可以log类打印到后台 } }
sdfsdaf
Mysql.java
package com.lindows.db; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class Mysql { /** * @param args */ public static Connection getConn() { Connection conn = null; String driverName = "com.mysql.jdbc.Driver"; String username = "root"; String password = ""; String dbName = "ssh"; String url = "jdbc:mysql://127.0.0.1:3306/" + dbName + "?user=" + username + "&password" + password; try { Class.forName(driverName).newInstance(); System.out.println("[创建驱动连接实例]>>>" + driverName); conn = DriverManager.getConnection(url); System.out.println(); System.out.println("[得到实例连接URL]>>>" + url+"\n"); } catch (Exception e) { System.out.println("db conn false"); e.printStackTrace(); } return conn; } public static void main(String[] args) { try { System.out.println("begin..."); Statement stmt = Mysql.getConn().createStatement(); System.out.println("[构造statement]>>>" + stmt + "\nover...\n"); } catch (SQLException e) { e.printStackTrace(); } // 第二种:直接连接 try { Class.forName("com.mysql.jdbc.Driver"); DriverManager.getConnection("jdbc:mysql://localhost:3306/ssh", "root", ""); System.out.println("ok"); } catch (Exception e) { e.printStackTrace(); } } }
tbl_user.sql
-- Create table create table TBL_USER ( TBL_ID NUMBER not null, TBL_USERNAME VARCHAR2(20), TBL_PASSWORD VARCHAR2(20) ) tablespace USERS pctfree 10 pctused 40 initrans 1 maxtrans 255 storage ( initial 64K minextents 1 maxextents unlimited ); -- Create/Recreate primary, unique and foreign key constraints alter table TBL_USER add constraint PK_UID primary key (TBL_ID) using index tablespace SYSTEM pctfree 10 initrans 2 maxtrans 255 storage ( initial 12K next 12K minextents 1 maxextents 249 pctincrease 50 );
DbcpUtil.java
package com.lindows.db; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.apache.commons.dbcp.BasicDataSource; import com.lindows.vo.UserBean; public class DbcpUtil { /** * @param args * @author Lindows */ private static BasicDataSource dataSource; private static Connection conn = null; private static Statement stmt = null; private static PreparedStatement pstmt = null; private static ResultSet rSet = null; static { dataSource = new BasicDataSource(); dataSource.setMaxActive(10); dataSource.setMinIdle(1); dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver"); dataSource.setUrl("jdbc:oracle:thin:@127.0.0.1:1521:oracle9i"); dataSource.setUsername("scott"); dataSource.setPassword("tiger"); } public Connection getConnection() { try { return dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); return null; } } // 单独写方法关闭Connection public void closeConnection(Connection conn) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } // 传参RPC,然后需要一起关闭 public void releaseRPC(ResultSet rSet, PreparedStatement pstmt, Connection conn) throws Exception { if (rSet != null) { rSet.close(); } if (pstmt != null) { pstmt.close(); } if (conn != null) { conn.close(); } } // 传参RSC,然后需要一起关闭 public void releaseRSC(ResultSet rSet, Statement stmt, Connection conn) throws Exception { if (rSet != null) { rSet.close(); } if (stmt != null) { stmt.close(); } if (conn != null) { conn.close(); } } // 可以关闭其中任意一个传参 public static void release(Object object) { try { if (object instanceof ResultSet) { ((ResultSet) object).close(); } else if (object instanceof Statement) { ((Statement) object).close(); } else if (object instanceof PreparedStatement) { ((PreparedStatement) object).close(); } else if (object instanceof Connection) { ((Connection) object).close(); } } catch (Exception e) { e.printStackTrace(); } } //tbl_user.sql public void findUser(int tbl_id, String username) throws Exception { Connection conn = null; PreparedStatement pstmt = null; ResultSet rSet = null; String sql = "SELECT * FROM tbl_user t WHERE t.tbl_id = ? and t.tbl_username = ?"; conn = dataSource.getConnection(); pstmt = conn.prepareStatement(sql); pstmt.setInt(1, tbl_id); //第一个问号索引 pstmt.setString(2, username); rSet = pstmt.executeQuery(); while (rSet.next()) { System.out.println(rSet.getString(1)); System.out.println(rSet.getString(2)); } } public static void main(String[] args) { DbcpUtil dbUtil = new DbcpUtil(); try { dbUtil.findUser(1, "tt"); } catch (Exception e) { e.printStackTrace(); } } }
数据库连接关闭顺序
http://topic.csdn.net/t/20040503/15/3029992.html
连接池原理图.rar
http://dl.iteye.com/topics/download/9f84c62e-20c3-3194-94c6-7dfdc0def9b8
end