JDBC
1.jdbc概述
问题:实际开发中,不可能用工具或者命令行操作数据库,数据库表中的数据最终要使用Java程序来操作,那么Java中如何操作数据库中的数据呢?
答 : 在Java语言中,有一个专门连接数据库的规范(JDBC),专门负责连接数据库进行数据操作的规范
JDBC只是SUN编写的一堆接口(规范的体现),SUN公司自己并没有实现
问题 : 为什么SUN只定义一个JDBC规范,而不实现呢?
答 : 因为市面上的数据库很多,每个数据库内部接口不会向外暴露,而且即便是暴露让SUN去实现,市面上很多数据库全部要SUN来实现不现实
实际中哪个数据库需要支持JAVA语言,就需要自己实现Java的JDBC规范,因为实现了JDBC很多接口,那么就会有很多实现类,而很多实现类在java中会使用一个专门的包封装起来,叫做jar包(在JDBC中叫做驱动包),各大数据库产商实现JDBC规范以后都会把他们jar包放在官网上以供开发者下载使用
DBC(Java DataBase Connectivity):是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基
JDBC规范对应的api包
2. 入门案例
1)连接数据库
案例使用JDBC操作MySQL数据库
2)创建普通java项目
3)在项目下面新建一个lib目录
4) 将MySQL驱动包拷贝到项目中并添加依赖
5) 获取数据库连接对象
准备:
1.拷贝MySQL的驱动包到项目中去:mysql-connector-java-5.1.x-bin.jar
2.build path,告速项目去哪里去找字节码文件.
--------------------------------------------------------------------------------
操作JDBC的第一步,获取JDBC的连接对象.:Connection.
步骤:
1.加载注册驱动.
就是把驱动中的Driver字节码加载到JVM中.
Class.forName("com.mysql.jdbc.Driver");
为什么这句话就可以加载注册驱动?
第一步:把com.mysql.jdbc.Driver.class这份字节码加载到JVM中.
第二步:当一份字节码被加载进JVM,马上就会执行该字节码中的静态代码块.
第三步:该静态代码中,就在完成,先创建驱动对象,再注册.
2.通过DriverManager获取连接对象.
public static Connection getConnection(String url,String user,String password)
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbName","root","admin");
jdbc:mysql://localhost:3306/dbName
jdbc:mysql:// :连接MySQL数据库的协议,不同数据库协议不一样
localhost:3306 :数据库软件的主机和端口
dbName : 具体要连接数据库
若数据库安装在本机,并且端口是默认的3306,则可以简写:
Connection conn = DriverManager.getConnection("jdbc:mysql:///dbName","root","admin");
验证已经获取连接:可以在MySQL控制台,使用命令:show processlist; 查看MySQL运行进程.
1 public class GetConnectionDemo { 2 3 public static void main(String[] args) throws Exception { 4 5 6 7 //1.加载注册驱动 : 把当前类对应的字节码加载到JVM中 8 9 Class.forName("com.mysql.jdbc.Driver"); 10 11 //2.获取数据库连接 12 13 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 14 15 System.out.println(conn); 16 17 18 19 } 20 21 }
3.创建表-DDL操作
在其他操作之间先要把数据库表要创建出来
创建一张t_student表: id: name: age: |
1 /* 2 3 * 4 5 * 创建表操作 6 7 * SQL : create table t_student (id int primary key auto_increment,name varchar(50),age int) 8 9 */ 10 11 12 13 public static void main(String[] args) throws Exception { 14 15 String sql = "create table t_student (id int primary key auto_increment,name varchar(50),age int)"; 16 17 //贾琏欲执事 18 19 //1,加载注册驱动 20 21 Class.forName("com.mysql.jdbc.Driver"); 22 23 //2,获取数据库连接 24 25 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 26 27 //3,创建语句对象(用于执行SQL语句的对象) 28 29 Statement st = conn.createStatement(); 30 31 //4, 执行SQL语句 32 33 //int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数 34 35 //ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象 36 37 st.executeUpdate(sql); 38 39 //5,释放资源(先开后关) 40 41 st.close(); 42 43 conn.close(); 44 45 }
4. DML操作-表数据的增删改
1 //DML : 对表数据的增删改操作 2 3 public class DMLDemo { 4 5 6 7 /* 8 9 * 向 t_student表中插入一条数据 10 11 * sql : insert into t_student(name,age) values ('乔峰',30) 12 13 */ 14 15 @Test 16 17 public void testInsert() throws Exception { 18 19 String sql = "insert into t_student(name,age) values ('乔峰',30)"; 20 21 22 23 // 1.加载注册驱动 24 25 Class.forName("com.mysql.jdbc.Driver"); 26 27 28 29 // 2.获取数据库连接对象 30 31 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 32 33 // 3.创建语句对象 34 35 Statement st = conn.createStatement(); 36 37 38 39 // 4.执行SQL语句 40 41 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数 42 43 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象 44 45 int rows = st.executeUpdate(sql); 46 47 System.out.println(rows); 48 49 //5.释放资源(先开后关) 50 51 st.close(); 52 53 conn.close(); 54 55 56 57 } 58 59 /* 60 61 * 删除操作: 删除t_student表中的某一条数据 62 63 * SQL :delete from t_student where id = 2 64 65 */ 66 67 @Test 68 69 public void testDelete() throws Exception { 70 71 String sql = "delete from t_student where id = 2"; 72 73 74 75 // 1.加载注册驱动 76 77 Class.forName("com.mysql.jdbc.Driver"); 78 79 80 81 // 2.获取数据库连接对象 82 83 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 84 85 // 3.创建语句对象 86 87 Statement st = conn.createStatement(); 88 89 90 91 // 4.执行SQL语句 92 93 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数 94 95 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象 96 97 int rows = st.executeUpdate(sql); 98 99 System.out.println(rows); 100 101 //5.释放资源(先开后关) 102 103 st.close(); 104 105 conn.close(); 106 107 } 108 109 /* 110 111 * 修改操作 : 修改t_student表中的某一条数据 112 113 * SQL : update t_student set name = '虚竹',age = 50 where id = 3 114 115 */ 116 117 @Test 118 119 public void testUpdate() throws Exception { 120 121 String sql = "update t_student set name = '虚竹',age = 50 where id = 3"; 122 123 124 125 // 1.加载注册驱动 126 127 Class.forName("com.mysql.jdbc.Driver"); 128 129 130 131 // 2.获取数据库连接对象 132 133 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 134 135 // 3.创建语句对象 136 137 Statement st = conn.createStatement(); 138 139 140 141 // 4.执行SQL语句 142 143 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数 144 145 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象 146 147 int rows = st.executeUpdate(sql); 148 149 System.out.println(rows); 150 151 //5.释放资源(先开后关) 152 153 st.close(); 154 155 conn.close(); 156 157 } 158 159 }
5.DQL操作-查询操作
1) 查询操作的分析
2)查询具体操作
结果集的列的位置
使用 rs.next() 偏移光标,循环指定具体的某一行
获取数据的具体方法
getObject(int columnIndex) |
|
getObject(String columnLabel) |
1 package cn.sxt.jdbc._01connection; 2 3 4 5 6 7 import java.sql.Connection; 8 9 import java.sql.DriverManager; 10 11 import java.sql.ResultSet; 12 13 import java.sql.Statement; 14 15 import java.util.ArrayList; 16 17 import java.util.List; 18 19 20 21 import org.junit.Test; 22 23 24 25 //DQL :查询操作 26 27 public class D_DQLDemo { 28 29 30 31 /* 32 33 * 多行查询 :查询t_student表中的所有数据 34 35 * SQL : select * from t_student 36 37 */ 38 39 @Test 40 41 public void testList() throws Exception { 42 43 String sql = "select * from t_student"; 44 45 46 47 // 1.加载注册驱动 48 49 Class.forName("com.mysql.jdbc.Driver"); 50 51 52 53 // 2.获取数据库连接对象 54 55 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 56 57 // 3.创建语句对象 58 59 Statement st = conn.createStatement(); 60 61 62 63 // 4.执行SQL语句 64 65 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数 66 67 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象 68 69 ResultSet rs = st.executeQuery(sql); 70 71 72 73 74 75 //创建list集合用于封装Student对象 76 77 List<Student> stus = new ArrayList<>(); 78 79 80 81 while(rs.next()) { 82 83 //1.通过结果集的位置获取对应的数 84 85 /*Object id = rs.getObject(1); 86 87 Object name = rs.getObject(2); 88 89 Object age = rs.getObject(3);*/ 90 91 92 93 //2.通过结果集的 列名获取对应的数据 94 95 /*Object id = rs.getObject("id"); 96 97 Object name = rs.getObject("name"); 98 99 Object age = rs.getObject("age");*/ 100 101 //3.通过数据库数据和Java对应的数据类型获取对应的只 102 103 int id = rs.getInt("id"); 104 105 String name = rs.getString("name"); 106 107 int age = rs.getInt("age"); 108 109 //System.out.println(id+","+name+","+age); 110 111 112 113 //将获取的数据封装成对应的Student对象 114 115 Student stu = new Student(id, name, age); 116 117 118 119 //将一个个Student对象添加到list集合中 120 121 stus.add(stu); 122 123 } 124 125 126 127 for (Student student : stus) { 128 129 System.out.println(student); 130 131 } 132 133 //5.释放资源(先开后关) 134 135 rs.close(); 136 137 st.close(); 138 139 conn.close(); 140 141 } 142 143 144 145 146 147 /* 148 149 * 单行查询: 查询出t_student 指定id的信息 150 151 * SQL : select * from t_student where id = 1; 152 153 */ 154 155 @Test 156 157 public void testGetOne() throws Exception { 158 159 String sql = "select * from t_student where id = 2"; 160 161 162 163 // 1.加载注册驱动 164 165 Class.forName("com.mysql.jdbc.Driver"); 166 167 168 169 // 2.获取数据库连接对象 170 171 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 172 173 // 3.创建语句对象 174 175 Statement st = conn.createStatement(); 176 177 178 179 // 4.执行SQL语句 180 181 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数 182 183 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象 184 185 ResultSet rs = st.executeQuery(sql); 186 187 188 189 if(rs.next()) { 190 191 //1.通过结果集的位置获取对应的数 192 193 /*Object id = rs.getObject(1); 194 195 Object name = rs.getObject(2); 196 197 Object age = rs.getObject(3);*/ 198 199 200 201 //2.通过结果集的 列名获取对应的数据 202 203 /*Object id = rs.getObject("id"); 204 205 Object name = rs.getObject("name"); 206 207 Object age = rs.getObject("age");*/ 208 209 //3.通过数据库数据和Java对应的数据类型获取对应的只 210 211 int id = rs.getInt("id"); 212 213 String name = rs.getString("name"); 214 215 int age = rs.getInt("age"); 216 217 //System.out.println(id+","+name+","+age); 218 219 220 221 //将获取的数据封装成对应的Student对象 222 223 Student stu = new Student(id, name, age); 224 225 System.out.println(stu); 226 227 } 228 229 //5.释放资源(先开后关) 230 231 rs.close(); 232 233 st.close(); 234 235 conn.close(); 236 237 } 238 239 }
6.预编译语句对象PreparedStatment
问题 : 我们有了Statment对象可以执行SQL,为什么还要使用PreparedStatment?
优势
1. SQL语句结构清晰,参数的设置和SQL语句分离
2. 性能更高
3. 防止SQL注入
Statement: 表示静态SQL语句对象. PreparedStatement:Statement的子接口,表示预编译SQL语句对象. 通过占位符(?)来拼SQL. |
1) 创建PreparedStatement
创建语句对象 Statment
createStatement() |
创建预编译语句对象PreparedStatement
prepareStatement(String sql) |
2)执行SQL语句的方法
[1] Statment
在执行SQL语句的时候回带上SQL语句
executeQuery(String sql) |
|
int |
executeUpdate(String sql) |
[2]PreparedStatement
在执行SQL语句的方法中不需要设置SQL语句
executeQuery() |
|
int |
executeUpdate() |
3) 设置站位参数的值
void setXxx(int parameterIndex,Xxx value):用于设置占位符参数, parameterIndex:第几个问号. 注意:从1开始. value:设置的真实值. Xxx:表示数据类型.String/int/long/Double |
4)代码
1 package cn.sxt.jdbc._01connection; 2 3 4 5 import static org.junit.Assert.*; 6 7 8 9 import java.sql.Connection; 10 11 import java.sql.DriverManager; 12 13 import java.sql.PreparedStatement; 14 15 16 17 import org.junit.Test; 18 19 20 21 //DML : 对表数据的增删改操作,使用预编译语句对象 22 23 public class E_DMLByPreparedStatmentDemo { 24 25 26 27 /* 28 29 * 向 t_student表中插入一条数据 30 31 * sql : insert into t_student(name,age) values ('乔峰',30) 32 33 */ 34 35 @Test 36 37 public void testInsert() throws Exception { 38 39 String sql = "insert into t_student(name,age) values (?,?)"; 40 41 42 43 // 1.加载注册驱动 44 45 Class.forName("com.mysql.jdbc.Driver"); 46 47 48 49 // 2.获取数据库连接对象 50 51 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 52 53 // 3.创建预编译语句对象 54 55 PreparedStatement ps = conn.prepareStatement(sql); 56 57 //3.1设置占位符参数 58 59 ps.setString(1, "东方姑娘"); 60 61 ps.setInt(2, 18); 62 63 64 65 // 4.执行SQL语句:注意不要带SQL参数 66 67 ps.executeUpdate(); 68 69 //5.释放资源(先开后关) 70 71 ps.close(); 72 73 conn.close(); 74 75 76 77 } 78 79 /* 80 81 * 删除操作: 删除t_student表中的某一条数据 82 83 * SQL :delete from t_student where id = 2 84 85 */ 86 87 @Test 88 89 public void testDelete() throws Exception { 90 91 String sql = "delete from t_student where id = ?"; 92 93 94 95 // 1.加载注册驱动 96 97 Class.forName("com.mysql.jdbc.Driver"); 98 99 100 101 // 2.获取数据库连接对象 102 103 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 104 105 // 3.创建预编译语句对象 106 107 PreparedStatement ps = conn.prepareStatement(sql); 108 109 //3.1设置占位符对应的参数值 110 111 ps.setInt(1, 1); 112 113 114 115 // 4.执行SQL语句 116 117 int rows = ps.executeUpdate(); 118 119 System.out.println(rows); 120 121 //5.释放资源(先开后关) 122 123 ps.close(); 124 125 conn.close(); 126 127 } 128 129 /* 130 131 * 修改操作 : 修改t_student表中的某一条数据 132 133 * SQL : update t_student set name = '虚竹',age = 50 where id = 3 134 135 */ 136 137 @Test 138 139 public void testUpdate() throws Exception { 140 141 String sql = "update t_student set name = ?,age = ? where id = ?"; 142 143 144 145 // 1.加载注册驱动 146 147 Class.forName("com.mysql.jdbc.Driver"); 148 149 150 151 // 2.获取数据库连接对象 152 153 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 154 155 // 3.创建预编译语句对象 156 157 PreparedStatement ps = conn.prepareStatement(sql); 158 159 //3.1设置占位符参数对应的值 160 161 ps.setString(1, "西方失败"); 162 163 ps.setInt(2, 40); 164 165 ps.setInt(3, 4); 166 167 // 4.执行SQL语句 168 169 int rows = ps.executeUpdate(); 170 171 System.out.println(rows); 172 173 //5.释放资源(先开后关) 174 175 ps.close(); 176 177 conn.close(); 178 179 } 180 181 } 182 183
7. JavaWeb开发的分层设计-三层架构
1) DAO层设计
实际开发中,JavaWeb开发代码一般分为三层,分层结构是JavaWeb开发中的一种设计思想,这样会让我们开发层次分明,每一层只要完成对应的功能即可,使得项目便于开发和维护
【1】Web层/表现层 : 主要接受前台浏览器用户的参数,给浏览器响应数据等等
【2】Service层/业务成/服务层:主要处理业务功能,日志,权限,事物,等等
【3】DAO层/持久层 :专门负责和数据库交互,数据处理相关代码
DAO : Data Access Object 数据访问对象
实际开发中 : 用户请求到-Web层--->Service层-->DAO层
2) DAO思想
3) 使用DAO以后代码的以及包的设计结构
开发中如果使用的分层,编写的包和类名接口名等等都是有固定规则,不能随便瞎写
[1]. DAO层接口包命名
公司域名倒写+项目名称/模块名称+dao 如 : cn.sxt.crm.dao |
[2]DAO层实现类包命名
公司域名倒写+项目名称/模块名称+dao+impl 如 : cn.sxt.crm.dao.impl |
[3] DAO层操作对应表的接口命名
对应表的名称 + Dao/DAO
如 : StudentDao/DAO , TeacherDao/DAO |
[4] DAO层操作对应表的实现类命名
对应表的名称 + Dao/DAOImpl
如 : StudentDaoImpl/DAOImpl , TeacherDaoImpl/DAOImpl |
[5]数据表对应的Java类domain/pojo包命名
POJO(Plain Ordinary Java Object)简单的Java对象
domian : 域对象
公司域名倒写+项目名称/模块名称+domain/pojo 如 : cn.sxt.crm.domain |
[6] 对应的测试包命名
公司域名倒写+项目名称/模块名称+test 如 : cn.sxt.crm.test |
[7]项目的工具类包命名
公司域名倒写+项目名称/模块名称+util/utils 如 : cn.sxt.crm.util/utils |
[8]DAO代码设计结构
[9] Dao的实现类代码
1 package cn.sxt.jdbc.dao.impl; 2 3 4 5 import java.sql.Connection; 6 7 import java.sql.DriverManager; 8 9 import java.sql.PreparedStatement; 10 11 import java.sql.ResultSet; 12 13 import java.sql.SQLException; 14 15 import java.util.ArrayList; 16 17 import java.util.List; 18 19 20 21 import cn.sxt.jdbc.dao.StudentDao; 22 23 import cn.sxt.jdbc.domain.Student; 24 25 26 27 public class StudentDaoImpl implements StudentDao { 28 29 30 31 @Override 32 33 public int saveStudent(Student stu) { 34 35 String sql = "insert into t_student(name,age) values (?,?)"; 36 37 38 39 Connection conn = null; 40 41 PreparedStatement ps = null; 42 43 try { 44 45 46 47 // 1.加载注册驱动 48 49 Class.forName("com.mysql.jdbc.Driver"); 50 51 52 53 // 2.获取数据库连接对象 54 55 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 56 57 // 3.创建预编译语句对象 58 59 ps = conn.prepareStatement(sql); 60 61 //3.1设置占位符参数 62 63 ps.setString(1, stu.getName()); 64 65 ps.setInt(2, stu.getAge()); 66 67 68 69 // 4.执行SQL语句:注意不要带SQL参数 70 71 return ps.executeUpdate(); 72 73 74 75 76 77 } catch (Exception e) { 78 79 e.printStackTrace(); 80 81 }finally { 82 83 //5.释放资源(先开后关) 84 85 try { 86 87 if(ps !=null) { 88 89 ps.close(); 90 91 } 92 93 } catch (SQLException e) { 94 95 e.printStackTrace(); 96 97 }finally { 98 99 try { 100 101 if(conn !=null) { 102 103 conn.close(); 104 105 } 106 107 } catch (SQLException e) { 108 109 // TODO Auto-generated catch block 110 111 e.printStackTrace(); 112 113 } 114 115 } 116 117 } 118 119 return 0; 120 121 } 122 123 124 125 @Override 126 127 public int deleteById(int id) { 128 129 130 131 String sql = "delete from t_student where id = ?"; 132 133 134 135 Connection conn = null; 136 137 PreparedStatement ps = null; 138 139 try { 140 141 142 143 // 1.加载注册驱动 144 145 Class.forName("com.mysql.jdbc.Driver"); 146 147 148 149 // 2.获取数据库连接对象 150 151 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 152 153 // 3.创建预编译语句对象 154 155 ps = conn.prepareStatement(sql); 156 157 //3.1设置占位符参数 158 159 ps.setInt(1, id); 160 161 162 163 // 4.执行SQL语句:注意不要带SQL参数 164 165 return ps.executeUpdate(); 166 167 168 169 170 171 } catch (Exception e) { 172 173 e.printStackTrace(); 174 175 }finally { 176 177 //5.释放资源(先开后关) 178 179 try { 180 181 if(ps !=null) { 182 183 ps.close(); 184 185 } 186 187 } catch (SQLException e) { 188 189 e.printStackTrace(); 190 191 }finally { 192 193 try { 194 195 if(conn !=null) { 196 197 conn.close(); 198 199 } 200 201 } catch (SQLException e) { 202 203 // TODO Auto-generated catch block 204 205 e.printStackTrace(); 206 207 } 208 209 } 210 211 } 212 213 return 0; 214 215 } 216 217 218 219 @Override 220 221 public int updateStudentById(Student stu) { 222 223 224 225 String sql = "update t_student set name = ?,age = ? where id = ?"; 226 227 228 229 Connection conn = null; 230 231 PreparedStatement ps = null; 232 233 try { 234 235 236 237 // 1.加载注册驱动 238 239 Class.forName("com.mysql.jdbc.Driver"); 240 241 242 243 // 2.获取数据库连接对象 244 245 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 246 247 // 3.创建预编译语句对象 248 249 ps = conn.prepareStatement(sql); 250 251 //3.1设置占位符参数 252 253 ps.setString(1, stu.getName()); 254 255 ps.setInt(2, stu.getAge()); 256 257 ps.setInt(3, stu.getId()); 258 259 // 4.执行SQL语句:注意不要带SQL参数 260 261 return ps.executeUpdate(); 262 263 264 265 266 267 } catch (Exception e) { 268 269 e.printStackTrace(); 270 271 }finally { 272 273 //5.释放资源(先开后关) 274 275 try { 276 277 if(ps !=null) { 278 279 ps.close(); 280 281 } 282 283 } catch (SQLException e) { 284 285 e.printStackTrace(); 286 287 }finally { 288 289 try { 290 291 if(conn !=null) { 292 293 conn.close(); 294 295 } 296 297 } catch (SQLException e) { 298 299 // TODO Auto-generated catch block 300 301 e.printStackTrace(); 302 303 } 304 305 } 306 307 } 308 309 return 0; 310 311 } 312 313 314 315 @Override 316 317 public Student selectById(int id) { 318 319 String sql = "select * from t_student where id = ?"; 320 321 322 323 Connection conn = null; 324 325 PreparedStatement ps = null; 326 327 ResultSet rs = null; 328 329 330 331 try { 332 333 // 1.加载注册驱动 334 335 Class.forName("com.mysql.jdbc.Driver"); 336 337 338 339 // 2.获取数据库连接对象 340 341 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 342 343 // 3.创建语句对象 344 345 ps = conn.prepareStatement(sql); 346 347 //3.1设置占位符参数对应的值 348 349 ps.setInt(1, id); 350 351 352 353 // 4.执行SQL语句 354 355 rs = ps.executeQuery(); 356 357 if(rs.next()) { 358 359 //通过数据库数据和Java对应的数据类型获取对应的只 360 361 String name = rs.getString("name"); 362 363 int age = rs.getInt("age"); 364 365 //System.out.println(id+","+name+","+age); 366 367 368 369 //将获取的数据封装成对应的Student对象 370 371 Student stu = new Student(id, name, age); 372 373 374 375 return stu; 376 377 } 378 379 380 381 } catch (Exception e) { 382 383 // TODO: handle exception 384 385 }finally { 386 387 try { 388 389 if(rs !=null) { 390 391 rs.close(); 392 393 } 394 395 } catch (SQLException e) { 396 397 e.printStackTrace(); 398 399 }finally { 400 401 try { 402 403 if(ps !=null) { 404 405 ps.close(); 406 407 } 408 409 } catch (SQLException e) { 410 411 e.printStackTrace(); 412 413 }finally { 414 415 try { 416 417 if(conn !=null) { 418 419 conn.close(); 420 421 } 422 423 } catch (SQLException e) { 424 425 e.printStackTrace(); 426 427 } 428 429 } 430 431 } 432 433 } 434 435 436 437 return null; 438 439 } 440 441 442 443 @Override 444 445 public List<Student> selectList() { 446 447 String sql = "select * from t_student"; 448 449 //创建list集合用于封装Student对象 450 451 List<Student> stus = new ArrayList<>(); 452 453 454 455 Connection conn = null; 456 457 PreparedStatement ps = null; 458 459 ResultSet rs = null; 460 461 462 463 try { 464 465 466 467 // 1.加载注册驱动 468 469 Class.forName("com.mysql.jdbc.Driver"); 470 471 472 473 // 2.获取数据库连接对象 474 475 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 476 477 // 3.创建语句对象 478 479 ps = conn.prepareStatement(sql); 480 481 482 483 // 4.执行SQL语句 484 485 rs = ps.executeQuery(); 486 487 while(rs.next()) { 488 489 //通过数据库数据和Java对应的数据类型获取对应的只 490 491 int id = rs.getInt("id"); 492 493 String name = rs.getString("name"); 494 495 int age = rs.getInt("age"); 496 497 //System.out.println(id+","+name+","+age); 498 499 500 501 //将获取的数据封装成对应的Student对象 502 503 Student stu = new Student(id, name, age); 504 505 //将一个个Student对象添加到list集合中 506 507 stus.add(stu); 508 509 } 510 511 512 513 } catch (Exception e) { 514 515 // TODO: handle exception 516 517 }finally { 518 519 try { 520 521 if(rs !=null) { 522 523 rs.close(); 524 525 } 526 527 } catch (SQLException e) { 528 529 e.printStackTrace(); 530 531 }finally { 532 533 try { 534 535 if(ps !=null) { 536 537 ps.close(); 538 539 } 540 541 } catch (SQLException e) { 542 543 e.printStackTrace(); 544 545 }finally { 546 547 try { 548 549 if(conn !=null) { 550 551 conn.close(); 552 553 } 554 555 } catch (SQLException e) { 556 557 e.printStackTrace(); 558 559 } 560 561 } 562 563 } 564 565 } 566 567 568 569 return stus; 570 571 } 572 573 574 575 } 576 577
[10] 快速生成单元测试类
一个dao层或者service编写代码以后,需要为每一个功能都进行单元测试,一个dao中的方法很多。我们快速为这个dao层的类生成单元测试类,(dao的每一个方法都自动生成一个测试方法)
4) 代码初步重构
上述的DAO方法中的代码,存在的问题:
问题1:每个DAO方法中都会写:驱动名称/url/账号/密码,不利于维护. 解决方案: 声明为成员变量即可.(在被类中任何地方都可以访问)
问题2:问题1的解决方案有问题. 每个DAO实现类里都有一模一样的4行代码,不利于维护(考虑有100个DAO实现类,就得重复99次). 解决方案: 把驱动名称/url/账号/密码这四行代码,专门抽取到一个JDBC的工具类中.---->JdbcUtil. 问题3:其实DAO方法,每次操作都只想需要Connection对象即可,而不关心是如何创建的. 解决方案:把创建Connection的代码,抽取到JdbcUtil中,并提供方法getConn用于向调用者返回Connection对象即可. 问题4:每次调用者调用getConn方法的时候,都会创建一个Connection对象. 但是,每次都会加载注册驱动一次.--->没必要的. 解决方案:把加载注册驱动的代码放在静态代码块中--->只会在所在类被加载进JVM的时候,执行一次. 问题5:每个DAO方法都要关闭资源.(鸡肋代码). 解决方案:把关闭资源的代码,抽取到JdbcUtil中. public static void close(Connection conn, Statement st, ResultSet rs) {} 调用者: DML: JdbcUtil.close(conn,st,null); DQL: JdbcUtil.close(conn,st,rs); 问题6 :连接数据库的账号密码写死在JdbcUtil工具类中了,不利于维护 抽取 db.properties 配置文件,将数据库对应的账号密码写到配置文件中,然后使用程序读取配置文件内容即可
|
[1]JdbcUtil工具类
1 package cn.sxt.jdbc.util; 2 3 4 5 import java.io.InputStream; 6 7 import java.sql.Connection; 8 9 import java.sql.DriverManager; 10 11 import java.sql.PreparedStatement; 12 13 import java.sql.ResultSet; 14 15 import java.sql.SQLException; 16 17 import java.util.Properties; 18 19 20 21 22 23 public class JdbcUtil { 24 25 26 27 // alt+shif+a 多行修改,修改以后还原 alt+shif+a 28 29 30 31 /*private static String driverClassName = "com.mysql.jdbc.Driver"; 32 33 private static String url = "jdbc:mysql://localhost:3306/jdbcdemo"; 34 35 private static String username = "root"; 36 37 private static String password = "root";*/ 38 39 40 41 private static Properties p = new Properties(); 42 43 44 45 static { 46 47 try { 48 49 //1.获取类加载器 50 51 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 52 53 //2,使用类加载器获取项目 类路径下面的文件 54 55 InputStream inputStream = classLoader.getResourceAsStream("db.properties"); 56 57 58 59 //3.使用Priperties加载配置文件对应的输入流 60 61 p.load(inputStream); 62 63 64 65 Class.forName(p.getProperty("driverClassName")); 66 67 } catch (Exception e) { 68 69 e.printStackTrace(); 70 71 } 72 73 } 74 75 76 77 public static Connection getConnection() { 78 79 try { 80 81 82 83 return DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"), p.getProperty("password")); 84 85 } catch (Exception e) { 86 87 e.printStackTrace(); 88 89 throw new RuntimeException("亲,连接数据库失败", e); 90 91 } 92 93 } 94 95 96 97 public static void close(Connection conn,PreparedStatement ps,ResultSet rs) { 98 99 try { 100 101 if(rs !=null) { 102 103 rs.close(); 104 105 } 106 107 } catch (SQLException e) { 108 109 e.printStackTrace(); 110 111 }finally { 112 113 try { 114 115 if(ps !=null) { 116 117 ps.close(); 118 119 } 120 121 } catch (SQLException e) { 122 123 e.printStackTrace(); 124 125 }finally { 126 127 try { 128 129 if(conn !=null) { 130 131 conn.close(); 132 133 } 134 135 } catch (SQLException e) { 136 137 e.printStackTrace(); 138 139 } 140 141 } 142 143 } 144 145 } 146 147 }
[2]使用工具类以后的DAO实现类效果
1 package cn.sxt.jdbc.dao.impl; 2 3 4 5 import java.sql.Connection; 6 7 import java.sql.PreparedStatement; 8 9 import java.sql.ResultSet; 10 11 import java.sql.SQLException; 12 13 import java.util.ArrayList; 14 15 import java.util.List; 16 17 18 19 import cn.sxt.jdbc.dao.StudentDao; 20 21 import cn.sxt.jdbc.domain.Student; 22 23 import cn.sxt.jdbc.util.JdbcUtil; 24 25 26 27 public class StudentDaoImpl implements StudentDao { 28 29 30 31 32 33 34 35 @Override 36 37 public int saveStudent(Student stu) { 38 39 String sql = "insert into t_student(name,age) values (?,?)"; 40 41 42 43 Connection conn = null; 44 45 PreparedStatement ps = null; 46 47 try { 48 49 conn = JdbcUtil.getConnection(); 50 51 52 53 // 3.创建预编译语句对象 54 55 ps = conn.prepareStatement(sql); 56 57 //3.1设置占位符参数 58 59 ps.setString(1, stu.getName()); 60 61 ps.setInt(2, stu.getAge()); 62 63 64 65 // 4.执行SQL语句:注意不要带SQL参数 66 67 return ps.executeUpdate(); 68 69 70 71 } catch (Exception e) { 72 73 e.printStackTrace(); 74 75 }finally { 76 77 JdbcUtil.close(conn, ps, null); 78 79 } 80 81 return 0; 82 83 } 84 85 86 87 @Override 88 89 public int deleteById(int id) { 90 91 92 93 String sql = "delete from t_student where id = ?"; 94 95 96 97 Connection conn = null; 98 99 PreparedStatement ps = null; 100 101 try { 102 103 104 105 conn = JdbcUtil.getConnection(); 106 107 // 3.创建预编译语句对象 108 109 ps = conn.prepareStatement(sql); 110 111 //3.1设置占位符参数 112 113 ps.setInt(1, id); 114 115 116 117 // 4.执行SQL语句:注意不要带SQL参数 118 119 return ps.executeUpdate(); 120 121 122 123 124 125 } catch (Exception e) { 126 127 e.printStackTrace(); 128 129 }finally { 130 131 JdbcUtil.close(conn, ps, null); 132 133 } 134 135 return 0; 136 137 } 138 139 140 141 @Override 142 143 public int updateStudentById(Student stu) { 144 145 146 147 String sql = "update t_student set name = ?,age = ? where id = ?"; 148 149 150 151 Connection conn = null; 152 153 PreparedStatement ps = null; 154 155 try { 156 157 158 159 conn = JdbcUtil.getConnection(); 160 161 // 3.创建预编译语句对象 162 163 ps = conn.prepareStatement(sql); 164 165 //3.1设置占位符参数 166 167 ps.setString(1, stu.getName()); 168 169 ps.setInt(2, stu.getAge()); 170 171 ps.setInt(3, stu.getId()); 172 173 // 4.执行SQL语句:注意不要带SQL参数 174 175 return ps.executeUpdate(); 176 177 178 179 180 181 } catch (Exception e) { 182 183 e.printStackTrace(); 184 185 }finally { 186 187 JdbcUtil.close(conn, ps, null); 188 189 } 190 191 return 0; 192 193 } 194 195 196 197 @Override 198 199 public Student selectById(int id) { 200 201 String sql = "select * from t_student where id = ?"; 202 203 204 205 Connection conn = null; 206 207 PreparedStatement ps = null; 208 209 ResultSet rs = null; 210 211 212 213 try { 214 215 conn = JdbcUtil.getConnection(); 216 217 // 3.创建语句对象 218 219 ps = conn.prepareStatement(sql); 220 221 //3.1设置占位符参数对应的值 222 223 ps.setInt(1, id); 224 225 226 227 // 4.执行SQL语句 228 229 rs = ps.executeQuery(); 230 231 if(rs.next()) { 232 233 //通过数据库数据和Java对应的数据类型获取对应的只 234 235 String name = rs.getString("name"); 236 237 int age = rs.getInt("age"); 238 239 //System.out.println(id+","+name+","+age); 240 241 //将获取的数据封装成对应的Student对象 242 243 Student stu = new Student(id, name, age); 244 245 return stu; 246 247 } 248 249 250 251 } catch (Exception e) { 252 253 // TODO: handle exception 254 255 }finally { 256 257 try { 258 259 if(rs !=null) { 260 261 rs.close(); 262 263 } 264 265 } catch (SQLException e) { 266 267 e.printStackTrace(); 268 269 }finally { 270 271 JdbcUtil.close(conn, ps, rs); 272 273 } 274 275 } 276 277 278 279 return null; 280 281 } 282 283 284 285 @Override 286 287 public List<Student> selectList() { 288 289 String sql = "select * from t_student"; 290 291 //创建list集合用于封装Student对象 292 293 List<Student> stus = new ArrayList<>(); 294 295 296 297 Connection conn = null; 298 299 PreparedStatement ps = null; 300 301 ResultSet rs = null; 302 303 304 305 try { 306 307 308 309 conn = JdbcUtil.getConnection(); 310 311 // 3.创建语句对象 312 313 ps = conn.prepareStatement(sql); 314 315 316 317 // 4.执行SQL语句 318 319 rs = ps.executeQuery(); 320 321 while(rs.next()) { 322 323 //通过数据库数据和Java对应的数据类型获取对应的只 324 325 int id = rs.getInt("id"); 326 327 String name = rs.getString("name"); 328 329 int age = rs.getInt("age"); 330 331 //System.out.println(id+","+name+","+age); 332 333 334 335 //将获取的数据封装成对应的Student对象 336 337 Student stu = new Student(id, name, age); 338 339 //将一个个Student对象添加到list集合中 340 341 stus.add(stu); 342 343 } 344 345 346 347 } catch (Exception e) { 348 349 // TODO: handle exception 350 351 }finally { 352 353 JdbcUtil.close(conn, ps, rs); 354 355 } 356 357 358 359 return stus; 360 361 } 362 363 364 365 }
5) 知识点补充,类加载器
在项目的 类路径(src)下面创建一个 db.properties配置文件,专门配置连接数据库的账号密码
如何使用类加载器加载配置文件
[1]配置文件
注:配置文件创建的位置
配置文件一般都放在项目的src 源目录下面
【2】加载代码
1 package cn.sxt.jdbc.test; 2 3 4 5 import static org.junit.Assert.*; 6 7 8 9 import java.io.InputStream; 10 11 import java.util.Properties; 12 13 14 15 import org.junit.Test; 16 17 18 19 public class PropertiesTest { 20 21 22 23 @Test 24 25 public void testName() throws Exception { 26 27 28 29 /* 30 31 * ClassLoader 类加载器 32 33 * ClassLoader :可以从项目的类路径下面读取对应的配置文件返回一个输入流 34 35 * ClassLoader 在程序运行的时候JVM已经为每一个项目都创建了一个,我们开发者只需要获取即可 36 37 * 获取类加载器方式 38 39 * 1、使用当前线程 40 41 * ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 42 43 * 2、通过某一类的字节码实例也可以获取 44 45 * ClassLoader classLoader = PropertiesTest.class.getClassLoader(); 46 47 */ 48 49 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 50 51 //使用类加载器获取项目 类路径下面的文件 52 53 InputStream inputStream = classLoader.getResourceAsStream("db.properties"); 54 55 56 57 58 59 /* 60 61 * Properties 是Map集合下面的一个 专门用于读取配置文件的对象 62 63 * 可以读取当前类路径下面的 xxx.properites类型的配置文件 64 65 * 66 67 * xxx.properites的内容必须是key=value 键值对的数据 68 69 */ 70 71 72 73 //1.创建Properties对象 74 75 Properties p = new Properties(); 76 77 78 79 //2.加载配置文件 80 81 p.load(inputStream); 82 83 84 85 System.out.println(p); 86 87 88 89 //获取具体某一个key对应的值 90 91 String driverClassName = p.getProperty("driverClassName"); 92 93 System.out.println(driverClassName); 94 95 } 96 97 } 98 99
[3] 效果
8.连接池
1)遇到的问题-引出连接池
2) 连接池思想
3) 连接池的概述
在Java中,连接池使用javax.sql.DataSource接口来表示连接池.
注意:DataSource仅仅只是一个接口,由各大服务器厂商来实现(Tomcat.JBoss,阿里巴巴). 常用的DataSource的实现: DBCP: Spring推荐的 C3P0: Hibernate推荐的 Druid : (德鲁伊)阿里巴巴开源的,性能最好,速度最快 DataSource(数据源)和连接池(Connection Pool)是同一个.
|
4) 使用连接池和不使用连接池的区别在哪里
从代码上: 不使用连接池: Conenction对象由DriverManager获取. Connection conn = DriverManager.getConnection(url,username,password);
使用连接池: 如何创建DataSource对象,如何在DataSource中设置url,账号,密码. Connection conn = DataSource对象.getConnection(); -------------------------------------------------------------------- 使用连接池的时候: 释放资源: Connection对象.close(): 是把Connection放回给连接池,而不是和数据库断开. |
5) Druid连接池的使用
[1] 准备druid 连接池jar包到项目
1 package cn.sxt.jdbc.test; 2 3 4 5 import static org.junit.Assert.*; 6 7 8 9 import java.io.InputStream; 10 11 import java.io.Reader; 12 13 import java.sql.Connection; 14 15 import java.util.Properties; 16 17 18 19 import javax.sql.DataSource; 20 21 22 23 import org.junit.Test; 24 25 26 27 import com.alibaba.druid.pool.DruidDataSource; 28 29 import com.alibaba.druid.pool.DruidDataSourceFactory; 30 31 import com.alibaba.druid.pool.DruidPooledConnection; 32 33 34 35 public class DataSourceTest { 36 37 // 直接创建连接池对象 38 39 @Test 40 41 public void testName() throws Exception { 42 43 // 1.创建连接池对象 44 45 DruidDataSource ds = new DruidDataSource(); 46 47 // 2.设置连接数据库的账号密码 48 49 ds.setDriverClassName("com.mysql.jdbc.Driver"); 50 51 ds.setUrl("jdbc:mysql://localhost:3306/jdbcdemo"); 52 53 ds.setUsername("root"); 54 55 ds.setPassword("root"); 56 57 ds.setMaxActive(10);// 最大连接数 58 59 // 3.获取连接对象 60 61 Connection conn = ds.getConnection(); 62 63 System.out.println(conn); 64 65 } 66 67 68 69 // 使用工厂对象创建连接池对象,工厂对象的好处,不需要直接设置账号密码等等,只需要将 70 71 // 连接数据库的账号密码等等以指定的 key的名称配置到 xxx.properties文件中即可,工厂对象底层自动读取 72 73 @Test 74 75 public void testDataSourceByFactory() throws Exception { 76 77 78 79 // 1.获取类加载器用于加载clsspath下面的 配置文件 80 81 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 82 83 // 2.读取druid.properties配置文件 84 85 InputStream inputStream = classLoader.getResourceAsStream("druid.properties"); 86 87 // 3.创建Properties对象,并读取配置文件对应的输入流 88 89 Properties p = new Properties(); 90 91 p.load(inputStream); 92 93 94 95 // 4.创建连接池对象 96 97 DataSource ds = DruidDataSourceFactory.createDataSource(p); 98 99 // 5.获取连接对象 100 101 Connection conn = ds.getConnection(); 102 103 System.out.println(conn); 104 105 } 106 107 }
[2]db.propperties
[3]使用Druid抽取的工具类
9. 事务
案例:银行转账:从张无忌账户上给赵敏转1000块. 准备:account(账户表): --------------------------------------------------------------- id name(账号,唯一) balance(余额) 1 张无忌 20000 2 赵敏 0 --------------------------------------------------------------- 转账的思路: 1.检查张无忌的账号余额是否大于等于1000. SQL: SELECT balance FROM account WHERE name = '张无忌' AND balance >=1000 余额>=1000:GOTO 2: 余额 <1000:提示:亲,你的余额不足. 2.在张无忌的账号余额上减少1000. SQL: UPDATE account SET balance = balance-1000 WHERE name = '张无忌' 3.在赵敏的账户余额尚增加1000. SQL: UPDATE account SET balance = balance+1000 WHERE name = '赵敏' ------------------------------------------------------------------------------------------- 注意:在第二步和第三步之间,停电了. 使用异常模拟停电:System.out.println(1/0); |
1)事务概述
事务(Transaction,简写为tx): 在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态。 为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元: 当每个逻辑操作单元全部完成时,数据的一致性可以保持, 而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。
事务的操作:先定义开始一个事务,然后对数据作修改操作,这时如果提交(commit),这些修改就永久地保存下来,如果回退(rollback),数据库管理系统将放弃您所作的所有修改而回到开始事务时的状态。
-------------------------------------------------- 事务的ACID属性: 1. 原子性(Atomicity) 2. 一致性(Consistency) 3. 隔离性(Isolation) 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 4. 持久性(Durability)
-------------------------------------------------- 事务:指构成单个逻辑工作单元的操作集合 事务处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),要么整个事务回滚(rollback)到最初状态 处理事务的两个动作: 提交:commit: 当整个事务中,所有的逻辑单元都正常执行成功. ---->提交事务.---数据已经提交,不能更改. 回滚:rollback: 当整个事务中,有一个逻辑单元执行失败, ---->回滚事务. 撤销该事务中的所有操作--->恢复到最初的状态.
--------------------------------------------------------------------------------------------------- 如何在代码中去处理事务: 1.在JDBC中,事务是默认自动提交的. 必须先设置事务为手动提交. connection对象.setAutoCommit(false);//设置事务为手动提交. 2.手动的提交事务. connection对象.commit(); 3.若出现异常必须回滚事务: 不回滚事务,总余额依然是正确的. 若不回滚事务,不会释放数据库资源. connection对象.rollback(); ----------------------------------------------------------------------------------- 1.在JDBC在事务是默认提交的,那是在什么时候提交的. 在执行一个DML/DDL操作的时候,就已经提交事务了. 2.针对于CRUD操作. 只有DML操作才有事务,查询操作没有事务. 但是,我们一般会把查询也放在事务里面.
4.MySQL中,InnoDB支持外键.支持事务,MyISAM不支持外键,不支持事务.
InnoDB存储引擎: 支持事务,支持外键,但是查询效率略低,(金融,理财,p2p) MyISAM存储引擎:不支持事务和外键,但是查询效率较高(新闻网站)
Oracle 不存在存储引擎,都有事务 |
2)事务处理代码
1 public class TransactionTest { 2 3 @Test 4 5 public void testName() throws Exception { 6 7 Connection conn = null; 8 9 Statement st = null; 10 11 ResultSet rs = null; 12 13 try { 14 15 conn = DruidUtil.getConnection(); 16 17 //将事务设置为手动提交 18 19 conn.setAutoCommit(false); 20 21 22 23 st = conn.createStatement(); 24 25 // 1.检查张无忌的账号余额是否大于等于1000. 26 27 rs = st.executeQuery("SELECT balance FROM account WHERE name = '张无忌' AND balance >=1000"); 28 29 if(!rs.next()) { 30 31 throw new RuntimeException("亲,您的账户余额不够"); 32 33 } 34 35 // 余额>=1000:GOTO 2: 36 37 // 余额 <1000:提示:亲,你的余额不足. 38 39 // 2.在张无忌的账号余额上减少1000. 40 41 st.executeUpdate("UPDATE account SET balance = balance-1000 WHERE name = '张无忌'"); 42 43 44 45 System.out.println(1/0); 46 47 48 49 // 3.在赵敏的账户余额尚增加1000. 50 51 st.executeUpdate("UPDATE account SET balance = balance+1000 WHERE name = '赵敏'"); 52 53 54 55 //提交事务 56 57 conn.commit(); 58 59 60 61 62 63 } catch (Exception e) { 64 65 e.printStackTrace(); 66 67 //回滚事务 68 69 conn.rollback(); 70 71 72 73 }finally { 74 75 DruidUtil.close(conn, st, rs); 76 77 } 78 79 80 81 } 82 83 }