JDBC
1. Jdbc概述
问题:实际开发中,不可能用工具或者命令行操作数据库,数据库表中的数据最终要使用Java程序来操作,那么Java中如何操作数据库中的数据呢?
答 : 在Java语言中,有一个专门连接数据库的规范(JDBC),专门负责连接数据库进行数据操作的规范
JDBC只是SUN编写的一堆接口(规范的体现),SUN公司自己并没有实现
问题 : 为什么SUN只定义一个JDBC规范,而不实现呢?
答 : 因为市面上的数据库很多,每个数据库内部接口不会向外暴露,而且即便是暴露让SUN去实现,市面上很多数据库全部要SUN来实现不现实
实际中哪个数据库需要支持JAVA语言,就需要自己实现Java的JDBC规范,因为实现了JDBC很多接口,那么就会有很多实现类,而很多实现类在java中会使用一个专门的包封装起来,叫做jar包(在JDBC中叫做驱动包),各大数据库产商实现JDBC规范以后都会把他们jar包放在官网上以供开发者下载使用
1.1. JDBC
JDBC(Java DataBase Connectivity):
是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基
JDBC规范对应的api包
2.java连接数据库
以连接mysql为例
2.1. 创建普通java项目
2.2. 在项目下面新建一个lib目录
2.3. 将MySQL驱动包拷贝到项目中并添加依赖
2.5 获取数据库连接对象
准备:
1.拷贝MySQL的驱动包到项目中去:mysql-connector-java-5.1.x-bin.jar
2.build path,告速项目去哪里去找字节码文件.
--------------------------------------------------------------------------------
操作JDBC的第一步,获取JDBC的连接对象.:Connection.
步骤:
加载注册驱动.
就是把驱动中的Driver字节码加载到JVM中.
Class.forName("com.mysql.jdbc.Driver");
为什么这句话就可以加载注册驱动?
第一步:把com.mysql.jdbc.Driver.class这份字节码加载到JVM中.
第二步:当一份字节码被加载进JVM,马上就会执行该字节码中的静态代码块.
第三步:该静态代码中,就在完成,先创建驱动对象,再注册.
通过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 public static void main(String[] args) throws Exception {
3
4 //1.加载注册驱动 : 把当前类对应的字节码加载到JVM中
5 Class.forName("com.mysql.jdbc.Driver");
6 //2.获取数据库连接
7 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
8 System.out.println(conn);
9
10 }
11 }
3. 创建表-DDL操作
在其他操作之间先要把数据库表要创建出来
/贾琏欲执事
创建一张t_student表:
id:
name:
age:
1 //DML : 对表数据的增删改操作
2 public class DMLDemo {
3
4 /*
5 * 向 t_student表中插入一条数据
6 * sql : insert into t_student(name,age) values ('乔峰',30)
7 */
8 @Test
9 public void testInsert() throws Exception {
10 String sql = "insert into t_student(name,age) values ('乔峰',30)";
11
12 // 1.加载注册驱动
13 Class.forName("com.mysql.jdbc.Driver");
14
15 // 2.获取数据库连接对象
16 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
17 // 3.创建语句对象
18 Statement st = conn.createStatement();
19
20 // 4.执行SQL语句
21 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数
22 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象
23 int rows = st.executeUpdate(sql);
24 System.out.println(rows);
25 //5.释放资源(先开后关)
26 st.close();
27 conn.close();
28
29 }
30 /*
31 * 删除操作: 删除t_student表中的某一条数据
32 * SQL :delete from t_student where id = 2
33 */
34 @Test
35 public void testDelete() throws Exception {
36 String sql = "delete from t_student where id = 2";
37
38 // 1.加载注册驱动
39 Class.forName("com.mysql.jdbc.Driver");
40
41 // 2.获取数据库连接对象
42 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
43 // 3.创建语句对象
44 Statement st = conn.createStatement();
45
46 // 4.执行SQL语句
47 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数
48 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象
49 int rows = st.executeUpdate(sql);
50 System.out.println(rows);
51 //5.释放资源(先开后关)
52 st.close();
53 conn.close();
54 }
55 /*
56 * 修改操作 : 修改t_student表中的某一条数据
57 * SQL : update t_student set name = '虚竹',age = 50 where id = 3
58 */
59 @Test
60 public void testUpdate() throws Exception {
61 String sql = "update t_student set name = '虚竹',age = 50 where id = 3";
62
63 // 1.加载注册驱动
64 Class.forName("com.mysql.jdbc.Driver");
65
66 // 2.获取数据库连接对象
67 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
68 // 3.创建语句对象
69 Statement st = conn.createStatement();
70
71 // 4.执行SQL语句
72 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数
73 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象
74 int rows = st.executeUpdate(sql);
75 System.out.println(rows);
76 //5.释放资源(先开后关)
77 st.close();
78 conn.close();
79 }
80 }
4.DML操作-表数据的增删改
1 //DML : 对表数据的增删改操作
2 public class DMLDemo {
3
4 /*
5 * 向 t_student表中插入一条数据
6 * sql : insert into t_student(name,age) values ('乔峰',30)
7 */
8 @Test
9 public void testInsert() throws Exception {
10 String sql = "insert into t_student(name,age) values ('乔峰',30)";
11
12 // 1.加载注册驱动
13 Class.forName("com.mysql.jdbc.Driver");
14
15 // 2.获取数据库连接对象
16 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
17 // 3.创建语句对象
18 Statement st = conn.createStatement();
19
20 // 4.执行SQL语句
21 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数
22 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象
23 int rows = st.executeUpdate(sql);
24 System.out.println(rows);
25 //5.释放资源(先开后关)
26 st.close();
27 conn.close();
28
29 }
30 /*
31 * 删除操作: 删除t_student表中的某一条数据
32 * SQL :delete from t_student where id = 2
33 */
34 @Test
35 public void testDelete() throws Exception {
36 String sql = "delete from t_student where id = 2";
37
38 // 1.加载注册驱动
39 Class.forName("com.mysql.jdbc.Driver");
40
41 // 2.获取数据库连接对象
42 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
43 // 3.创建语句对象
44 Statement st = conn.createStatement();
45
46 // 4.执行SQL语句
47 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数
48 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象
49 int rows = st.executeUpdate(sql);
50 System.out.println(rows);
51 //5.释放资源(先开后关)
52 st.close();
53 conn.close();
54 }
55 /*
56 * 修改操作 : 修改t_student表中的某一条数据
57 * SQL : update t_student set name = '虚竹',age = 50 where id = 3
58 */
59 @Test
60 public void testUpdate() throws Exception {
61 String sql = "update t_student set name = '虚竹',age = 50 where id = 3";
62
63 // 1.加载注册驱动
64 Class.forName("com.mysql.jdbc.Driver");
65
66 // 2.获取数据库连接对象
67 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
68 // 3.创建语句对象
69 Statement st = conn.createStatement();
70
71 // 4.执行SQL语句
72 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数
73 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象
74 int rows = st.executeUpdate(sql);
75 System.out.println(rows);
76 //5.释放资源(先开后关)
77 st.close();
78 conn.close();
79 }
80 }
5. DQL操作-查询操作
5.1. 查询操作的分析
5.2 查询具体操作
结果集的列的位置
1.使用 rs.next() 偏移光标,循环指定具体的某一行
获取数据的具体方法
Object | getObject(int columnIndex) |
Object | getObject(String columnLabel)
|
1 package cn.sxt.jdbc._01connection;
2
3
4 import java.sql.Connection;
5 import java.sql.DriverManager;
6 import java.sql.ResultSet;
7 import java.sql.Statement;
8 import java.util.ArrayList;
9 import java.util.List;
10
11 import org.junit.Test;
12
13 //DQL :查询操作
14 public class D_DQLDemo {
15
16 /*
17 * 多行查询 :查询t_student表中的所有数据
18 * SQL : select * from t_student
19 */
20 @Test
21 public void testList() throws Exception {
22 String sql = "select * from t_student";
23
24 // 1.加载注册驱动
25 Class.forName("com.mysql.jdbc.Driver");
26
27 // 2.获取数据库连接对象
28 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
29 // 3.创建语句对象
30 Statement st = conn.createStatement();
31
32 // 4.执行SQL语句
33 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数
34 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象
35 ResultSet rs = st.executeQuery(sql);
36
37
38 //创建list集合用于封装Student对象
39 List<Student> stus = new ArrayList<>();
40
41 while(rs.next()) {
42 //1.通过结果集的位置获取对应的数
43 /*Object id = rs.getObject(1);
44 Object name = rs.getObject(2);
45 Object age = rs.getObject(3);*/
46
47 //2.通过结果集的 列名获取对应的数据
48 /*Object id = rs.getObject("id");
49 Object name = rs.getObject("name");
50 Object age = rs.getObject("age");*/
51 //3.通过数据库数据和Java对应的数据类型获取对应的只
52 int id = rs.getInt("id");
53 String name = rs.getString("name");
54 int age = rs.getInt("age");
55 //System.out.println(id+","+name+","+age);
56
57 //将获取的数据封装成对应的Student对象
58 Student stu = new Student(id, name, age);
59
60 //将一个个Student对象添加到list集合中
61 stus.add(stu);
62 }
63
64 for (Student student : stus) {
65 System.out.println(student);
66 }
67 //5.释放资源(先开后关)
68 rs.close();
69 st.close();
70 conn.close();
71 }
72
73
74 /*
75 * 单行查询: 查询出t_student 指定id的信息
76 * SQL : select * from t_student where id = 1;
77 */
78 @Test
79 public void testGetOne() throws Exception {
80 String sql = "select * from t_student where id = 2";
81
82 // 1.加载注册驱动
83 Class.forName("com.mysql.jdbc.Driver");
84
85 // 2.获取数据库连接对象
86 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
87 // 3.创建语句对象
88 Statement st = conn.createStatement();
89
90 // 4.执行SQL语句
91 // int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数
92 // ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象
93 ResultSet rs = st.executeQuery(sql);
94
95 if(rs.next()) {
96 //1.通过结果集的位置获取对应的数
97 /*Object id = rs.getObject(1);
98 Object name = rs.getObject(2);
99 Object age = rs.getObject(3);*/
100
101 //2.通过结果集的 列名获取对应的数据
102 /*Object id = rs.getObject("id");
103 Object name = rs.getObject("name");
104 Object age = rs.getObject("age");*/
105 //3.通过数据库数据和Java对应的数据类型获取对应的只
106 int id = rs.getInt("id");
107 String name = rs.getString("name");
108 int age = rs.getInt("age");
109 //System.out.println(id+","+name+","+age);
110
111 //将获取的数据封装成对应的Student对象
112 Student stu = new Student(id, name, age);
113 System.out.println(stu);
114 }
115 //5.释放资源(先开后关)
116 rs.close();
117 st.close();
118 conn.close();
119 }
120 }
6. 预编译语句对象PreparedStatment
问题 : 我们有了Statment对象可以执行SQL,为什么还要使用PreparedStatment?
优势
- SQL语句结构清晰,参数的设置和SQL语句分离
- 性能更高
- 防止SQL注入
Statement: 表示静态SQL语句对象. PreparedStatement:Statement的子接口,表示预编译SQL语句对象. 通过占位符(?)来拼SQL. |
6.1 创建PreparedStatement
创建语句对象
Statement | createStatement() |
创建预编译语句对象PreparedStatement
PreparedStatement | prepareStatement(String sql) | |
|
6.2. 执行SQL语句的方法
6.2.1. Statment
在执行SQL语句的时候回带上SQL语句
ResultSet | executeQuery(String sql) |
int | executeUpdate(String sql) |
6.2.2. PreparedStatement
在执行SQL语句的方法中不需要设置SQL语句
ResultSet | executeQuery() |
int | executeUpdate() |
6.3. 设置站位参数的值
void setXxx(int parameterIndex,Xxx value):用于设置占位符参数, 第几个问号. 注意:从1开始. 设置的真实值. Xxx:表示数据类型.String/int/long/Double/Date |
6.4. 代码
package cn.sxt.jdbc._01connection;
import static org.junit.Assert.*;
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement;
import org.junit.Test;
//DML : 对表数据的增删改操作,使用预编译语句对象 public class E_DMLByPreparedStatmentDemo
/* * 向 t_student表中插入一条数据 sql */ @Test public void testInsert() throws Exception { String sql = "insert into t_student(name,age) values (?,?)";
// 1.加载注册驱动 Class.forName("com.mysql.jdbc.Driver");
// 2.获取数据库连接对象 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); // 3.创建预编译语句对象 PreparedStatement ps = conn.prepareStatement(sql); //3.1设置占位符参数 ps.setString(1, "东方姑娘"); ps.setInt(2, 18);
// 4.执行SQL语句:注意不要带SQL参数 ps.executeUpdate(); //5.释放资源(先开后关) ps.close(); conn.close();
} /* * 删除操作: 删除t_student表中的某一条数据 * SQL :delete from t_student where id = 2 */ @Test public void testDelete() throws Exception { String sql = "delete from t_student where id = ?";
// 1.加载注册驱动 Class.forName("com.mysql.jdbc.Driver");
// 2.获取数据库连接对象 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); // 3.创建预编译语句对象 PreparedStatement ps = conn.prepareStatement(sql); //3.1设置占位符对应的参数值 ps.setInt(1, 1);
// 4.执行SQL语句 int rows = ps.executeUpdate(); System.out.println(rows); //5.释放资源(先开后关) ps.close(); conn.close(); } /* * 修改操作 : 修改t_student表中的某一条数据 * SQL : update t_student set name = '虚竹',age = 50 where id = 3 */ @Test public void testUpdate() throws Exception { String sql = "update t_student set name = ?,age = ? where id = ?";
// 1.加载注册驱动 Class.forName("com.mysql.jdbc.Driver");
// 2.获取数据库连接对象 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); // 3.创建预编译语句对象 PreparedStatement ps = conn.prepareStatement(sql); //3.1设置占位符参数对应的值 ps.setString(1, "西方失败"); ps.setInt(2, 40); ps.setInt(3, 4); // 4.执行SQL语句 int rows = ps.executeUpdate(); System.out.println(rows); //5.释放资源(先开后关) ps.close(); conn.close(); } }
|
7. JavaWeb开发的分层设计-三层架构
7.1. DAO层设计
实际开发中,JavaWeb开发代码一般分为三层,分层结构是JavaWeb开发中的一种设计思想,这样会让我们开发层次分明,每一层只要完成对应的功能即可,使得项目便于开发和维护
1 . Web层/表现层 : 主要接受前台浏览器用户的参数,给浏览器响应数据等等
- Service层/业务成/服务层:主要处理业务功能,日志,权限,事物,等等
- DAO层/持久层 :专门负责和数据库交互,数据处理相关代码
DAO : Data Access Object 数据访问对象
实际开发中 : 用户请求到-Web层--->Service层-->DAO层
7.2. DAO思想
|
7.3. 使用DAO以后代码的以及包的设计结构
开发中如果使用的分层,编写的包和类名接口名等等都是有固定规则,不能随便瞎写
7.3.1. DAO层接口包命名
公司域名倒写+项目名称/模块名称+dao 如 : cn.sxt.crm.dao |
7.3.2. DAO层实现类包命名
公司域名倒写+项目名称/模块名称+dao+impl 如 : cn.sxt.crm.dao.impl |
7.3.3. DAO层操作对应表的接口命名
对应表的名称 + Dao/DAO
如 : StudentDao/DAO , TeacherDao/DAO |
7.3.4. DAO层操作对应表的实现类命名
对应表的名称 + Dao/DAOImpl
如 : StudentDaoImpl/DAOImpl , TeacherDaoImpl/DAOImpl |
7.3.5. 数据表对应的Java类domain/pojo包命名
POJO(Plain Ordinary Java Object)简单的Java对象
domian : 域对象
公司域名倒写+项目名称/模块名称+domain/pojo 如 : cn.sxt.crm.domain |
7.3.6. 对应的测试包命名
公司域名倒写+项目名称/模块名称+test 如 : cn.sxt.crm.test |
7.3.7. 项目的工具类包命名
公司域名倒写+项目名称/模块名称+util/utils 如 : cn.sxt.crm.util/utils |
7.3.8. DAO代码设计结构
7.3.9. Dao的实现类代码
1 package cn.sxt.jdbc.dao.impl;
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 import java.util.ArrayList;
9 import java.util.List;
10
11 import cn.sxt.jdbc.dao.StudentDao;
12 import cn.sxt.jdbc.domain.Student;
13
14 public class StudentDaoImpl implements StudentDao {
15
16 @Override
17 public int saveStudent(Student stu) {
18 String sql = "insert into t_student(name,age) values (?,?)";
19
20 Connection conn = null;
21 PreparedStatement ps = null;
22 try {
23
24 // 1.加载注册驱动
25 Class.forName("com.mysql.jdbc.Driver");
26
27 // 2.获取数据库连接对象
28 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
29 // 3.创建预编译语句对象
30 ps = conn.prepareStatement(sql);
31 //3.1设置占位符参数
32 ps.setString(1, stu.getName());
33 ps.setInt(2, stu.getAge());
34
35 // 4.执行SQL语句:注意不要带SQL参数
36 return ps.executeUpdate();
37
38
39 } catch (Exception e) {
40 e.printStackTrace();
41 }finally {
42 //5.释放资源(先开后关)
43 try {
44 if(ps !=null) {
45 ps.close();
46 }
47 } catch (SQLException e) {
48 e.printStackTrace();
49 }finally {
50 try {
51 if(conn !=null) {
52 conn.close();
53 }
54 } catch (SQLException e) {
55 // TODO Auto-generated catch block
56 e.printStackTrace();
57 }
58 }
59 }
60 return 0;
61 }
62
63 @Override
64 public int deleteById(int id) {
65
66 String sql = "delete from t_student where id = ?";
67
68 Connection conn = null;
69 PreparedStatement ps = null;
70 try {
71
72 // 1.加载注册驱动
73 Class.forName("com.mysql.jdbc.Driver");
74
75 // 2.获取数据库连接对象
76 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
77 // 3.创建预编译语句对象
78 ps = conn.prepareStatement(sql);
79 //3.1设置占位符参数
80 ps.setInt(1, id);
81
82 // 4.执行SQL语句:注意不要带SQL参数
83 return ps.executeUpdate();
84
85
86 } catch (Exception e) {
87 e.printStackTrace();
88 }finally {
89 //5.释放资源(先开后关)
90 try {
91 if(ps !=null) {
92 ps.close();
93 }
94 } catch (SQLException e) {
95 e.printStackTrace();
96 }finally {
97 try {
98 if(conn !=null) {
99 conn.close();
100 }
101 } catch (SQLException e) {
102 // TODO Auto-generated catch block
103 e.printStackTrace();
104 }
105 }
106 }
107 return 0;
108 }
109
110 @Override
111 public int updateStudentById(Student stu) {
112
113 String sql = "update t_student set name = ?,age = ? where id = ?";
114
115 Connection conn = null;
116 PreparedStatement ps = null;
117 try {
118
119 // 1.加载注册驱动
120 Class.forName("com.mysql.jdbc.Driver");
121
122 // 2.获取数据库连接对象
123 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
124 // 3.创建预编译语句对象
125 ps = conn.prepareStatement(sql);
126 //3.1设置占位符参数
127 ps.setString(1, stu.getName());
128 ps.setInt(2, stu.getAge());
129 ps.setInt(3, stu.getId());
130 // 4.执行SQL语句:注意不要带SQL参数
131 return ps.executeUpdate();
132
133
134 } catch (Exception e) {
135 e.printStackTrace();
136 }finally {
137 //5.释放资源(先开后关)
138 try {
139 if(ps !=null) {
140 ps.close();
141 }
142 } catch (SQLException e) {
143 e.printStackTrace();
144 }finally {
145 try {
146 if(conn !=null) {
147 conn.close();
148 }
149 } catch (SQLException e) {
150 // TODO Auto-generated catch block
151 e.printStackTrace();
152 }
153 }
154 }
155 return 0;
156 }
157
158 @Override
159 public Student selectById(int id) {
160 String sql = "select * from t_student where id = ?";
161
162 Connection conn = null;
163 PreparedStatement ps = null;
164 ResultSet rs = null;
165
166 try {
167 // 1.加载注册驱动
168 Class.forName("com.mysql.jdbc.Driver");
169
170 // 2.获取数据库连接对象
171 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
172 // 3.创建语句对象
173 ps = conn.prepareStatement(sql);
174 //3.1设置占位符参数对应的值
175 ps.setInt(1, id);
176
177 // 4.执行SQL语句
178 rs = ps.executeQuery();
179 if(rs.next()) {
180 //通过数据库数据和Java对应的数据类型获取对应的只
181 String name = rs.getString("name");
182 int age = rs.getInt("age");
183 //System.out.println(id+","+name+","+age);
184
185 //将获取的数据封装成对应的Student对象
186 Student stu = new Student(id, name, age);
187
188 return stu;
189 }
190
191 } catch (Exception e) {
192 // TODO: handle exception
193 }finally {
194 try {
195 if(rs !=null) {
196 rs.close();
197 }
198 } catch (SQLException e) {
199 e.printStackTrace();
200 }finally {
201 try {
202 if(ps !=null) {
203 ps.close();
204 }
205 } catch (SQLException e) {
206 e.printStackTrace();
207 }finally {
208 try {
209 if(conn !=null) {
210 conn.close();
211 }
212 } catch (SQLException e) {
213 e.printStackTrace();
214 }
215 }
216 }
217 }
218
219 return null;
220 }
221
222 @Override
223 public List<Student> selectList() {
224 String sql = "select * from t_student";
225 //创建list集合用于封装Student对象
226 List<Student> stus = new ArrayList<>();
227
228 Connection conn = null;
229 PreparedStatement ps = null;
230 ResultSet rs = null;
231
232 try {
233
234 // 1.加载注册驱动
235 Class.forName("com.mysql.jdbc.Driver");
236
237 // 2.获取数据库连接对象
238 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");
239 // 3.创建语句对象
240 ps = conn.prepareStatement(sql);
241
242 // 4.执行SQL语句
243 rs = ps.executeQuery();
244 while(rs.next()) {
245 //通过数据库数据和Java对应的数据类型获取对应的只
246 int id = rs.getInt("id");
247 String name = rs.getString("name");
248 int age = rs.getInt("age");
249 //System.out.println(id+","+name+","+age);
250
251 //将获取的数据封装成对应的Student对象
252 Student stu = new Student(id, name, age);
253 //将一个个Student对象添加到list集合中
254 stus.add(stu);
255 }
256
257 } catch (Exception e) {
258 // TODO: handle exception
259 }finally {
260 try {
261 if(rs !=null) {
262 rs.close();
263 }
264 } catch (SQLException e) {
265 e.printStackTrace();
266 }finally {
267 try {
268 if(ps !=null) {
269 ps.close();
270 }
271 } catch (SQLException e) {
272 e.printStackTrace();
273 }finally {
274 try {
275 if(conn !=null) {
276 conn.close();
277 }
278 } catch (SQLException e) {
279 e.printStackTrace();
280 }
281 }
282 }
283 }
284
285 return stus;
286 }
287
288 }
7.3.10. 快速生成单元测试类
一个dao层或者service编写代码以后,需要为每一个功能都进行单元测试,一个dao中的方法很多。我们快速为这个dao层的类生成单元测试类,(dao的每一个方法都自动生成一个测试方法)
|
|
|
|
7.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工具类中了,不利于维护
抽取
7.4.1. JdbcUtil工具类
1 package cn.sxt.jdbc.util;
2
3 import java.io.InputStream;
4 import java.sql.Connection;
5 import java.sql.DriverManager;
6 import java.sql.PreparedStatement;
7 import java.sql.ResultSet;
8 import java.sql.SQLException;
9 import java.util.Properties;
10
11
12 public class JdbcUtil {
13
14 // alt+shif+a 多行修改,修改以后还原 alt+shif+a
15
16 /*private static String driverClassName = "com.mysql.jdbc.Driver";
17 private static String url = "jdbc:mysql://localhost:3306/jdbcdemo";
18 private static String username = "root";
19 private static String password = "root";*/
20
21 private static Properties p = new Properties();
22
23 static {
24 try {
25 //1.获取类加载器
26 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
27 //2,使用类加载器获取项目 类路径下面的文件
28 InputStream inputStream = classLoader.getResourceAsStream("db.properties");
29
30 //3.使用Priperties加载配置文件对应的输入流
31 p.load(inputStream);
32
33 Class.forName(p.getProperty("driverClassName"));
34 } catch (Exception e) {
35 e.printStackTrace();
36 }
37 }
38
39 public static Connection getConnection() {
40 try {
41
42 return DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"), p.getProperty("password"));
43 } catch (Exception e) {
44 e.printStackTrace();
45 throw new RuntimeException("亲,连接数据库失败", e);
46 }
47 }
48
49 public static void close(Connection conn,PreparedStatement ps,ResultSet rs) {
50 try {
51 if(rs !=null) {
52 rs.close();
53 }
54 } catch (SQLException e) {
55 e.printStackTrace();
56 }finally {
57 try {
58 if(ps !=null) {
59 ps.close();
60 }
61 } catch (SQLException e) {
62 e.printStackTrace();
63 }finally {
64 try {
65 if(conn !=null) {
66 conn.close();
67 }
68 } catch (SQLException e) {
69 e.printStackTrace();
70 }
71 }
72 }
73 }
74 }
7.4.2. 使用工具类以后的DAO实现类效果
1 package cn.sxt.jdbc.dao.impl;
2
3 import java.sql.Connection;
4 import java.sql.PreparedStatement;
5 import java.sql.ResultSet;
6 import java.sql.SQLException;
7 import java.util.ArrayList;
8 import java.util.List;
9
10 import cn.sxt.jdbc.dao.StudentDao;
11 import cn.sxt.jdbc.domain.Student;
12 import cn.sxt.jdbc.util.JdbcUtil;
13
14 public class StudentDaoImpl implements StudentDao {
15
16
17
18 @Override
19 public int saveStudent(Student stu) {
20 String sql = "insert into t_student(name,age) values (?,?)";
21
22 Connection conn = null;
23 PreparedStatement ps = null;
24 try {
25 conn = JdbcUtil.getConnection();
26
27 // 3.创建预编译语句对象
28 ps = conn.prepareStatement(sql);
29 //3.1设置占位符参数
30 ps.setString(1, stu.getName());
31 ps.setInt(2, stu.getAge());
32
33 // 4.执行SQL语句:注意不要带SQL参数
34 return ps.executeUpdate();
35
36 } catch (Exception e) {
37 e.printStackTrace();
38 }finally {
39 JdbcUtil.close(conn, ps, null);
40 }
41 return 0;
42 }
43
44 @Override
45 public int deleteById(int id) {
46
47 String sql = "delete from t_student where id = ?";
48
49 Connection conn = null;
50 PreparedStatement ps = null;
51 try {
52
53 conn = JdbcUtil.getConnection();
54 // 3.创建预编译语句对象
55 ps = conn.prepareStatement(sql);
56 //3.1设置占位符参数
57 ps.setInt(1, id);
58
59 // 4.执行SQL语句:注意不要带SQL参数
60 return ps.executeUpdate();
61
62
63 } catch (Exception e) {
64 e.printStackTrace();
65 }finally {
66 JdbcUtil.close(conn, ps, null);
67 }
68 return 0;
69 }
70
71 @Override
72 public int updateStudentById(Student stu) {
73
74 String sql = "update t_student set name = ?,age = ? where id = ?";
75
76 Connection conn = null;
77 PreparedStatement ps = null;
78 try {
79
80 conn = JdbcUtil.getConnection();
81 // 3.创建预编译语句对象
82 ps = conn.prepareStatement(sql);
83 //3.1设置占位符参数
84 ps.setString(1, stu.getName());
85 ps.setInt(2, stu.getAge());
86 ps.setInt(3, stu.getId());
87 // 4.执行SQL语句:注意不要带SQL参数
88 return ps.executeUpdate();
89
90
91 } catch (Exception e) {
92 e.printStackTrace();
93 }finally {
94 JdbcUtil.close(conn, ps, null);
95 }
96 return 0;
97 }
98
99 @Override
100 public Student selectById(int id) {
101 String sql = "select * from t_student where id = ?";
102
103 Connection conn = null;
104 PreparedStatement ps = null;
105 ResultSet rs = null;
106
107 try {
108 conn = JdbcUtil.getConnection();
109 // 3.创建语句对象
110 ps = conn.prepareStatement(sql);
111 //3.1设置占位符参数对应的值
112 ps.setInt(1, id);
113
114 // 4.执行SQL语句
115 rs = ps.executeQuery();
116 if(rs.next()) {
117 //通过数据库数据和Java对应的数据类型获取对应的只
118 String name = rs.getString("name");
119 int age = rs.getInt("age");
120 //System.out.println(id+","+name+","+age);
121 //将获取的数据封装成对应的Student对象
122 Student stu = new Student(id, name, age);
123 return stu;
124 }
125
126 } catch (Exception e) {
127 // TODO: handle exception
128 }finally {
129 try {
130 if(rs !=null) {
131 rs.close();
132 }
133 } catch (SQLException e) {
134 e.printStackTrace();
135 }finally {
136 JdbcUtil.close(conn, ps, rs);
137 }
138 }
139
140 return null;
141 }
142
143 @Override
144 public List<Student> selectList() {
145 String sql = "select * from t_student";
146 //创建list集合用于封装Student对象
147 List<Student> stus = new ArrayList<>();
148
149 Connection conn = null;
150 PreparedStatement ps = null;
151 ResultSet rs = null;
152
153 try {
154
155 conn = JdbcUtil.getConnection();
156 // 3.创建语句对象
157 ps = conn.prepareStatement(sql);
158
159 // 4.执行SQL语句
160 rs = ps.executeQuery();
161 while(rs.next()) {
162 //通过数据库数据和Java对应的数据类型获取对应的只
163 int id = rs.getInt("id");
164 String name = rs.getString("name");
165 int age = rs.getInt("age");
166 //System.out.println(id+","+name+","+age);
167
168 //将获取的数据封装成对应的Student对象
169 Student stu = new Student(id, name, age);
170 //将一个个Student对象添加到list集合中
171 stus.add(stu);
172 }
173
174 } catch (Exception e) {
175 // TODO: handle exception
176 }finally {
177 JdbcUtil.close(conn, ps, rs);
178 }
179
180 return stus;
181 }
182
183 }
7.5. 知识点补充,类加载器
在项目的 类路径(src)下面创建一个 db.properties配置文件,专门配置连接数据库的账号密码
如何使用类加载器加载配置文件
7.5.1. 配置文件
|
7.5.1.1. 配置文件创建的位置
配置文件一般都放在项目的src 源目录下面
|
7.5.2. 加载代码
1 package cn.sxt.jdbc.test;
2
3 import static org.junit.Assert.*;
4
5 import java.io.InputStream;
6 import java.util.Properties;
7
8 import org.junit.Test;
9
10 public class PropertiesTest {
11
12 @Test
13 public void testName() throws Exception {
14
15 /*
16 * ClassLoader 类加载器
17 * ClassLoader :可以从项目的类路径下面读取对应的配置文件返回一个输入流
18 * ClassLoader 在程序运行的时候JVM已经为每一个项目都创建了一个,我们开发者只需要获取即可
19 * 获取类加载器方式
20 * 1、使用当前线程
21 * ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
22 * 2、通过某一类的字节码实例也可以获取
23 * ClassLoader classLoader = PropertiesTest.class.getClassLoader();
24 */
25 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
26 //使用类加载器获取项目 类路径下面的文件
27 InputStream inputStream = classLoader.getResourceAsStream("db.properties");
28
29
30 /*
31 * Properties 是Map集合下面的一个 专门用于读取配置文件的对象
32 * 可以读取当前类路径下面的 xxx.properites类型的配置文件
33 *
34 * xxx.properites的内容必须是key=value 键值对的数据
35 */
36
37 //1.创建Properties对象
38 Properties p = new Properties();
39
40 //2.加载配置文件
41 p.load(inputStream);
42
43 System.out.println(p);
44
45 //获取具体某一个key对应的值
46 String driverClassName = p.getProperty("driverClassName");
47 System.out.println(driverClassName);
48 }
49 }
7.5.3. 效果
8. 连接池
8.1. 遇到的问题-引出连接池
8.2. 连接池思想
8.3. 连接池的概述
在Java中,连接池使用javax.sql.DataSource接口来表示连接池.
注意:DataSource仅仅只是一个接口,由各大服务器厂商来实现(Tomcat.JBoss,阿里巴巴).
常用的DataSource的实现:
推荐的
推荐的
(德鲁伊)阿里巴巴开源的,性能最好,速度最快
DataSource(数据源)和连接池(Connection Pool)是同一个.
8.4. 使用连接池和不使用连接池的区别在哪里
|
8.5. Druid连接池的使用
8.5.1. 准备druid 连接池jar包到项目
从代码上:
不使用连接池: Conenction对象由DriverManager获取.
Connection conn = DriverManager.getConnection(url,username,password);
使用连接池:
DataSource对象,如何在DataSource中设置url,账号,密码.
对象.getConnection();
--------------------------------------------------------------------
使用连接池的时候:
: Connection对象.close():
Connection放回给连接池,而不是和数据库断开.
1 package cn.sxt.jdbc.test;
2
3 import static org.junit.Assert.*;
4
5 import java.io.InputStream;
6 import java.io.Reader;
7 import java.sql.Connection;
8 import java.util.Properties;
9
10 import javax.sql.DataSource;
11
12 import org.junit.Test;
13
14 import com.alibaba.druid.pool.DruidDataSource;
15 import com.alibaba.druid.pool.DruidDataSourceFactory;
16 import com.alibaba.druid.pool.DruidPooledConnection;
17
18 public class DataSourceTest {
19 // 直接创建连接池对象
20 @Test
21 public void testName() throws Exception {
22 // 1.创建连接池对象
23 DruidDataSource ds = new DruidDataSource();
24 // 2.设置连接数据库的账号密码
25 ds.setDriverClassName("com.mysql.jdbc.Driver");
26 ds.setUrl("jdbc:mysql://localhost:3306/jdbcdemo");
27 ds.setUsername("root");
28 ds.setPassword("root");
29 ds.setMaxActive(10);// 最大连接数
30 // 3.获取连接对象
31 Connection conn = ds.getConnection();
32 System.out.println(conn);
33 }
34
35 // 使用工厂对象创建连接池对象,工厂对象的好处,不需要直接设置账号密码等等,只需要将
36 // 连接数据库的账号密码等等以指定的 key的名称配置到 xxx.properties文件中即可,工厂对象底层自动读取
37 @Test
38 public void testDataSourceByFactory() throws Exception {
39
40 // 1.获取类加载器用于加载clsspath下面的 配置文件
41 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
42 // 2.读取druid.properties配置文件
43 InputStream inputStream = classLoader.getResourceAsStream("druid.properties");
44 // 3.创建Properties对象,并读取配置文件对应的输入流
45 Properties p = new Properties();
46 p.load(inputStream);
47
48 // 4.创建连接池对象
49 DataSource ds = DruidDataSourceFactory.createDataSource(p);
50 // 5.获取连接对象
51 Connection conn = ds.getConnection();
52 System.out.println(conn);
53 }
54 }
|
8.5.2. db.propperties
|
8.5.3. 使用Druid抽取的工具类
9. 事务
案例:银行转账:从张无忌账户上给赵敏转1000块.
准备:account(账户表):
---------------------------------------------------------------
id name(账号,唯一) balance(余额)
1 张无忌 20000
2 赵敏 0
---------------------------------------------------------------
转账的思路:
检查张无忌的账号余额是否大于等于1000.
张无忌' AND balance >=1000
>=1000:GOTO 2:
<1000:提示:亲,你的余额不足.
在张无忌的账号余额上减少1000.
张无忌'
在赵敏的账户余额尚增加1000.
赵敏'
-------------------------------------------------------------------------------------------
注意:在第二步和第三步之间,停电了.
:System.out.println(1/0);
9.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.若出现异常必须回滚事务:
,总余额依然是正确的. 若不回滚事务,不会释放数据库资源.
对象.rollback();
-----------------------------------------------------------------------------------
1.在JDBC在事务是默认提交的,那是在什么时候提交的.
在执行一个DML/DDL操作的时候,就已经提交事务了.
2.针对于CRUD操作. 只有DML操作才有事务,查询操作没有事务.
,我们一般会把查询也放在事务里面.
- 以后,凡是发现自己编写的代码是正确的,测试也通过,但是就是数据库表中的数据不变----->事务没提交的问题.
4.MySQL中,InnoDB支持外键.支持事务,MyISAM不支持外键,不支持事务.
InnoDB存储引擎: 支持事务,支持外键,但是查询效率略低,(金融,理财,p2p)
MyISAM存储引擎:不支持事务和外键,但是查询效率较高(新闻网站)
Oracle 不存在存储引擎,都有事务
9.2. 事务处理代码
1 public class TransactionTest {
2 @Test
3 public void testName() throws Exception {
4 Connection conn = null;
5 Statement st = null;
6 ResultSet rs = null;
7 try {
8 conn = DruidUtil.getConnection();
9 //将事务设置为手动提交
10 conn.setAutoCommit(false);
11
12 st = conn.createStatement();
13 // 1.检查张无忌的账号余额是否大于等于1000.
14 rs = st.executeQuery("SELECT balance FROM account WHERE name = '张无忌' AND balance >=1000");
15 if(!rs.next()) {
16 throw new RuntimeException("亲,您的账户余额不够");
17 }
18 // 余额>=1000:GOTO 2:
19 // 余额 <1000:提示:亲,你的余额不足.
20 // 2.在张无忌的账号余额上减少1000.
21 st.executeUpdate("UPDATE account SET balance = balance-1000 WHERE name = '张无忌'");
22
23 System.out.println(1/0);
24
25 // 3.在赵敏的账户余额尚增加1000.
26 st.executeUpdate("UPDATE account SET balance = balance+1000 WHERE name = '赵敏'");
27
28 //提交事务
29 conn.commit();
30
31
32 } catch (Exception e) {
33 e.printStackTrace();
34 //回滚事务
35 conn.rollback();
36
37 }finally {
38 DruidUtil.close(conn, st, rs);
39 }
40
41 }
42 }