0

我有一个以纯文本形式存储第 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 侦听器并刷新到数据库?

研究

4

1 回答 1

0

这是因为@Transaction当您从 bean 内部(并使用标准 AOP)机制调用方法时,没有考虑到这一点。更详细的答案在这里:https ://stackoverflow.com/a/4396530/66686

于 2019-02-05T06:06:05.867 回答