初步理解数据库锁和事务的关系
2017-07-13 16:10
295 查看
写项目的时候遇到spring事务和数据库锁的问题,抽空整理一下:
先不去考虑共享锁还是排他锁,总之对update语句或者select ...for update都会加锁。当然这里select语句的where条件比如是id(主键)或者加索引的字段,那么会对查询的数据加锁,如果where里是非索引字段,则会全表加锁。
那么在JDBC中,我们的处理是这样的
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.utils.DruidPool;
public class test{
public result test() {
// 数据库连接对象
DruidPooledConnection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "select * from table for update";
try {
conn = DruidPool.getConnection();
conn.setAutoCommit(false);//关闭自动提交
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
conn.commit;
} catch (SQLException e) {
e.printStackTrace();
conn.rollback();
} finally {
DruidPool.allClose(con, pstmt, rs);
}
return result;
}
}
在我们提交之前,其他操作加锁数据的线程都会被阻塞(不会阻塞没加for update 的select语句),直到我们commit或者rollback。
这一步在spring事务里已经做了封装,只需要在调用的方法上加注解@Transactional,spring的AOP会在执行方法结束后的代理对象中执行commit,若是捕获到RuntimeException,则会ro
a0fd
llback。
基于注解的事务控制:
proxy-target-class="true" 表示代理目标类,而不是bean所实现的接口。
那么,我所理解的就是在加了@Transactional的方法中,会在AOP的代理对象中对数据进行提交或回滚,以释放锁资源。
@Override
@Transactional
public Order selectByPrimaryKeyForUpdate(String id) {
long startTime = System.currentTimeMillis();
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
String inparam = LogUtil.formatParam("id", id);
Order order = null;
try {
order = orderMapper.selectByPrimaryKeyForUpdate(id);
} catch (Exception e) {
logger.error(methodName, "数据库插入失败", e);
MonitorLogger.error(null, methodName, CommonResultCode.DB_QUERY_FAILED, startTime, inparam, "数据库插入失败");
throw new RuntimeException(e.getLocalizedMessage(), e);
}
logger.infoFunExit(methodName, inparam, JSON.toJSONString(order), "获取订单数据成功");
MonitorLogger.info(null, methodName, CommonResultCode.SUCCESS, startTime, inparam, "获取订单数据成功");
return order;
}
如这个service中的方法,在这个方法执行结束之前,其他线程对这个主键id的数据操作都会被阻塞。
注意的是,如果外部直接调用的方法,比如A类的a()方法中调用B类的a()方法,而B类的a()方法又调用B类的b()方法,此时在B类的b()方法上加@Transactional是无效的:
public class A {
public void a(){
new B().a();
}
}
正确的方式是加再a()方法上:
public class B {
@Transactional
public void a(){
b();
}
public void b(){}
}
补充:
极少遇见这种情况:A类的a()方法调用B类的b()方法,b()方法加入了@Transactional,同时B类的b()方法调用了C类的c()方法,此时c()会沿用b()的事务,若c()需要建立自己的事务,则需要在事务的property里配置
具体请搜索:事务的传播机制。
总结:事务一般使用的情况就是在某方法中有多条对DB的操作,或者在高并发的查询时(select ...for update)的时候。
最后,第一次写博客,理解上还存在很多不足,如有错误,还望指教。
先不去考虑共享锁还是排他锁,总之对update语句或者select ...for update都会加锁。当然这里select语句的where条件比如是id(主键)或者加索引的字段,那么会对查询的数据加锁,如果where里是非索引字段,则会全表加锁。
那么在JDBC中,我们的处理是这样的
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.utils.DruidPool;
public class test{
public result test() {
// 数据库连接对象
DruidPooledConnection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "select * from table for update";
try {
conn = DruidPool.getConnection();
conn.setAutoCommit(false);//关闭自动提交
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
conn.commit;
} catch (SQLException e) {
e.printStackTrace();
conn.rollback();
} finally {
DruidPool.allClose(con, pstmt, rs);
}
return result;
}
}
在我们提交之前,其他操作加锁数据的线程都会被阻塞(不会阻塞没加for update 的select语句),直到我们commit或者rollback。
这一步在spring事务里已经做了封装,只需要在调用的方法上加注解@Transactional,spring的AOP会在执行方法结束后的代理对象中执行commit,若是捕获到RuntimeException,则会ro
a0fd
llback。
基于注解的事务控制:
<!--事物控制--> <tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
proxy-target-class="true" 表示代理目标类,而不是bean所实现的接口。
那么,我所理解的就是在加了@Transactional的方法中,会在AOP的代理对象中对数据进行提交或回滚,以释放锁资源。
@Override
@Transactional
public Order selectByPrimaryKeyForUpdate(String id) {
long startTime = System.currentTimeMillis();
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
String inparam = LogUtil.formatParam("id", id);
Order order = null;
try {
order = orderMapper.selectByPrimaryKeyForUpdate(id);
} catch (Exception e) {
logger.error(methodName, "数据库插入失败", e);
MonitorLogger.error(null, methodName, CommonResultCode.DB_QUERY_FAILED, startTime, inparam, "数据库插入失败");
throw new RuntimeException(e.getLocalizedMessage(), e);
}
logger.infoFunExit(methodName, inparam, JSON.toJSONString(order), "获取订单数据成功");
MonitorLogger.info(null, methodName, CommonResultCode.SUCCESS, startTime, inparam, "获取订单数据成功");
return order;
}
如这个service中的方法,在这个方法执行结束之前,其他线程对这个主键id的数据操作都会被阻塞。
注意的是,如果外部直接调用的方法,比如A类的a()方法中调用B类的a()方法,而B类的a()方法又调用B类的b()方法,此时在B类的b()方法上加@Transactional是无效的:
public class A {
public void a(){
new B().a();
}
}
public class B { public void a(){ b(); } @Transactional public void b(){} }
正确的方式是加再a()方法上:
public class B {
@Transactional
public void a(){
b();
}
public void b(){}
}
补充:
极少遇见这种情况:A类的a()方法调用B类的b()方法,b()方法加入了@Transactional,同时B类的b()方法调用了C类的c()方法,此时c()会沿用b()的事务,若c()需要建立自己的事务,则需要在事务的property里配置
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
总结:事务一般使用的情况就是在某方法中有多条对DB的操作,或者在高并发的查询时(select ...for update)的时候。
最后,第一次写博客,理解上还存在很多不足,如有错误,还望指教。
相关文章推荐
- 对数据库事务、隔离级别、锁、封锁协议的理解及其关系的理解
- 看图理解Oracle实例与用户、数据库关系
- 事务与数据库连接的关系不是一对一的
- 关于数据库事务、隔离级别、锁的理解与整理
- 数据库事务隔离级别理解
- 数据库事务的理解
- 多角度彻底理解数据库事务中的"脏读"."不可重复的读"及"虚读"
- 关系数据库的事务隔离、锁定与并发控制
- EF中的事务处理的初步理解
- 理解数据库事务隔离级别以及脏读, 不可重复读, 幻读
- 多角度彻底理解数据库事务中的脏读,虚读和不可重复读
- 多角度彻底理解数据库事务中的"脏读"."不可重复的读"及"虚读"
- 数据库事务隔离性和锁,加深理解
- 理解数据库事务隔离级别以及脏读, 不可重复读, 幻读
- EF中的事务处理的初步理解
- 数据库并发访问、事务与锁的关系
- 多角度彻底理解数据库事务中的"脏读"."不可重复的读"及"虚读"(转载)
- 一个Spring事务中,前后两次数据库操作的影响关系
- Android内核理解——初步理解context和activity的关系
- 如何理解关系数据库