自定义JDBCUtil工具

前面Jdbc的基本使用使用已经讲得差不多了,但如果我们经常要创建连接,关闭连接的话,那么每次都要写重复的代码,显然这样非常的麻烦,为了避免这种情况,编写了一个JdbcUtil工具类,如下。

1.db.properties文件

用于放置驱动名称,连接数据库地址,数据库账号和密码。(db.properties要放在src或者默认会加载的目录下,不要放在包下面了)

1
2
3
4
5
#MySQL连接配置
mysqlDriver=com.mysql.jdbc.Driver
mysqlURL=jdbc:mysql://localhost:3306/jdbcstudy?characterEncoding=utf8&useSSL=false
mysqlUser=root
mysqlPwd=123456

点击并拖拽以移动

2.创建JdbcUtil工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import javax.management.RuntimeMBeanException;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

public class JdbcUtil {
private static String url;
private static String user;
private static String password;
private static String driver;
static {
try {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\DateBase_select\\db.properties"));
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
driver = properties.getProperty("driver");
} catch (IOException e) {
throw new RuntimeException();
}
}

public static Connection getConnection(){
try {
return DriverManager.getConnection(url,user,password);
} catch (SQLException throwables) {
throw new RuntimeException();
}
}
public static void close(ResultSet resultSet,Statement statement, Connection connection){
try {
resultSet.close();
statement.close();
connection.close();
} catch (SQLException throwables) {
throw new RuntimeException();
}
}
public static void close(Statement statement, Connection connection){
try {
statement.close();
connection.close();
} catch (SQLException throwables) {
throw new RuntimeException();
}
}
}

点击并拖拽以移动

3.JdbcUtil的简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
//调用JdbcUtil来创建连接
con = JdbcUtil.getConnection();
String sql = "select * from t_user";
try {
ps = con.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) {
System.out.print("编号:" + rs.getInt(1) + ";");
System.out.print("姓名:" + rs.getString(2) + ";");
System.out.print("密码:" + rs.getString(3) + ";");
System.out.println();
}

} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(con, ps, rs);
}
}

点击并拖拽以移动

小伙伴会发现一个地方,工具类close()的参数中有一个Statement,这里用Statement而不用PreparedStatement是因为:StatementPreparedStatement父接口Statement可以兼容这两种不同的对象。

JDBC的事务

事务的基本概念

什么是事务呢?

事务是若干个SQL语句构成的一个操作序列,这些操作表示一个完整的功能,并且需要保证功能的完整性,因此要求在该事务中要求所有的sql要么都执行,要么都不执行,是一个不可分割的整体单位。(简单来说就是由多个小任务的组成的任务)

事务的四个基本要素(ACID)

  • 原子性(Atomicity):事务是一个不可分割的整体,所有操作要么全做,要么全不做;只要事务中有一个操作出错,回滚到事务开始前的状态的话,那么之前已经执行的所有操作都是无效的,都应该回滚到开始前的状态。
  • 一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
  • 隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱结束前,B不能向这张卡转账。
  • 持久性(Durability):事务一旦被提交后,事务对数据库的所有更新将被永远保存到数据库,不能回滚。

事务并发产生的问题

  • 丢失修改:当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。

5e759cd12aed423587a17258e107cfb4.png点击并拖拽以移动编辑

T1和T2同时修改一条数据,T2的修改覆盖了T1的修改; 如果在T1之后第T2才能进行更改,则可以避免该问

  • 脏读:事务A读取了事务B更新并且未提交的数据,然后B回滚操作,那么A读取到的数据是脏数据

d8e6fb0c2d6a4a2bbaeb3a8935d62191.png点击并拖拽以移动编辑

一个编辑人员T1正在更改电子文档。在更改过程中,另一个编辑人员T2复制了该文档(该复本包含到目前为止所做的全部更改)并将其分发给预期的用户。此后,第一个编辑人员T1认为所做的更改是错误的,于是删除了所做的编辑并保存了文档。分发给用户的文档包含不再存在的编辑内容,并且这些编辑内容应认为从未存在过。如果在第一个编辑人员确定最终更改前任何人都不能读取更改的文档,则可以避免该问题。

  • 不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。

a9f4710156854dff97527e4c014aaff3.png点击并拖拽以移动编辑

一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变!这种现象就叫做“不可重复读”。指事务T2读取数据后,事务T1执行更新操作,使T1和T2无法读取前一次结果。

  • 幻读:事务A从一个表中读取了一个字段,然后B在该表中插入/删除了一些新的行。 之后, 如果 A 再次读取同一个表, 就会多/少几行,就好像发生了幻觉一样,这就叫幻读。

c361723c5e5f45faba5f5d7fd2a10629.png点击并拖拽以移动编辑

事务t2读取到了事务t1体提交的新增、删除数据,不符合隔离性。幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)

事务的隔离级别

  • 读未提交(read-uncommitted):允许A事务读取其他事务未提交和已提交的数据
  • 不可重复读(read-committed):只允许A事务读取其他事务已提交的数据
  • 可重复读(repeatable-read):确保事务可以多次从一个字段中读取相同的值。在这个事务持续期间,禁止其他事务对这个字段进行更新;注意:mysql中使用了MVCC多版本控制技术,在这个级别也可以避免幻读。
  • 串行化(serializable):确保事务可以从一个表中读取相同的行,相同的记录。在这个事务持续期间,禁止其他事务对该表执行插入、更新、删除操作(效率非常低,基本不用)
事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted)
不可重复读(read-committed)
可重复读(repeatable-read)
串行化(serializable)

事务中需要使用的API方法

  1. connection.setAutoCommit(false); // 取消事务的自动提交
  2. connection.rollback(); // 事务回滚
    参数为空的时候,回滚到事务的开始。如果有参数的时候,返回到回滚点。
  3. connection.commit(); // 提交结果给数据库

事务使用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Transaction {
public static void main(String[] args) {
Connection connection = null;
String sql1 = "update `actor` set salary = salary + 100 where `name` = '小王'";
String sql2 = "update `actor` set salary = salary - 100 where `name` = '小李'";
PreparedStatement preparedStatement = null;
try {
connection = JdbcUtil.getConnection();
connection.setAutoCommit(false);
preparedStatement = connection.prepareStatement(sql1);
preparedStatement.executeUpdate();
int i = 1/0;
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();
connection.commit();
} catch (SQLException throwables) {
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
throwables.printStackTrace();
}finally {
JdbcUtil.close(preparedStatement,connection);
}

}
}

点击并拖拽以移动

注意:mysql中默认情况下,一个sql独占一个事务,且自动提交,从以上jdbc学习就可以看得出来

问题

​ idea连接数据库问题:

​ 1.配置时lib未导入连接数据库的架包

​ 2.驱动版本与数据库版本对不上