项目有一个简单的批量插入,一开始本地使用的迭代器,循环调用insert接口,
list.foreach(x -> basemapper.insert(x));
本地执行的时候是没有问题的
但是发布到线上测试环境之后,使用的是k8s容器部署的,执行插入之后,打印日志只有
日志没有影响行数,且数据库中没有插入任何数据,程序也没有报错。
后来尝试把迭代器替换成了
for(X x : list){ basemapper.insert(x) }
就没有问题了,这是为什么呢?
目前已知的区别是本地使用的是oracle jdk 1.8.0,线上测试使用的是openjdk version "1.8.0_232",暂时没有想到别的区别
可能是因为使用 foreach 迭代器批量插入数据时,MyBatis 没有正确地处理事务,导致数据没有被真正地插入到数据库中。而使用传统的 for 循环则可以正确地处理事务,因此数据可以被成功插入到数据库中。
MyBatis 在处理批量插入操作时,通常需要使用事务来保证数据的完整性和一致性。在使用 foreach 迭代器时,MyBatis 可能无法正确地处理事务,从而导致数据插入失败。为了避免这种情况,您可以尝试在插入操作之前手动开启事务,并在操作完成后手动提交事务。例如:
TransactionTemplate transactionTemplate = new TransactionTemplate(dataSource);
transactionTemplate.execute(new TransactionCallback<Void>() {
public Void doInTransaction(TransactionStatus status) {
list.forEach(x -> basemapper.insert(x));
return null;
}
});
上述代码中,我们使用了 Spring 的 TransactionTemplate 来手动处理事务。在 execute() 方法中,我们调用了 list.forEach() 方法来批量插入数据,并且在 doInTransaction() 方法中返回了 null 值。
通过这种方式,我们可以确保在批量插入数据时,MyBatis 可以正确地处理事务,从而避免数据插入失败的问题。
您好,我在日志中查看到的
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@41f3892a] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2642cc4] will not be managed by Spring
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@41f3892a]
是否跟您说的这个问题相关呢。
为什么本地调试时能够插入成功呢。
@一人暮らしda: 1.本地调试连接的数据库,与web服务器连接的数据库,是否是同一个库;
2.数据库服务器的响应时间:如果数据库服务器的响应时间较快,例如在100毫秒以内,那么sqlconnection连接关闭时间可能会在几百毫秒之内。
3.网络延迟和带宽限制:如果网络延迟较严重或者带宽有限,或web服务器上搭了几十个站点,或web服务器性能较差,连接关闭时间可能会较长。例如,如果网络延迟达到100毫秒以上,连接关闭时间可能会在几秒到十几秒之间。
4.数据库操作的数据量和复杂度:如果一次SQL事务涉及大量的数据操作或者复杂的逻辑处理,那么连接关闭时间可能会较长。例如,如果一次SQL事务需要执行多张表的操作或者需要进行复杂的计算,连接关闭时间可能会在几秒到几十秒之间。
5.在上述情况下,在forEach中,假设第一个insert执行2秒关闭事务,而第二个insert在1.5秒的时候启动一个新的事务,那么就可能报错了,同一个表、同一个连接,前一个事务未完成,又启动新的事务
6.由于for循环是顺序排队执行的,不会有插入新事务的问题,所以没报错
@lanedm: 好的,感谢
for循环里写sql会严重影响性能,建议for循环处理数据,最后在循环外saveBatch()插入数据