`
dyllove98
  • 浏览: 1380614 次
  • 性别: Icon_minigender_1
  • 来自: 济南
博客专栏
73a48ce3-d397-3b94-9f5d-49eb2ab017ab
Eclipse Rcp/R...
浏览量:38265
4322ac12-0ba9-3ac3-a3cf-b2f587fdfd3f
项目管理checkList...
浏览量:78543
4fb6ad91-52a6-307a-9e4f-816b4a7ce416
哲理故事与管理之道
浏览量:131691
社区版块
存档分类
最新评论

Java回顾之JDBC

 
阅读更多

第一篇:Java回顾之I/O

第二篇:Java回顾之网络通信

第三篇:Java回顾之多线程

第四篇:Java回顾之多线程同步

第五篇:Java回顾之集合

第六篇:Java回顾之序列化

第七篇:Java回顾之反射

第八篇:Java回顾之一些基础概念

 

这篇文章里,我们来讨论一些和JDBC相关的话题。

概述

尽管在实际开发过程中,我们一般使用ORM框架来代替传统的JDBC,例如Hibernate或者iBatis,但JDBC是Java用来实现数据访问的基础,掌握它对于我们理解Java的数据操作流程很有帮助。

JDBC的全称是Java Database Connectivity。

JDBC对数据库进行操作的流程:

  • 连接数据库
  • 发送数据请求,即传统的CRUD指令
  • 返回操作结果集

JDBC中常用的对象包括:

  • ConnectionManager
  • Connection
  • Statement
  • CallableStatement
  • PreparedStatement
  • ResultSet
  • SavePoint

一个简单示例

我们来看下面一个简单的示例,它使用JDK自带的Derby数据库,创建一张表,插入一些记录,然后将记录返回:

一个简单的JDBC示例
 1 private static void test1() throws SQLException
 2 {
 3     String driver = "org.apache.derby.jdbc.EmbeddedDriver";
 4     String dbURL = "jdbc:derby:EmbeddedDB;create=true";
 5     
 6     Connection con = null;
 7     Statement st = null;
 8     try
 9     {
10         Class.forName(driver);
11         con = DriverManager.getConnection(dbURL);
12         st = con.createStatement();
13         st.execute("create table foo(ID INT NOT NULL, NAME VARCHAR(30))");
14         st.executeUpdate("insert into foo(ID,NAME) values(1, 'Zhang San')");
15         
16         ResultSet rs = st.executeQuery("select ID,NAME from foo");
17         
18         while(rs.next())
19         {
20             int id = rs.getInt("ID");
21             String name = rs.getString("NAME");
22             System.out.println("ID=" + id + "; NAME=" + name);
23         }
24     }
25     catch(Exception ex)
26     {
27         ex.printStackTrace();
28     }
29     finally
30     {
31         if (st != null) st.close();
32         if (con != null) con.close();
33     }
34 }

如何建立数据库连接

上面的示例代码中,建立数据库连接的部分如下:

String driver = "org.apache.derby.jdbc.EmbeddedDriver";
String dbURL = "jdbc:derby:EmbeddedDB;create=true";

Class.forName(driver);
con = DriverManager.getConnection(dbURL);

建立数据库连接的过程,可以分为两步:

1)加载数据库驱动,即上文中的driver以及Class.forName(dirver)

2)定位数据库连接字符串, 即dbURL以及DriverManager.getConnection(dbURL)

不同的数据库,对应的dirver和dbURL不同,但加载驱动和建立连接的方式是相同的,即只需要修改上面driver和dbURL的值就可以了。

自动加载数据库驱动

如果我们每次建立连接时,都要使用Class.forName(...)来手动加载数据库驱动,这样会很麻烦,我们可以通过配置文件的方式,来保存数据库驱动的信息。

我们可以在classpath中,即编译出来的.class的存放路径,添加如下文件:

META-INF\services\java.sql.Driver

对应的内容就是JDBC驱动的全路径,也就是上面driver变量的值:

org.apache.derby.jdbc.EmbeddedDriver

接下来,我们在程序中,就不需要再显示的用Class.forName(...)来加载驱动了,它会被自动加载进来,当我们的数据库发生变化时,只需要修改这个文件就可以了,例如当我们的数据库由Derby变为MySQL时,只需要将上述的配置修改为:

com.mysql.jdbc.Driver

但是,需要注意一点,这里只是配置了JDBC驱动的全路径,并没有包含jar文件的信息,因此,我们还是需要将包含该驱动的jar文件手动的放置到程序的classpath中。

JDBC中的基本操作

对于数据库操作来说,CRUD操作应该是最常见的操作了, 即我们常说的增、删、查、改。

JDBC是使用Statement和ResultSet来完成这些操作的。

如何实现CRUD

下面是一个实现CRUD的示例:

JDBC实现基本的CRUD示例
 1 private static void insertTest() throws SQLException
 2 {
 3     String dbURL = "jdbc:mysql://localhost/test";
 4     Connection con = DriverManager.getConnection(dbURL, "root", "123");
 5     Statement st = con.createStatement();
 6     st.execute("insert into user(ID,NAME) values(1, 'Zhang San')");
 7     st.execute("insert into user(ID,NAME) values(2, 'Li Si')");
 8     st.execute("insert into user(ID,NAME) values(3, 'Wang Wu')");
 9     System.out.println("=====insert test=====");
10     showUser(st);
11     st.close();
12     con.close();
13 }
14 
15 private static void deleteTest() throws SQLException
16 {
17     String dbURL = "jdbc:mysql://localhost/test";
18     Connection con = DriverManager.getConnection(dbURL, "root", "123");
19     Statement st = con.createStatement();
20     st.execute("delete from user where ID=3");
21     System.out.println("=====delete test=====");
22     showUser(st);
23     st.close();
24     con.close();
25 }
26 
27 private static void updateTest() throws SQLException
28 {
29     String dbURL = "jdbc:mysql://localhost/test";
30     Connection con = DriverManager.getConnection(dbURL, "root", "123");
31     Statement st = con.createStatement();
32     st.executeUpdate("update user set NAME='TEST' where ID=2");
33     System.out.println("=====update test=====");
34     showUser(st);
35     st.close();
36     con.close();
37 }
38 
39 private static void showUser(Statement st) throws SQLException
40 {
41     ResultSet rs = st.executeQuery("select ID, NAME from user");
42     while(rs.next())
43     {
44         int id = rs.getInt("ID");
45         String name = rs.getString("NAME");
46         System.out.println("ID:" + id + "; NAME=" + name);
47     }
48     rs.close();
49 }

我们顺序调用上面的测试方法:

1 insertTest();
2 deleteTest();
3 updateTest();

执行结果如下:

=====insert test=====
ID:1; NAME=Zhang San
ID:2; NAME=Li Si
ID:3; NAME=Wang Wu
=====delete test=====
ID:1; NAME=Zhang San
ID:2; NAME=Li Si
=====update test=====
ID:1; NAME=Zhang San
ID:2; NAME=TEST

上面代码中的showUser方法会把user表中的所有记录打印出来。

如何调用存储过程

存储过程是做数据库开发时经常使用的技术,它可以通过节省编译时间的方式来提升系统性能,我们这里的示例使用MySQL数据库。

如何调用不带参数的存储过程

假设我们现在有一个简单的存储过程,它只是返回user表中的所有记录,存储过程如下:

1 CREATE DEFINER=`root`@`localhost` PROCEDURE `GetUser`()
2 BEGIN
3 select ID,NAME from user;
4 END

我们可以使用CallableStatement来调用存储过程:

调用存储过程示例一
 1 private static void execStoredProcedureTest() throws SQLException
 2 {
 3     String dbURL = "jdbc:mysql://localhost/test";
 4     Connection con = DriverManager.getConnection(dbURL, "root", "123");
 5     CallableStatement cst = con.prepareCall("call GetUser()");
 6     ResultSet rs = cst.executeQuery();
 7     while(rs.next())
 8     {
 9         int id = rs.getInt("ID");
10         String name = rs.getString("NAME");
11         System.out.println("ID:" + id + "; NAME=" + name);
12     }
13     rs.close();
14     cst.close();
15     con.close();
16 }

它的执行结果如下:

ID:1; NAME=Zhang San
ID:2; NAME=TEST

如何调用带参数的存储过程

MySQL的存储过程中的参数分为三种:in/out/inout,我们可以把in看做入力参数,out看做出力参数,JDBC对这两种类型的参数设置方式不同:

1)in, JDBC使用类似于cst.set(1, 10)的方式来设置

2)out,JDBC使用类似于cst.registerOutParameter(2, Types.VARCHAR);的方式来设置

我们来看一个in参数的示例,假设我们希望返回ID为特定值的user信息,存储过程如下:

1 CREATE DEFINER=`root`@`localhost` PROCEDURE `GetUserByID`(in id int)
2 BEGIN
3 set @sqlstr=concat('select * from user where ID=', id);
4 prepare psmt from @sqlstr;
5 execute psmt;
6 END

Java的调用代码如下:

JDBC调用存储过程示例二
 1 private static void execStoredProcedureTest2(int id) throws SQLException
 2 {
 3     String dbURL = "jdbc:mysql://localhost/test";
 4     Connection con = DriverManager.getConnection(dbURL, "root", "123");
 5     CallableStatement cst = con.prepareCall("call GetUserByID(?)");
 6     cst.setInt(1, id);
 7     ResultSet rs = cst.executeQuery();
 8     while(rs.next())
 9     {
10         String name = rs.getString("NAME");
11         System.out.println("ID:" + id + "; NAME=" + name);
12     }
13     rs.close();
14     cst.close();
15     con.close();
16 }

我们执行下面的语句:

execStoredProcedureTest2(1);

结果如下:

ID:1; NAME=Zhang San

对于out类型的参数,调用方式类似,不再赘述。

获取数据库以及结果集的metadata信息

在JDBC中,我们不仅能够对数据进行操作,我们还能获取数据库以及结果集的元数据信息,例如数据库的名称、驱动信息、表信息;结果集的列信息等。

获取数据库的metadata信息

我们可以通过connection.getMetaData方法来获取数据库的元数据信息,它的类型是DatabaseMetaData。

获取数据库的元数据信息
 1 private static void test1() throws SQLException
 2 {
 3     String dbURL = "jdbc:mysql://localhost/mysql";
 4     Connection con = DriverManager.getConnection(dbURL, "root", "123");
 5     
 6     DatabaseMetaData dbmd = con.getMetaData();
 7     
 8     System.out.println("数据库:" + dbmd.getDatabaseProductName() + " " + dbmd.getDatabaseProductVersion());
 9     System.out.println("驱动程序:" + dbmd.getDriverName() + " " + dbmd.getDriverVersion());
10 
11     ResultSet rs = dbmd.getTables(null, null, null, null);
12     System.out.println(String.format("|%-26s|%-9s|%-9s|%-9s|", "表名称","表类别","表类型","表模式"));        
13     while(rs.next())
14     {
15         System.out.println(String.format("|%-25s|%-10s|%-10s|%-10s|", 
16                 rs.getString("TABLE_NAME"),rs.getString("TABLE_CAT"),
17                 rs.getString("TABLE_TYPE"), rs.getString("TABLE_SCHEM")));
18     }
19 }

这里我们使用的数据库是MySQL中自带的默认数据库:mysql,它会记录整个数据库服务器中的一些信息。上述代码执行结果如下:

数据库:MySQL 5.5.28
驱动程序:MySQL-AB JDBC Driver mysql-connector-java-5.0.4 ( $Date: 2006-10-19 17:47:48 +0200 (Thu, 19 Oct 2006) $, $Revision: 5908 $ )
|表名称                       |表类别      |表类型      |表模式      |
|columns_priv             |mysql     |TABLE     |null      |
|db                       |mysql     |TABLE     |null      |
|event                    |mysql     |TABLE     |null      |
|func                     |mysql     |TABLE     |null      |
。。。

由于mysql中表比较多,上述结果只截取了一部分。

获取结果集的元数据信息

我们可以通过使用resultset.getMetaData方法来获取结果集的元数据信息,它的类型是ResultSetMetaData。

获取结果集的元数据信息
 1 private static void test2() throws SQLException
 2 {
 3     String dbURL = "jdbc:mysql://localhost/test";
 4     Connection con = DriverManager.getConnection(dbURL, "root", "123");
 5     Statement st = con.createStatement();
 6     ResultSet rs = st.executeQuery("select ID, NAME from user");
 7     ResultSetMetaData rsmd = rs.getMetaData();
 8     for (int i = 1; i <= rsmd.getColumnCount(); i++)
 9     {
10         System.out.println("Column Name:" + rsmd.getColumnName(i) + "; Column Type:" + rsmd.getColumnTypeName(i));
11     }
12 }

它的执行结果如下:

Column Name:ID; Column Type:INTEGER UNSIGNED
Column Name:NAME; Column Type:VARCHAR

可以看到,它返回类结果集中每一列的名称和类型。

基于ResultSet的操作

当我们需要对数据库进行修改时,除了上述通过Statement完成操作外,我们也可以借助ResultSet来完成。

需要注意的是,在这种情况下,我们定义Statement时,需要添加参数。

Statement构造函数可以包含3个参数:

  • resultSetType,它的取值包括:ResultSet.TYPE_FORWARD_ONLYResultSet.TYPE_SCROLL_INSENSITIVEResultSet.TYPE_SCROLL_SENSITIVE,默认情况下,该参数的值是ResultSet.TYPE_FORWARD_ONLY
  • resultSetConcurrency,它的取值包括:ResultSet.CONCUR_READ_ONLYResultSet.CONCUR_UPDATABLE,默认情况下,该参数的值是ResultSet.CONCUR_READ_ONLY
  • resultSetHoldability,它的取值包括:ResultSet.HOLD_CURSORS_OVER_COMMITResultSet.CLOSE_CURSORS_AT_COMMIT

为了使得ResultSet能够对数据进行操作我们需要:

  • 将resultSetType设置为ResultSet.TYPE_SCROLL_SENSITIVE
  • 将resultSetConcurrency设置为ResultSet.CONCUR_UPDATABLE

在通过ResultSet对数据进行调整的过程中,下面方法可能会被调用:

  • resultset.last()
  • resultset.first()
  • resultset.moveToInsertRow()
  • resultset.absolute()
  • resultset.setxxx()
  • resultset.updateRow()
  • resultset.insertRow()

下面是一个通过ResultSet对数据进行增、删、改的示例:

通过ResultSet对数据进行增、删、改
 1 private static void getResultCount() throws SQLException
 2 {
 3     System.out.println("=====Result Count=====");
 4     String dbURL = "jdbc:mysql://localhost/test";
 5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
 6     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
 7     ResultSet rs = st.executeQuery("select * from user");
 8     rs.last();
 9     System.out.println("返回结果的条数:"+ rs.getRow());
10     rs.first();
11     
12     rs.close();
13     st.close();
14     con.close();
15 }
16 
17 private static void insertDataToResultSet() throws SQLException
18 {
19     System.out.println("=====Insert=====");
20     String dbURL = "jdbc:mysql://localhost/test";
21     Connection con = DriverManager.getConnection(dbURL, "root", "123");
22     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
23     ResultSet rs = st.executeQuery("select ID,NAME from user");
24     rs.moveToInsertRow();
25     rs.updateInt(1, 4);
26     rs.updateString(2, "Xiao Ming");
27     rs.insertRow();
28     showUser(st);
29     
30     rs.close();
31     st.close();
32     con.close();
33 }
34 
35 private static void updateDataToResultSet() throws SQLException
36 {
37     System.out.println("=====Update=====");
38     String dbURL = "jdbc:mysql://localhost/test";
39     Connection con = DriverManager.getConnection(dbURL, "root", "123");
40     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
41     ResultSet rs = st.executeQuery("select * from user");
42     rs.last();
43     int count = rs.getRow();
44     rs.first();
45     rs.absolute(count);
46     rs.updateString(2, "Xiao Qiang");
47     rs.updateRow();
48     showUser(st);
49     
50     rs.close();
51     st.close();
52     con.close();
53 }
54 
55 private static void delDataFromResultSet() throws SQLException
56 {
57     System.out.println("=====Delete=====");
58     String dbURL = "jdbc:mysql://localhost/test";
59     Connection con = DriverManager.getConnection(dbURL, "root", "123");
60     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE, ResultSet.CLOSE_CURSORS_AT_COMMIT);
61     ResultSet rs = st.executeQuery("select * from user");
62     rs.last();
63     int count = rs.getRow();
64     rs.first();
65     rs.absolute(count);
66     rs.deleteRow();
67     showUser(st);
68     
69     rs.close();
70     st.close();
71     con.close();
72 }

分别调用上述方法:

1 getResultCount();
2 insertDataToResultSet();
3 updateDataToResultSet();
4 delDataFromResultSet();

执行结果如下:

=====Result Count=====
返回结果的条数:2
=====Insert=====
ID:1; NAME=Zhang San
ID:2; NAME=TEST
ID:4; NAME=Xiao Ming
=====Update=====
ID:1; NAME=Zhang San
ID:2; NAME=TEST
ID:4; NAME=Xiao Qiang
=====Delete=====
ID:1; NAME=Zhang San
ID:2; NAME=TEST

可以看到我们对ID为4的记录进行了插入、更新和删除操作。

预处理以及批处理

预处理和批处理都是用来提升系统性能的方式,一种是利用数据库的缓存机制,一种是利用数据库一次执行多条语句的方式。

预处理

数据库服务器接收到Statement后,一般会解析Statement、分析是否有语法错误、定制最优的执行计划,这个过程可能会降低系统的性能。一般的数据库服务器都这对这种情况,设计了缓存机制,当数据库接收到指令时,如果缓存中已经存在,那么就不再解析,而是直接运行。

这里相同的指令是指sql语句完全一样,包括大小写。

JDBC使用PreparedStatement来完成预处理:

预处理示例
 1 private static void test1() throws SQLException
 2 {
 3     System.out.println("=====Insert a single record by PreparedStatement=====");
 4     String dbURL = "jdbc:mysql://localhost/test";
 5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
 6     PreparedStatement pst = con.prepareStatement("insert into user(id,name) values(?,?)");
 7     pst.setInt(1, 5);
 8     pst.setString(2, "Lei Feng");
 9     pst.executeUpdate();
10     showUser(pst);
11     pst.close();
12     con.close();
13 }

执行结果如下:

=====Insert a single record by PreparedStatement=====
ID:1; NAME=Zhang San
ID:2; NAME=TEST
ID:5; NAME=Lei Feng

批处理

批处理是利用数据库一次执行多条语句的机制来提升性能,这样可以避免多次建立连接带来的性能损失。

批处理使用Statement的addBatch来添加指令,使用executeBatch方法来一次执行多条指令:

批处理示例
 1 private static void test2() throws SQLException
 2 {
 3     System.out.println("=====Insert multiple records by Statement & Batch=====");
 4     String dbURL = "jdbc:mysql://localhost/test";
 5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
 6     Statement st = con.createStatement();
 7     st.addBatch("insert into user(id,name) values(6,'Xiao Zhang')");
 8     st.addBatch("insert into user(id,name) values(7,'Xiao Liu')");
 9     st.addBatch("insert into user(id,name) values(8,'Xiao Zhao')");
10     st.executeBatch();
11     showUser(st);
12     st.close();
13     con.close();
14 }

执行结果如下:

=====Insert multiple records by Statement & Batch=====
ID:1; NAME=Zhang San
ID:2; NAME=TEST
ID:5; NAME=Lei Feng
ID:6; NAME=Xiao Zhang
ID:7; NAME=Xiao Liu
ID:8; NAME=Xiao Zhao

预处理和批处理相结合

我们可以把预处理和批处理结合起来,利用数据库的缓存机制,一次执行多条语句:

预处理和批处理相结合的示例
 1 private static void test3() throws SQLException
 2 {
 3     System.out.println("=====Insert multiple records by PreparedStatement & Batch=====");
 4     String dbURL = "jdbc:mysql://localhost/test";
 5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
 6     PreparedStatement pst = con.prepareStatement("insert into user(id,name) values(?,?)");
 7     pst.setInt(1, 9);
 8     pst.setString(2, "Xiao Zhang");
 9     pst.addBatch();
10     pst.setInt(1, 10);
11     pst.setString(2, "Xiao Liu");
12     pst.addBatch();
13     pst.setInt(1, 11);
14     pst.setString(2, "Xiao Zhao");
15     pst.addBatch();
16     pst.executeBatch();
17     showUser(pst);
18     pst.close();
19     con.close();
20 }

执行结果如下:

=====Insert multiple records by PreparedStatement & Batch=====
ID:1; NAME=Zhang San
ID:2; NAME=TEST
ID:5; NAME=Lei Feng
ID:9; NAME=Xiao Zhang
ID:10; NAME=Xiao Liu
ID:11; NAME=Xiao Zhao

数据库事务

谈到数据库开发,事务是一个不可回避的话题,JDBC默认情况下,是每一步都自动提交的,我们可以通过设置connection.setAutoCommit(false)的方式来强制关闭自动提交,然后通过connection.commit()和connection.rollback()来实现事务提交和回滚。

简单的数据库事务

下面是一个简单的数据库事务的示例:

简单的数据库事务示例
 1 private static void transactionTest1() throws SQLException
 2 {
 3     System.out.println("=====Simple Transaction test=====");
 4     String dbURL = "jdbc:mysql://localhost/test";
 5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
 6     Statement st = con.createStatement();
 7     try
 8     {
 9         con.setAutoCommit(false);
10         st.executeUpdate("insert into user(id,name) values(12, 'Xiao Li')");
11         con.commit();
12     }
13     catch(Exception ex)
14     {
15         ex.printStackTrace();
16         con.rollback();
17     }
18     finally
19     {
20         con.setAutoCommit(true);
21         showUser(st);
22         if (st != null) st.close();
23         if (con != null) con.close();
24     }
25 }

连续执行上述方法两次,我们可以得出下面的结果:

=====Simple Transaction test=====
ID:1; NAME=Zhang San
ID:2; NAME=TEST
ID:5; NAME=Lei Feng
ID:12; NAME=Xiao Li
=====Simple Transaction test=====
ID:1; NAME=Zhang San
ID:2; NAME=TEST
ID:5; NAME=Lei Feng
ID:12; NAME=Xiao Li
com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Duplicate entry '12' for key 'PRIMARY'
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:931)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2870)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1573)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1665)
    at com.mysql.jdbc.Connection.execSQL(Connection.java:3170)
    at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1316)
    at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1235)
    at sample.jdbc.mysql.ResultSetSample.transactionTest1(ResultSetSample.java:154)
    at sample.jdbc.mysql.ResultSetSample.main(ResultSetSample.java:17)

可以看到,第一次调用时,操作成功,事务提交,向user表中插入了一条记录;第二次调用时,发生主键冲突异常,事务回滚。

带有SavePoint的事务

当我们的事务操作中包含多个处理,但我们有时希望一些操作完成后可以先提交,这样可以避免整个事务的回滚。JDBC使用SavePoint来实现这一点。

带有SavePoint的事务示例
 1 private static void transactionTest2() throws SQLException
 2 {
 3     System.out.println("=====Simple Transaction test=====");
 4     String dbURL = "jdbc:mysql://localhost/test";
 5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
 6     Statement st = con.createStatement();
 7     Savepoint svpt = null;
 8     try
 9     {
10         con.setAutoCommit(false);
11         st.executeUpdate("insert into user(id,name) values(13, 'Xiao Li')");
12         st.executeUpdate("insert into user(id,name) values(14, 'Xiao Wang')");
13         svpt = con.setSavepoint("roll back to here");
14         st.executeUpdate("insert into user(id,name) values(15, 'Xiao Zhao')");
15         st.executeUpdate("insert into user(id,name) values(13, 'Xiao Li')");
16         con.commit();
17     }
18     catch(Exception ex)
19     {
20         ex.printStackTrace();
21         con.rollback(svpt);
22     }
23     finally
24     {
25         con.setAutoCommit(true);
26         showUser(st);
27         if (st != null) st.close();
28         if (con != null) con.close();
29     }
30 }

执行结果如下:

=====Simple Transaction test=====
com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Duplicate entry '13' for key 'PRIMARY'
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:931)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2870)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1573)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1665)
    at com.mysql.jdbc.Connection.execSQL(Connection.java:3170)
    at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1316)
    at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1235)
    at sample.jdbc.mysql.ResultSetSample.transactionTest2(ResultSetSample.java:185)
    at sample.jdbc.mysql.ResultSetSample.main(ResultSetSample.java:18)
ID:1; NAME=Zhang San
ID:2; NAME=TEST
ID:5; NAME=Lei Feng
ID:13; NAME=Xiao Li
ID:14; NAME=Xiao Wang

可以看到最终事务报出了主键冲突异常,事务回滚,但是依然向数据库中插入了ID为13和14的记录。

另外,在确定SavePoint后,ID为15的记录并没有被插入,它是通过事务进行了回滚。

5
1
分享到:
评论
3 楼 lyl070218 2013-06-24  
mark
2 楼 itway 2013-06-22  
挺多的,收藏一下,慢慢看~
1 楼 leaow567 2013-06-22  
这个必须支持啊,绝对的

相关推荐

    基于Java回顾之JDBC的使用详解

    本篇文章是对Java中JDBC的使用进行了详细的分析介绍,需要的朋友参考下

    JAVA笔试面试资料JDBC HTTP、JSP、Servlet、Struts面试题汇总资料.zip

    JAVA笔试面试资料JDBC HTTP、JSP、Servlet、Struts面试题汇总资料: 2014年最新Java笔试题及答案.docx 225道Java面试题 学会了Java面试随你问.docx Ant和Maven的作用是什么?...遇到的一些Java面试题回顾.docx

    JAVA 面向对象程序设计第9章 JDBC编程.pptx

    回顾;本章内容;9.1 JDBC基础;9.1.1 JDBC简介;9.1.1 JDBC简介;9.1.2 JDBC常用API简介;9.1.3 JDBC编程步骤;9.1.3 JDBC编程步骤;9.1.3 JDBC编程步骤;9.1.3 JDBC编程步骤; 9.1.3 JDBC编程步骤 ; 9.1.3 JDBC编程步骤 ;...

    jdbcDemo.zip

    [Java基础——jdbc],学习jdbc过程中的代码Demo,方便回顾。包括jdbc的简单使用,自定义工具类,批处理,事务管理。

    《Java程序设计案例教程》教学课件09Java数据库编程.pptx

    学习目标 01 掌握在Java应用程序中用JDBC连接驱动访问MySQL数据库的方法和步骤。 02 掌握通过Java应用程序操作MySQL数据库的方法和步骤,包括查询(顺序查询、游动查询)、添加、修改和删除数据记录等基本数据操作...

    JavaSwing简易人员管理系统

    采用mvc三层架构,jdbc(简易封装jdbc实现针对指定数据库,指定实体的建表保存)连接mysql数据库,实现功能,对人员的添加,修改信息,查询,删除以及所查人员的动态显示,适合学习Java的新手回顾javase的基础知识。

    Java Web编程宝典-十年典藏版.pdf.part2(共2个)

    主要包括Java Web开发环境、JSP语法、JSP内置对象、Java Bean技术、Servlet技术、EL与JSTL标签库、数据库应用开发、初识Struts2基础、揭密Struts2高级技术、Hib锄劬e技术入门、Hibernate高级应用、Spring核心之IoC、...

    Java SE完整版精品优质课件 自学入门必看的优秀Java基础知识培训教案 第14章_java网络编程(共42页).pptx

    特别适合新入门自学的新手同学,也适合老手回顾学习,巩固知识 第1章 Java语言概述 第2章 基本语法 第3章 面向对象编程 第4章 高级类特性1 第5章 高级类特性2 第6章 异常处理 第7章 Java集合 第8章 泛型 第9章 注解...

    java 编程入门思考

    14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 ...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    1.2 准备好开始Java之旅 3 1.2.1 下载JDK 4 1.2.2 安装JDK 5 1.2.3 配置环境变量 6 1.2.4 测试环境是否安装成功 8 1.2.5 如果失败了怎么办? 9 1.3 让自己的第一个程序运行起来 10 1.3.1 编写自己的Hello ...

    Java初学者入门教学

    14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 ...

    java联想(中文)

    14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 ...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    1.2 准备好开始Java之旅 3 1.2.1 下载JDK 4 1.2.2 安装JDK 5 1.2.3 配置环境变量 6 1.2.4 测试环境是否安装成功 8 1.2.5 如果失败了怎么办? 9 1.3 让自己的第一个程序运行起来 10 1.3.1 编写自己的Hello ...

    java视频 黑马 27期

    jdbc回顾 xml和Tomcat JavaWeb的核心技术 JDBC加强及案例 JDBC案例 AJAX及JQuery的AJAX Listener与Filter 基础加强 JavaWeb综合项目实战 Linux Nginx集群搭建 oracle hibernate struts spring ssh练习 maven 商务...

    JAVA_Thinking in Java

    14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 ...

    jsp连接数据库

    jsp连接数据库数据库的连接纽带 Java Database Connectivity JDBC简介 SQL语言回顾 JDBC基础编程

    Thinking in Java 中文第四版+习题答案

    2. Java的学习 3. 目标 4. 联机文档 5. 章节 6. 练习 7. 多媒体 8. 源代码 9. 编码样式 10. Java版本 11. 课程和培训 12. 错误 13. 封面设计 14. 致谢 第1章 对象入门 1.1 抽象的进步 1.2 对象的接口 1.3 实现方案的...

    Thinking in Java简体中文(全)

    14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 ...

    JAVA_Thinking in Java(中文版 由yyc,spirit整理).chm

    14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 ...

    Think in Java(中文版)chm格式

    14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 ...

Global site tag (gtag.js) - Google Analytics