事务的特性(ACID)
- 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 - 一致性(Consistency)
事务前后数据的完整性必须保持一致。 - 隔离性(Isolation)
事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。 - 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
- 原子性(Atomicity)
如果不考虑事物的隔离性会出现以下问题
如果不考虑事务的隔离性,会出现什么问题?
脏读
一个事务读取到另一个事务的未提交数据。
不可重复读:两次读取的数据不一致(侧重于update)
指的是读取数据的客户端开启事务后两次读取的数据不一致。(对方数据提交前后两次不一致)
虚读(幻读) (侧重于insert)
两次读取的数据不一致
丢失更新
两个事务对同一条记录进行操作,后提交的事务,将先提交的事务的修改覆盖了。
未避免以上问题,所以数据库系统有以下四种隔离级别
Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)
最高隔离级别,一般不采用,因为在某个用于开启事务后,其他数据库无法访问该数据,会一直等待。导致效率特别低。
Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读
mysql的默认级别
Read committed:可避免脏读情况发生(读已提交)
Read uncommitted:最低级别,以上情况均无法保证。(读未提交)
如何解决丢失更新的问题
解决丢失更新可以采用两种方式:
1.悲观锁
悲观锁 (假设丢失更新一定会发生 ) —– 利用数据库内部锁机制,管理事务
提供的锁机制
1.共享锁
select * from table lock in share mode(读锁、共享锁)
2.排它锁
select * from table for update (写锁、排它锁)/*
mysql数据库内部提供两种常用 锁机制:共享锁(读锁)和排它锁(写锁)
允许一张数据表中数据记录,添加多个共享锁,添加共享锁记录,对于其他事务可读不可写的
一张数据表中数据记录,只能添加一个排它锁,在添加排它锁的数据 不能再添加其他共享锁和排它锁的 ,对于其他事物可读不可写的
*/
update语句默认添加排它锁2.乐观锁
乐观锁 (假设丢失更新不会发生)——- 采用程序中添加版本字段解决丢失更新问题create table product (
id int,
name varchar(20),
updatetime timestamp
);
insert into product values(1,'冰箱',null);
update product set name='洗衣机' where id = 1;
解决丢失更新:在数据表添加版本字段,每次修改过记录后,版本字段都会更新,如果读取是版本字段,
与修改时版本字段不一致,说明别人进行修改过数据 (重改)
在数据库中查看以及设置事务的隔离级别
查看事务隔离级别
select @@tx_isolation 查询当前事务隔离级别
/*
mysql中默认的事务隔离级别是 Repeatable read.
oracle 中默认的事务隔离级别是 Read committed
*/
mysql中怎样设置事务隔离级别
set session transaction isolation level read committed; /*设置当前会话事务隔离级别*/
set global transaction isolation level read committed; /*全局的*/
jdbc设置事物隔离
//使用java.sql.Connection接口中提供的方法
void setTransactionIsolation(int level) throws SQLException
/*
参数level可以取以下值:
level - 以下 Connection 常量之一:
Connection.TRANSACTION_READ_UNCOMMITTED、
Connection.TRANSACTION_READ_COMMITTED、
Connection.TRANSACTION_REPEATABLE_READ
Connection.TRANSACTION_SERIALIZABLE。
(注意,不能使用 Connection.TRANSACTION_NONE,因为它指定了不受支持的事务。)
*/
在service中进行事物控制
在实际开发中,如果service中会进行多个数据库操作,我们应该使service的执行过程为一个完整的事务,所以我们需要在service中加入事务控制。所以在Dao层需要对connection进行操作。使用ThreaddLocal可以使我们在同一线程中不经过传参也能获取到Connectio。(其工具类如下:)
public class JdbcUtil {
private static final String DRIVERCLASS;
private static final String URL;
private static final String USERNAME;
private static final String PASSWORD;
private static final ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
static {
DRIVERCLASS = ResourceBundle.getBundle("jdbc").getString("driverClass");
URL = ResourceBundle.getBundle("jdbc").getString("url");
USERNAME = ResourceBundle.getBundle("jdbc").getString("username");
PASSWORD = ResourceBundle.getBundle("jdbc").getString("password");
}
static {
try {
// 将加载驱动操作,放置在静态代码块中.这样就保证了只加载一次.
Class.forName(DRIVERCLASS);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
Connection con = tl.get();// 从ThreadLocal中获取Connection。第一次获取得到的是null.
if (con == null) {
// 2.获取连接
con = DriverManager.getConnection(URL, USERNAME, PASSWORD);
tl.set(con); // 将con装入到ThreadLocal中。
}
// tl.remove(); //解除
return con;
}
// 关闭操作
public static void closeConnection(Connection con) throws SQLException {
if (con != null) {
con.close();
}
}
public static void closeStatement(Statement st) throws SQLException {
if (st != null) {
st.close();
}
}
public static void closeResultSet(ResultSet rs) throws SQLException {
if (rs != null) {
rs.close();
}
}
}
在service中(主要是要最开始要开启事务,结束了要结交,失败了需要回滚)
public class AccountService {
public void account(String get, String send, double money) throws AccountException{
AccountDaoImpl dao = new AccountDaoImpl();
Connection con = null;
try {
con = JdbcUtils.getConnection();
// 开启事务
con.setAutoCommit(false);
dao.accountOut( accountOut, money);
dao.accountIn( accountIn, money);
} catch (Exception e) {
e.printStackTrace();
// 出现问题,进行事务回滚
if (con != null) {
try {
con.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
throw new AccountException(e.getMessage());
} finally {
// 事务提交
// 关闭con
if (con != null) {
try {
con.commit();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
转载于:https://www.cnblogs.com/foerver-zs/p/a3c2d80547e63809d3a3da638827214c.html