set autocommit = 0
关闭当前会话中事务的自动提交,需要手动 commit 或者 rollback,相当于开启一个全局的事务。在 mysql 的事务中,默认autocommit = 1
,每一次 sql 操作都被认为是一个单次的事务,被隐式提交。而oracle默认是
autocommit=0
start transaction
挂起 autocommit 的状态,开启一个事务上下文。首先数据库会隐式提交之前的还未被提交的操作,同时开启一个新事务。挂起autocommit 的意思是保存 autocommit 的当前状态,然后 start transaction,直到 commit or
rollback 结束本次事务,再恢复之前挂起的 autocommit 的状态。
如果 start transaction 前 autocommit = 1
,则完成本次事务后 autocommit 还是 1
如果 start transaction 前 autocommit = 0
,则完成本次事务后 autocommit 还是0,接下来的操作你仍需手动
commit 才可以提交。
SqlSession sqlSession = null; try { InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); // 不开启自动提交事务 sqlSession = sqlSessionFactory.openSession(false); List list = sqlSession.selectList("com.shuang.test.findAllUsers"); if (list.size() > 0) { sqlSession.update("xxx"); } } catch (Exception e) { e.printStackTrace(); } finally { // 强制提交事务,如果不设置的话,只有在insert或update才会提交事务,如果selectForUpdate结果为空,无法进行update操作是无法提交事务的 sqlSession.commit(true); sqlSession.close(); }
上面的代码是在oracle数据库中进行的,数据库连接池使用druid,代码看起来无任何问题。而实际是当查询为空时,不会执行下面的update语句,而事务还是没有提交,导致相应行被锁住了。也就是sqlSessionFactory.openSession(false)中的设置的autoCommit不起作用(注意程序中的autoCommit的值与mysql中的autocommit没有任何关系,它只是一个属性,用来辅助代码最后的是否进行commit,它不是设置mysql的autocommit的值)
解决办法
- 用sqlSession.getConnection().setAutoCommit(false);来设置autoCommit属性为false
- 提交时用直接调用connection的commit方法:sqlSession.getConnection().commit();
- 使用spring来开启事务
需要注意的是,上面的例子,在oracle数据库会阻塞,但mysql不会。原因就是mysql数据库是autocommit=1,会隐式commit,所以不会阻塞,而oracle是autocommit=0,但commit没有执行就阻塞了。