Java回顾之JDBC
添加时间:2013-5-14 点击量:
第一篇: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( into foo(ID,NAME) values(1, Zhang San));
15
16 ResultSet rs = st.executeQuery( ID,NAME 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 Test() 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( into user(ID,NAME) values(1, Zhang San));
7 st.execute( into user(ID,NAME) values(2, Li Si));
8 st.execute( into user(ID,NAME) values(3, Wang Wu));
9 System.out.println(===== test=====);
10 showUser(st);
11 st.close();
12 con.close();
13 }
14
15 private static void Test() 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( user where ID=3);
21 System.out.println(===== test=====);
22 showUser(st);
23 st.close();
24 con.close();
25 }
26
27 private static void Test() 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( user set NAME=TEST where ID=2);
33 System.out.println(===== 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( ID, NAME 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 Test();
2 Test();
3 Test();
履行成果如下:
===== test=====
ID:1; NAME=Zhang San
ID:2; NAME=Li Si
ID:3; NAME=Wang Wu
===== test=====
ID:1; NAME=Zhang San
ID:2; NAME=Li Si
===== test=====
ID:1; NAME=Zhang San
ID:2; NAME=TEST
上方代码中的showUser办把user表中的所有记录打印出来。
如何调用存储过程
存储过程是做数据库开辟时经常应用的技巧,它可以经由过程节俭编译时候的体式格式来提拔体系机能,我们这里的示例应用MySQL数据库。
如何调用不带参数的存储过程
假设我们如今有一个简单的存储过程,它只是返回user表中的所有记录,存储过程如下:
1 CREATE DEFINER=`root`@`localhost` PROCEDURE `GetUser`()
2 BEGIN
3 ID,NAME 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( user where ID=, id);
4 prepare psmt @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( ID, NAME 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_ONLY
、ResultSet.TYPE_SCROLL_INSENSITIVE
或 ResultSet.TYPE_SCROLL_SENSITIVE,默认景象下,该参数的值是ResultSet.TYPE_FORWARD_ONLY
。
resultSetConcurrency
,它的取值包含:ResultSet.CONCUR_READ_ONLY
或 ResultSet.CONCUR_UPDATABLE
,默认景象下,该参数的值是ResultSet.CONCUR_READ_ONLY
。
resultSetHoldability
,它的取值包含:ResultSet.HOLD_CURSORS_OVER_COMMIT
或 ResultSet.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.Row()
- resultset.Row()
下面是一个经由过程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( 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 DataToResultSet() 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( ID,NAME user);
24 rs.moveToInsertRow();
25 rs.Int(1, 4);
26 rs.String(2, Xiao Ming);
27 rs.Row();
28 showUser(st);
29
30 rs.close();
31 st.close();
32 con.close();
33 }
34
35 private static void DataToResultSet() 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( user);
42 rs.last();
43 int count = rs.getRow();
44 rs.first();
45 rs.absolute(count);
46 rs.String(2, Xiao Qiang);
47 rs.Row();
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( user);
62 rs.last();
63 int count = rs.getRow();
64 rs.first();
65 rs.absolute(count);
66 rs.Row();
67 showUser(st);
68
69 rs.close();
70 st.close();
71 con.close();
72 }
分别调用上述办法:
1 getResultCount();
2 DataToResultSet();
3 DataToResultSet();
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( 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( into user(id,name) values(6,Xiao Zhang));
8 st.addBatch( into user(id,name) values(7,Xiao Liu));
9 st.addBatch( 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( 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( 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( into user(id,name) values(13, Xiao Li));
12 st.executeUpdate( into user(id,name) values(14, Xiao Wang));
13 svpt = con.setSavepoint(roll back to here);
14 st.executeUpdate( into user(id,name) values(15, Xiao Zhao));
15 st.executeUpdate( 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的记录并没有入,它是经由过程事务进行了回滚。
我俩之间有着强烈的吸引力。短短几个小时后,我俩已经明白:我们的心是一个整体的两半,我俩的心灵是孪生兄妹,是知己。她让我感到更有活力,更完美,更幸福。即使她不在我身边,我依然还是感到幸福,因为她总是以这样或者那样的方式出现在我心头。——恩里克·巴里奥斯《爱的文明》
第一篇: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数据库,创建一张表,插入一些记录,然后将记录返回:
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( into foo(ID,NAME) values(1, Zhang San));
15
16 ResultSet rs = st.executeQuery( ID,NAME 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的示例:
1 private static void Test() 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( into user(ID,NAME) values(1, Zhang San));
7 st.execute( into user(ID,NAME) values(2, Li Si));
8 st.execute( into user(ID,NAME) values(3, Wang Wu));
9 System.out.println(===== test=====);
10 showUser(st);
11 st.close();
12 con.close();
13 }
14
15 private static void Test() 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( user where ID=3);
21 System.out.println(===== test=====);
22 showUser(st);
23 st.close();
24 con.close();
25 }
26
27 private static void Test() 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( user set NAME=TEST where ID=2);
33 System.out.println(===== 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( ID, NAME 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 Test();
2 Test();
3 Test();
履行成果如下:
===== test=====
ID:1; NAME=Zhang San
ID:2; NAME=Li Si
ID:3; NAME=Wang Wu
===== test=====
ID:1; NAME=Zhang San
ID:2; NAME=Li Si
===== test=====
ID:1; NAME=Zhang San
ID:2; NAME=TEST
上方代码中的showUser办把user表中的所有记录打印出来。
如何调用存储过程
存储过程是做数据库开辟时经常应用的技巧,它可以经由过程节俭编译时候的体式格式来提拔体系机能,我们这里的示例应用MySQL数据库。
如何调用不带参数的存储过程
假设我们如今有一个简单的存储过程,它只是返回user表中的所有记录,存储过程如下:
1 CREATE DEFINER=`root`@`localhost` PROCEDURE `GetUser`()
2 BEGIN
3 ID,NAME 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( user where ID=, id);
4 prepare psmt @sqlstr;
5 execute psmt;
6 END
Java的调用代码如下:
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( ID, NAME 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_ONLY
、ResultSet.TYPE_SCROLL_INSENSITIVE
或ResultSet.TYPE_SCROLL_SENSITIVE,默认景象下,该参数的值是
ResultSet.TYPE_FORWARD_ONLY
。resultSetConcurrency
,它的取值包含:ResultSet.CONCUR_READ_ONLY
或ResultSet.CONCUR_UPDATABLE
,默认景象下,该参数的值是ResultSet.CONCUR_READ_ONLY
。resultSetHoldability
,它的取值包含:ResultSet.HOLD_CURSORS_OVER_COMMIT
或ResultSet.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.Row()
- resultset.Row()
下面是一个经由过程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( 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 DataToResultSet() 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( ID,NAME user);
24 rs.moveToInsertRow();
25 rs.Int(1, 4);
26 rs.String(2, Xiao Ming);
27 rs.Row();
28 showUser(st);
29
30 rs.close();
31 st.close();
32 con.close();
33 }
34
35 private static void DataToResultSet() 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( user);
42 rs.last();
43 int count = rs.getRow();
44 rs.first();
45 rs.absolute(count);
46 rs.String(2, Xiao Qiang);
47 rs.Row();
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( user);
62 rs.last();
63 int count = rs.getRow();
64 rs.first();
65 rs.absolute(count);
66 rs.Row();
67 showUser(st);
68
69 rs.close();
70 st.close();
71 con.close();
72 }
分别调用上述办法:
1 getResultCount();
2 DataToResultSet();
3 DataToResultSet();
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( 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( into user(id,name) values(6,Xiao Zhang));
8 st.addBatch( into user(id,name) values(7,Xiao Liu));
9 st.addBatch( 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( 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( 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来实现这一点。
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( into user(id,name) values(13, Xiao Li));
12 st.executeUpdate( into user(id,name) values(14, Xiao Wang));
13 svpt = con.setSavepoint(roll back to here);
14 st.executeUpdate( into user(id,name) values(15, Xiao Zhao));
15 st.executeUpdate( 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的记录并没有入,它是经由过程事务进行了回滚。
我俩之间有着强烈的吸引力。短短几个小时后,我俩已经明白:我们的心是一个整体的两半,我俩的心灵是孪生兄妹,是知己。她让我感到更有活力,更完美,更幸福。即使她不在我身边,我依然还是感到幸福,因为她总是以这样或者那样的方式出现在我心头。——恩里克·巴里奥斯《爱的文明》