一、前言
今天测试一批日志数据插入数据库,发现通过BaseMapper的int insert(T entity);方法在大量数据进行插入的时候插入的数据变成了未提交。意思就是程序运行insert成功,但是数据库里却没有数据。当一条一条数据插入的时候却是可以的,循环快速插入却不生效。不知道为何。
二、BaseMapper insert方法
public interface BaseMapper<T> extends Mapper<T> {
/**
* 插入一条记录
*
* @param entity 实体对象
*/
int insert(T entity);
}
调用insert方法
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class BlockchainLogService {
@Autowired
private BlockchainLogMapper blockchainLogMapper;
public void blockchainLog(BlockchainLog blockchainLog) {
try {
blockchainLogMapper.insert(blockchainLog);
} catch (Exception e) {
log.info("保存日志异常:"+e.getMessage());
}
}
}
这种方式发现当循环插入日志时,事务并没有提交。
我的解决办法:手动添加commit语句
@Repository
public interface BlockchainLogMapper extends BaseMapper<BlockchainLog>{
/**
* 查询商户
*/
@Select("<script>"
+ "commit "
+ "</script>")
public void commit();
}
在插入日志后,调用commit()方法,通过这个方式就解决了。
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class BlockchainLogService {
@Autowired
private BlockchainLogMapper blockchainLogMapper;
public void blockchainLog(BlockchainLog blockchainLog) {
try {
blockchainLogMapper.insert(blockchainLog);
blockchainLogMapper.commit();
} catch (Exception e) {
log.info("保存日志异常:"+e.getMessage());
}
}
三、MySQL事务隔离级别
MySQL 事务隔离级别是为了解决并发事务互相干扰的问题的,MySQL 事务隔离级别总共有以下 4 种:
- READ UNCOMMITTED:读未提交。
- READ COMMITTED:读已提交。
- REPEATABLE READ:可重复读。
- SERIALIZABLE:序列化。
1.四种事务隔离级别
1.1 READ UNCOMMITTED
读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,而未提交的数据可能会发生回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
1.2 READ COMMITTED
读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
1.3 REPEATABLE READ
可重复读,MySQL 默认的事务隔离级别。可重复读可以解决“不可重复读”的问题,但还存在幻读的问题。所谓的幻读指的是,在同一事务的不同时间使用相同 SQL 查询时,会产生不同的结果。例如,一个 SELECT 被执行了两次,但是第二次返回了第一次没有返回的一行,那么这一行就是一个“幻像”行。
注意:幻读和不可重复读的侧重点是不同的,不可重复读侧重于数据修改,两次读取到的同一行数据不一样;而幻读侧重于添加或删除,两次查询返回的数据行数不同。
1.4 SERIALIZABLE
序列化,事务最高隔离级别,它会强制事务排序,使之不会发生冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。