我遇到一种情况,用户需要将一个有价值数据的文件导入数据库。他们可以在开始时选择是否执行标准导入,然后创建报告摘要文件,或“模拟”导入(即创建相同的报告摘要,但实际上不导入任何内容)。基本设置如下:
public class Importer {
@Autowired
protected IConverter converter;
@Autowired
protected ISerializer serializer;
@Autowired
protected IReporter reporter;
public void import( InputStream stream ) throws Exception {
CustomerData data = converter.convert( stream );
// ** database at this point has been updated! **
if( getContext().isSerialize() ) {
serializer.serialize( data );
}
if( getContext().isReport() ) {
reporter.report( data, "report.xls" );
}
}
}
public class Converter implements IConverter throws Exception {
@Transactional
public CustomerData convert( InputStream stream ) {
try {
CustomerData data = ... // read file and create/match with db entities
return data;
} finally {
if( !getContext().isSerialize() ) {
// clear any changes made to objects made in the db
getEntityManager().clear();
// ** database at this point is unaffected **
}
}
}
}
Importer是在Spring 4.1中配置的bean类。数据库是JPA 2.1 / Hibernate 4.3.11 / MySQL 5.5。使用Java 8。
CustomerData对象是一个数据库实体对象树,其中一些已与数据库中的数据匹配(可能使用导入文件中的数据更新属性)以及其他新成员。
isSerialize()和isReport()允许控制数据库是否更新。模拟导入时,isSerialize()= false,isReport()= true。
单步执行代码,当我输入finally块并清除实体管理器时,数据库中的数据就像导入之前一样。但是当我返回到import()方法时,数据库已经根据实体的更改进行了更新!
显然,事务性import()方法完成提交数据,但为什么清除实体管理器不会阻止更改发生?为了确保我在[Hibernate] AbstractEntityManagerImpl.flush()上设置一个断点,这里根本没有调用它。
有人可以帮助我理解为什么clear()不起作用,以及我应该做什么。
答案 0 :(得分:0)
谢谢,S. Piller让我走上正轨。对于处于类似位置的任何人,清除实体管理器不会停止在事务结束时提交给数据库的刷新更改 - clear()将仅清除自上次刷新以来更改的实体。
解决方法是通知事务管理器需要回滚事务:
@Transactional
public CustomerData convert( InputStream stream ) {
try {
CustomerData data = ... // read file and create/match with db entities
return data;
} finally {
if( !getContext().isSerialize() ) {
// Ensure the current transaction is rolled back once the topmost @Transactional method completes.
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
}