MySQL数据库学习笔记(十)—-JDBC事务处理、封装JDBC工具类
一、JDBC事务处理:
我们已经知道,事务的概念即:所有的操作要么同时成功,要么同时失败。在MySQL中提供了Commit、Rollback命令进行事务的提交与回滚。实际上在JDBC中也存在事务处理,如果要想进行事务处理的话,则必须按照以下的步骤完成。
JDBC中事务处理的步骤:
1、要取消掉JDBC的自动提交:void setAutoCommit(boolean autoCommit)
2、执行各个SQL语句,加入到批处理之中
3、如果所有语句执行成功,则提交事务 commit();如果出现了错误,则回滚:rollback()
核心代码:
conn.setAutoCommit(false); // 取消自动提交把SQL语句加入批处理stmt.addBatch(…) ()stmt.addBatch(…)//执行批处理操作stmt.executeBatch();conn.commit(); // 提交事务//如果发生错误conn.rollback();
代码举例:
首先在sql中创建一个空的数据库,现在在java中,使用PreparedStatement插入数据并修改数据。正常情况下,代码应该这样写:
1 package com.vae.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.PreparedStatement; 6 import java.sql.ResultSet; 7 import java.sql.SQLException; 8 9 public class JDBCtest {101112 //数据库连接地址13 public final static String URL = "jdbc:mysql://localhost:3306/JDBCdb";14 //用户名15 public final static String USERNAME = "root";16 //密码17 public final static String PASSWORD = "smyh";18 //驱动类19 public final static String DRIVER = "com.mysql.jdbc.Driver";202122 public static void main(String[] args) {23 // TODO Auto-generated method stub24 //insert(p);25 //update(p);26 //delete(3);27 insertAndQuery();28 }293031 //方法:使用PreparedStatement插入数据、更新数据32 public static void insertAndQuery(){33 Connection conn = null;34 try {35 Class.forName(DRIVER);36 conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);37 String sql1 = "insert into user(name,pwd)values(?,?)";38 String sql2 = "update user set pwd=? where name=?";39 PreparedStatement ps = conn.prepareStatement(sql1);40 ps.setString(1, "smyhvae");41 ps.setString(2, "007");42 ps.executeUpdate();4344 ps = conn.prepareStatement(sql2);45 ps.setString(1, "008");46 ps.setString(2, "smyh");47 ps.executeUpdate();4849 ps.close();50 conn.close();5152 } catch (ClassNotFoundException e) {53 e.printStackTrace();54 } catch (SQLException e) {55 e.printStackTrace();56 }57 }5859 }
事务处理:
现在我们把上面的插入操作和修改操作变成一个事务,就要增加一部分代码了。修改上方的insertAndQuery()方法里面的代码:
1 //方法:使用PreparedStatement插入数据、更新数据 2 public static void insertAndQuery(){ 3 Connection conn = null; 4 try { 5 Class.forName(DRIVER); 6 conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); 7 conn.setAutoCommit(false);//设置为手动提交事务 8 String sql1 = "insert into user(name,pwd)values(?,?)"; 9 String sql2 = "update user set pwd=? where name=?";10 PreparedStatement ps = conn.prepareStatement(sql1);11 ps.setString(1, "smyhvae");12 ps.setString(2, "007");13 ps.executeUpdate();1415 ps = conn.prepareStatement(sql2);16 ps.setString(1, "008");17 ps.setString(2, "smyh");18 ps.executeUpdate();19 conn.commit(); //如果所有sql语句成功,则提交事务20 ps.close();21 conn.close();2223 } catch (ClassNotFoundException e) {24 e.printStackTrace();25 } catch (SQLException e) {26 e.printStackTrace();27 try {28 conn.rollback();//只要有一个sql语句出现错误,则将事务回滚29 } catch (SQLException e1) {30 e1.printStackTrace();31 }32 }3334 }
核心代码是第07行、19行、28行。这三行代码就完成了事务处理的操作。两个sql语句中,只要有一个语句出现错误,程序将无法运行,说明事务提交失败,且报错如下:
二、封装JDBC工具类
之前的JDBC代码分析:
我们可以先回顾一下上一篇博文中的第五段:使用PreparedStatement重构增删改查。
通过分析可以发现有以下不足:有许多重复的代码、每次都要加载驱动、获取连接等。增删改查无非只是slq语句不一样而已。
封装工具类就是一个抽象的过程,我们可以把现在代码中非常公用的代码抽取出来,形成一个工具类。
- 第一步:抽象公共的代码到工具类。
- 第二步:为提高可以连接不同数据库的能力,将连接数据库的URL、用户名,密码等信息编写在一个属性文件(jdbc.properties)中,方便以后进行修改。
我们先把之前的文章中,使用PreparedStatement查询数据库的代码贴出来,方便和后面的内容进行对比,省的翻来翻去麻烦。
使用PreparedStatement查询数据库:(重构前)
在这之前,请建好一个Person类,参考上一篇博文就行了。然后,JDBCtest.java的代码如下:
1 package com.vae.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.PreparedStatement; 6 import java.sql.ResultSet; 7 import java.sql.SQLException; 8 9 public class JDBCtest {101112 //数据库连接地址13 public final static String URL = "jdbc:mysql://localhost:3306/JDBCdb";14 //用户名15 public final static String USERNAME = "root";16 //密码17 public final static String PASSWORD = "smyh";18 //驱动类19 public final static String DRIVER = "com.mysql.jdbc.Driver";202122 public static void main(String[] args) {23 // TODO Auto-generated method stub24 Person p = new Person();2526 p = findById(2);27 System.out.println(p);28 }29303132 // 使用PreparedStatement查询数据33 public static Person findById(int id){34 Person p = null;35 try {36 Class.forName(DRIVER);37 Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);38 String sql = "select name,age,description from person where id=?";39 PreparedStatement ps = conn.prepareStatement(sql);40 //设置占位符对应的值41 ps.setInt(1, id);4243 ResultSet rs = ps.executeQuery();44 if(rs.next()){45 p = new Person();46 p.setId(id);47 p.setName(rs.getString(1));48 p.setAge(rs.getInt(2));49 p.setDescription(rs.getString(3));5051 }52 rs.close();53 ps.close();54 conn.close();555657 } catch (ClassNotFoundException e) {58 e.printStackTrace();59 } catch (SQLException e) {60 e.printStackTrace();61 }62 return p;63 }646566 }
接下来开始真正的工作了,从零开始。
封装工具类:
新建工程文件,结构如下:
(1)先新建一个DBUtils工具类:
1 package com.vae.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 import java.util.ResourceBundle; 910 /**11 * 数据库操作工具类12 * @author lamp13 *14 */15 public class DBUtils {1617 //数据库连接地址18 public static String URL;19 //用户名20 public static String USERNAME;21 //密码22 public static String PASSWORD;23 //mysql的驱动类24 public static String DRIVER;2526 private static ResourceBundle rb = ResourceBundle.getBundle("com.vae.jdbc.db-config");2728 private DBUtils(){}2930 //使用静态块加载驱动程序31 static{32 URL = rb.getString("jdbc.url");33 USERNAME = rb.getString("jdbc.username");34 PASSWORD = rb.getString("jdbc.password");35 DRIVER = rb.getString("jdbc.driver");36 try {37 Class.forName(DRIVER);38 } catch (ClassNotFoundException e) {39 e.printStackTrace();40 }41 }42 //定义一个获取数据库连接的方法43 public static Connection getConnection(){44 Connection conn = null;45 try {46 conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);47 } catch (SQLException e) {48 e.printStackTrace();49 System.out.println("获取连接失败");50 }51 return conn;52 }5354 /**55 * 关闭数据库连接56 * @param rs57 * @param stat58 * @param conn59 */60 public static void close(ResultSet rs,Statement stat,Connection conn){61 try {62 if(rs!=null)rs.close();63 if(stat!=null)stat.close();64 if(conn!=null)conn.close();65 } catch (SQLException e) {66 e.printStackTrace();67 }68 }6970 }
28行:既然是工具类,一般不要实例化,此时可以采用单例设计模式,或者将构造方法私有化。
26行:很明显可以看到,我们是将连接数据库的URL、用户名,密码等信息编写在一个属性文件(jdbc.properties)中,稍后再来定义这个属性文件。
31行:为避免重复代码,使用静态代码块:只会在类加载的时候执行一次。
42行:定义一个获取数据库连接的方法
60行:关闭数据库连接
(2)接下来新建一个属性文件,new–>file,命名为:db-config.properties,代码如下:
jdbc.url=jdbc:mysql://localhost:3306/jdbcdbjdbc.username=rootjdbc.password=smyhjdbc.driver=com.mysql.jdbc.Driver
以后如果需要修改配置信息,只需要在这里改就行了。注意在上面的DBUtils类中是怎么来调用这个配置信息的。
(3)紧接着新建文件,定义好Person类:
Person.java
(4)然后开始编写主程序来测试一下:
1 package com.vae.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 8 9 public class Test {1011 public static void main(String[] args) {12 Person p = new Person();13 p = findById(1);14 System.out.println(p);15 }1617181920 /**21 * 查询的方法22 */23 public static Person findById(int id){24 Person p =null;25 //通过工具类获取数据库连接26 Connection conn = DBUtils.getConnection();27 PreparedStatement ps = null;28 ResultSet rs = null;29 String sql = "select name,age,description from person where id=?";30 try {31 ps = conn.prepareStatement(sql);32 //设置占位符对应的值33 ps.setInt(1, id);34 rs = ps.executeQuery();35 if(rs.next()){36 p = new Person();37 p.setName(rs.getString(1));38 p.setAge(rs.getInt(2));39 p.setDescription(rs.getString(3));40 }41 } catch (SQLException e) {42 e.printStackTrace();43 }finally{44 //通过工具类关闭数据库连接45 DBUtils.close(rs, ps, conn);46 }47 return p;4849 }5051 }