我有一个以纯文本形式存储第 3 方密码的数据库表。我现在正在更新表以存储加密的密码。为了处理这个问题,我有一个@EntityListener
类来执行加密和解密后加载和预持久/更新。现在,我正在尝试编写代码来加密当前在数据库中的所有密码。
我编写了迁移函数来加载尚未迁移的所有内容并再次保存(以便实体侦听器可以运行)。实体侦听器实际上不会被调用,除非 Hibernate 认为对象是脏的,所以我决定从当前会话中驱逐实体,认为这将是运行转换的最简单方法。这是我所拥有的:
public class Migrator {
@Autowired
EntityManager entityManager;
@Autowired
MyRepository myRepo;
@Async
public void migrateAll() {
List<MyEntity> toBeMigrated = myRepo.getUnencrypted();
for (MyEntity eachEntity : toBeMigrated) {
attemptEncryption(eachEntity);
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
private void attemptEncryption(MyEntity entity) {
Session session = (Session) entityManager.getDelegate();
session.evict(entity);
myRepo.save(entity);
}
}
(未显示:调用 Migrator 的简单 Web 控制器)
我添加了@Transactional
注释,attemptEncryption()
以便保存迁移的每条记录。我不希望随机行的失败导致整个操作回滚;成功保存某些内容后,我希望提交该事务。
由于这需要一段时间才能运行,我想发回 HTTP 响应并migrateAll()
在单独的线程中运行。它在没有多线程的情况下工作得很好,但是一旦我添加了@Async
注释,我就遇到了org.hibernate.SessionException: Session is closed!
异常(stacktrace 显示在尝试驱逐实体时抛出了异常attemptEncryption()
)。我认为将工作转移到另一个线程是原因,但我已经@Async
在我的应用程序的其他部分使用过,没有任何问题。这段代码和我的其他异步代码之间的唯一主要区别是我正在从会话中驱逐实体;我不会在其他任何地方这样做。此外,数据库可以很好地加载实体myRepo.getUnencrypted()
。如果我在 Hibernate 上做错了什么,我觉得这应该会失败。
问题
- 当我去驱逐实体时,为什么我的会话关闭了?
- 有没有更好的方法让我的实体变脏,以便 Hibernate 运行 pre-persist 侦听器并刷新到数据库?
研究
- (以分离方式加载对象)使用 Spring Data JPA 和 JPA EntityListener 进行字段级加密- 我尝试添加
@Transactional(propagation = Propagation.NOT_SUPPORTED)
到getUnencrypted()
方法,但没有任何改变。 - (@Async 方法中的会话)https://stackoverflow.com/a/29271930/2047962 - 我已经
@Transactional
按照规定使用了注释。 - (强制更新“干净”实体)https://stackoverflow.com/a/37255257/2047962 - 无法使用 Persistable。我不希望这个实体总是看起来很脏,我只希望它对这个方法来说很脏。
- 在 Hibernate 中强制更新- 在
attemptEncryption()