测试user表结构
测试代码(使用mybatis-plus)
import com.ddjava.ddjavademo.dao.UserDao;
import com.ddjava.ddjavademo.entity.User;
import com.ddjava.ddjavademo.utils.lock.RedissLockUtil;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.UUID;
/**
* @Description
* @Author Wangyw
* @Date 2021/8/31 1:31 下午
**/
@RestController
@RequestMapping(value = "/test")
public class TestController {
@Autowired
private UserDao userDao;
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
/**
* 测试事物隔离级别+分布式锁
* 查询user是否存在记录,如果不存在保存一条到数据库,保证数据库只有一条记录
* 1.查询user是否存在,如果不存在, 抢锁
* 2.抢到锁再次查询是否库里user是否存在,如果不存再进行创建,
* 3.提交事物
* 4.释放锁
*/
@RequestMapping(value = "/sw")
public void sw(){
String lockKey = "aaa";
RLock lock = null;
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
User user = null;
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(def);
try {
List<User> list = userDao.selectList(null);
if(list != null && list.size() > 0){
user = list.get(0);
}
//TimeUnit.SECONDS.sleep(2);
if(user == null){
lock = RedissLockUtil.lock(lockKey);
System.out.println( "线程 " + Thread.currentThread().getId() + " 拿到锁 " + System.currentTimeMillis());
list = userDao.selectList(null);
if(list != null && list.size() > 0){
user = list.get(0);
}
if(user == null){
user = new User();
user.setAge(10);
user.setName(UUID.randomUUID().toString());
user.setAddress(UUID.randomUUID().toString());
userDao.insert(user);
System.out.println(Thread.currentThread().getName() + "数据库保存user");
}else{
System.out.println(Thread.currentThread().getName() + "锁后 partyPlatformUserTrainDataError != null");
}
}else{
System.out.println( "线程 " + Thread.currentThread().getName() + " 查询 user不为空,不需要加锁");
}
dataSourceTransactionManager.commit(transactionStatus);
}catch (Exception e){
e.printStackTrace();
dataSourceTransactionManager.rollback(transactionStatus);
}finally {
if(lock != null){
try {
lock.unlock();
System.out.println( "线程 " + Thread.currentThread().getId() + " 释放锁 " + System.currentTimeMillis());
}catch (Exception e){
System.out.println("释放锁失败");
e.printStackTrace();
}
}
}
}
}
测试结果
发现并不能保证数据库中直插入一条,开启sql日志打印发现在57行代码list = userDao.selectList(null);这行代码执行的sql查询并没有打印,因为mybatis-plus只是对mybatis的增加,mybatis默认是开启了一级缓存,所以虽然spring手动设置了mysql的事物隔离级别,但是56行拿到锁之后执行到57行并没有去查询数据库,而是查询的缓存。
解决方案
关闭一级缓存,
mybatis-plus:
configuration:
local-cache-scope: statement