JDBC

1.jdbc概述

问题:实际开发中,不可能用工具或者命令行操作数据库,数据库表中的数据最终要使用Java程序来操作,那么Java中如何操作数据库中的数据呢?

Java语言中,有一个专门连接数据库的规范(JDBC),专门负责连接数据库进行数据操作的规范

JDBC只是SUN编写的一堆接口(规范的体现),SUN公司自己并没有实现

 

问题 为什么SUN只定义一个JDBC规范,而不实现呢?

因为市面上的数据库很多,每个数据库内部接口不会向外暴露,而且即便是暴露让SUN去实现,市面上很多数据库全部要SUN来实现不现实

 

实际中哪个数据库需要支持JAVA语言,就需要自己实现JavaJDBC规范,因为实现了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() 偏移光标,循环指定具体的某一行

获取数据的具体方法

 Object

getObject(int columnIndex) 
 columnIndex : 通过结果集的位置(1开始)获取对应的数据

 Object

getObject(String columnLabel) 
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

 Statement

createStatement() 
创建一个 Statement 对象来将 SQL 语句发送到数据库。

 

创建预编译语句对象PreparedStatement

 PreparedStatement

prepareStatement(String sql) 
创建预编译语句对象,SQL是带有占位符的SQL模板.

 

2)执行SQL语句的方法

[1] Statment

在执行SQL语句的时候回带上SQL语句

 ResultSet

executeQuery(String sql) 
          执行给定的 SQL 语句,该语句返回单个 ResultSet 对象。

 int

executeUpdate(String sql) 
          执行给定 SQL 语句,该语句可能为 INSERTUPDATE 或 DELETE 语句,或者不返回任何内容的 SQL 语句(如 SQL DDL 语句)。

[2]PreparedStatement 

在执行SQL语句的方法中不需要设置SQL语句

 ResultSet

executeQuery() 
          在此 PreparedStatement 对象中执行 SQL 查询,并返回该查询生成的 ResultSet 对象。

 int

executeUpdate() 
          在此 PreparedStatement 对象中执行 SQL 语句,该语句必须是一个 SQL 数据操作语言(Data Manipulation Language,DML)语句,比如 INSERTUPDATE 或 DELETE 语句;或者是无返回内容的 SQL 语句,比如 DDL 语句。

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]数据表对应的Javadomain/pojo包命名

POJOPlain 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操作才有事务,查询操作没有事务.

  但是,我们一般会把查询也放在事务里面.

  1. 以后,凡是发现自己编写的代码是正确的,测试也通过,但是就是数据库表中的数据不变----->事务没提交的问题.

 

 

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 }
原文地址:https://www.cnblogs.com/qq2267711589/p/10887536.html