JDBC事务管理&数据库层的封装

JDBC事务管理&数据库层的封装

JDBC事务管理

什么是事务

一个事件的完成需要几个子操作的联合完成,只要有一个子操作执行失败,则数据回滚到原始状态,都成功则提交数据。

数据库的四大特性ACID,原子性、一致性、隔离性、持久性,每个特性都有其特定的职责。

原子性:一个事务中的所有操作,要不操作全部成功,要不全部失败,不能存在中间态。

一致性:事务必须使得数据库从一个一致性状态转变到另一个一致性状态。比如银行转账,A账户转到B账户,不管转几次,A和B账户的总额不能变。

隔离性:是指多个用户同时请求数据库,开启多个事务同时处理某个数据库,隔离性保证了各个事务之间均不受干扰,每个事务都感觉不到其他事务的存在。

持久性:对数据库的修改是持久性的,一旦修改,就算数据库系统出现故障,这种修改也不会丢失。

JDBC实现事务

只要涉及数据库的增删改操作,一定要使用事务,养成习惯,哪怕是一条sql语句,也要使用。

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

/**
* JDBC的事务管理:
* 转账业务
* 示例:
* 张三给李四转账1000元
* 注意:
* JDBC中的事务是自动提交的。
* 问题:
* 如果在业务的处理过程中,某条sql语句执行失败了,但是数据已经更改了
* 就会造成数据的不一致,会产生很大问题,比如张三给李四转账,张三的钱扣了,
* 李四收钱那条sql语句执行失败,李四没收到钱,这就出现了问题!
* 解决:
* 设置JDBC的事务为手动提交,只有全部sql语句(张三转钱,李四收钱)执行成功才能提交,否则回滚。
* 方式:设置JDBC的事务为手动提交,在sql执行处加一个try-catch异常处理语句,只要有异常,
* 即有的sql语句无法执行,则在catch异常处理语句中回滚rollback(),全部执行好之后就提交commit()。
* 说明:
* 只有增加、删除、修改才会涉及到事务,查询不涉及,至于原因,大家应该理解
* @author x1aolin
*
*/
public class TestTran {
public static void main(String[] args) {
//声明JDBC变量
Connection conn = null;
Statement stmt = null;
//声明JDBC参数
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:XE";
String username = "hh";
String password = "123456";
try {
//加载驱动
Class.forName(driver);
//设置数据库连接对象
conn = DriverManager.getConnection(url, username, password);
//取消事务自动提交,改为手动提交
conn.setAutoCommit(false);
//设置SQl命令对象
stmt = conn.createStatement();
//设置sql语句
String sql1 = "update ss set money = money - 1000 where name = '张三'";
String sql2 = "update ss set money = money + 1000 where name = '李四'";
//事务的实现方式 加一个try-catch
try{
//执行sql语句
int i1 = stmt.executeUpdate(sql1);
int i2 = stmt.executeUpdate(sql2);
System.out.println(i1+"--"+i2);
//未出现异常,数据提交
conn.commit();
}catch(Exception e){
//出现异常,数据回滚
conn.rollback();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally{
//关闭资源
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

JDBC数据库的封装

引子

比如说我们来写一个代码,来更新昵称,大家可以粗略看一下。

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;

/**
* 问题:
* 不同的用户数据,使用相同的数据库操作
* 解决:
* 将数据操作代码单独进行封装
* 优点:
* 避免代码的冗余,便于维护,但并不是提升了代码的提升效率!
* @author x1aolin
*
*/
public class UpdateName {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Connection conn = null;
Statement stmt = null;

String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:XE";
String username = "hh";
String password = "123456";

System.out.print("请输入学号:");
String tmp = sc.nextLine();
int id = Integer.parseInt(tmp);
System.out.print("请输入要修改的名称:");
String name = sc.nextLine();
try {
//加载驱动
Class.forName(driver);
//创建数据库连接对象
conn = DriverManager.getConnection(url, username, password);
//设置事务为手动提交 !!!
conn.setAutoCommit(false);
//创建sql命令对象
stmt = conn.createStatement();
//创建sql命令
String sql = "update ss set name = '"+name+"' where id = " + id;
//执行sql命令
int i = stmt.executeUpdate(sql);
conn.commit();
if(i>0) {
System.out.println("昵称更新成功");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

从上面的代码可以看出,如果我们需要一个操作,就必须重写一遍与数据库之间的连接,这将会非常麻烦,也不利于后期的维护。因此,我们需要将各种功能封装起来,当我们遇到类似的需求时,直接调用封装的方法即可。

封装方法

工程中包的命名规则

(一)dao包:用于存放完成数据库操作的功能接口。

(二)daoImpl包: 用于实现dao包中对应的接口。

(三)pojo包:和表结构相同的实体类,对哪个表进行处理,就创建哪个类。

工程对应包中类的命名规则

(一)与dao包对应的接口命名规范:对哪个表进行操作,就将对应接口命名为表名Dao

(二)与daoImpl对应的类名实现规范:对哪个表进行操作,就将对应实现类命名为表名DaoImpl

(三)与pojo包对应的类名命名规范:里面放对应表的实体类。

然后,我们将引子中的代码按照上述规范放到对应的位置来实现相同的功能,可读性更高,更利于维护。

dao包

1
2
3
4
5
package com.x1aolin.dao;

public interface SsDao {
public int UpdateName(String newName,int id);
}

daoImpl包

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
50
51
52
53
54
55
56
package com.x1aolin.daoImpl;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import com.x1aolin.dao.SsDao;

public class SsDaoImpl implements SsDao{
public int UpdateName(String newName,int id){
Connection conn = null;
Statement stmt = null;

String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:XE";
String username = "jiahui";
String password = "peijiahui";
int i = -1;
try {
//加载驱动
Class.forName(driver);
//创建数据库连接对象
conn = DriverManager.getConnection(url, username, password);
//设置事务为手动提交
conn.setAutoCommit(false);
//创建sql命令对象
stmt = conn.createStatement();
//创建sql命令
String sql = "update ss set name = '"+newName+"' where id = " + id;
//执行sql命令
i = stmt.executeUpdate(sql);
conn.commit();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return i;
}
}

practice包:(当前包并不是必须的,只是用作练习)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.x1aolin.practice;

import java.util.Scanner;
import com.x1aolin.daoImpl.SsDaoImpl;

public class UpdateName {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入学号:");
String tmp = sc.nextLine();
int id = Integer.parseInt(tmp);
System.out.println("请输入要修改的名称:");
String sname = sc.nextLine();
//调用Dao层对数据库进行操作
SsDaoImpl ss = new SsDaoImpl();
int i = ss.UpdateName(sname, id);
if(i>0){
System.out.println("昵称修改成功!");
}
}
}

从现在开始,以后我们写相关代码,就按照上面的规范来进行代码的书写,包括使用封装、事务等来完成对表的增删改工作。对于增加、删除和修改除了sql语句不同以外,其他声明基本一致,因此,我们可以将其封装成一个方法。

封装的思想:相同的保留,不同的传参。

项目开发步骤

第一步:业务需求分析

第二步:数据库设计

第三步:sql语句设计

第四步:数据库操作功能类实现

第五步:业务逻辑代码实现

第六步:联合测试

第七步:上机测试

第八步:维护

对开发需求的直接映射是SQL语句,这也是最重要的。

您的每一份支持将鼓励我继续创作!
-------------本文结束感谢您的阅读-------------