欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Mybatis 事务死锁解决全过程

程序员文章站 2022-06-16 16:52:59
...

使用的事务管理是

 

org.springframework.jdbc.datasource.DataSourceTransactionManager

 一、刚开始事务是这样实现的

 

 

TransactionStatus ts = transactionManager.getTransaction(null);
		try {

			// 得到用户信息
			User user = userMapper.selectByPrimaryKey(userId);
			
			user.setHealthBeans(user.getHealthBeans() == null ? 0 : user.getHealthBeans());

			Article article = articleMapper.selectByPrimaryKey(articleId);
			
			User createrUser = userMapper.selectByPrimaryKey(article.getCreater());
			HealthBeansRecord record = new HealthBeansRecord();
			
			healthBeansRecordMapper.insertSelective(record);
			

			
			record.setNumber((byte) +exceptionalCount);
			
			healthBeansRecordMapper.insertSelective(record);

			userMapper.modifyHealthBeans(userId, -exceptionalCount);

			userMapper.modifyHealthBeans(createrUser.getId(), exceptionalCount);
			
			transactionManager.commit(ts);
		} catch (Exception e) {
			transactionManager.rollback(ts);
			throw new BasicException(String.format("出现异常,异常原因:%s,参数userId:%s,参数articleId:%s", e.getMessage(),
					userId, articleId), e);
		}

 但是会时不时的出现:

 

 

### Error updating database.  Cause: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
### The error may involve defaultParameterMap
### The error occurred while setting parameters

 二、改成如下方式

 

 

		try {
                        ts = transactionManager.getTransaction(null);
			// 得到用户信息
			User user = userMapper.selectByPrimaryKey(userId);
			
			user.setHealthBeans(user.getHealthBeans() == null ? 0 : user.getHealthBeans());

			Article article = articleMapper.selectByPrimaryKey(articleId);
			
			User createrUser = userMapper.selectByPrimaryKey(article.getCreater());
			HealthBeansRecord record = new HealthBeansRecord();
			
			healthBeansRecordMapper.insertSelective(record);
			

			record.setUserId(createrUser.getId());
			record.setNumber((byte) +exceptionalCount);
			
			healthBeansRecordMapper.insertSelective(record);

			userMapper.modifyHealthBeans(userId, -exceptionalCount);

			userMapper.modifyHealthBeans(createrUser.getId(), exceptionalCount);
			
			transactionManager.commit(ts);
		} catch (Exception e) {
			transactionManager.rollback(ts);
			throw new BasicException(String.format("出现异常,异常原因:%s,参数userId:%s,参数articleId:%s", e.getMessage(),
					userId, articleId), e);
		}

 将TransactionStatus的声明为类的成员变量,在try catch中完成事务的使用。这样锁超时的错误少了很多,但是还会出现超时的情况 

 

三、给使用事务的代码添加类级异步锁

try {
	synchronized (getClass()) {
		ts = transactionManager.getTransaction(null);
		// 得到用户信息
		User user = userMapper.selectByPrimaryKey(userId);
		
		user.setHealthBeans(user.getHealthBeans() == null ? 0 : user.getHealthBeans());

		Article article = articleMapper.selectByPrimaryKey(articleId);
		
		User createrUser = userMapper.selectByPrimaryKey(article.getCreater());
		HealthBeansRecord record = new HealthBeansRecord();
		
		healthBeansRecordMapper.insertSelective(record);
		

		record.setUserId(createrUser.getId());
		record.setNumber((byte) +exceptionalCount);
		
		healthBeansRecordMapper.insertSelective(record);

		userMapper.modifyHealthBeans(userId, -exceptionalCount);

		userMapper.modifyHealthBeans(createrUser.getId(), exceptionalCount);
		
		transactionManager.commit(ts);
	}
} catch (Exception e) {
	transactionManager.rollback(ts);
	throw new BasicException(String.format("出现异常,异常原因:%s,参数userId:%s,参数articleId:%s", e.getMessage(),
			userId, articleId), e);
}

 这样一来,经过测试,不再出现锁超时的情况了。

但是总感觉,这样的异步控制是不应该在业务代码中进行控制的!