0

I had a database table that was storing 3rd party passwords in plain text. I am now updating the table to store the passwords encrypted. To handle this, I have an @EntityListener class that does the encryption and decryption post-load and pre-persist/update. Now, I'm trying to write code to encrypt all of the passwords that are currently in the database.

I wrote the migration function to load everything that hasn't been migrated and save it again (so that the entity listener could run). The entity listener won't actually be invoked unless Hibernate thinks that the object is dirty, so I decided to evict the entity from the current session, thinking that would be the easiest way to run the conversion. Here's what I have:

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);
  }
}

(Not shown: a simple web controller that calls the Migrator)

I added the @Transactional annotation to attemptEncryption() so that each record migrated would be saved. I didn't want a failure on a random row causing a rollback of the entire operation; as soon as something is successfully saved, I want that transaction committed.

Since this takes a while to run, I wanted to send back the HTTP response and run migrateAll() in a separate thread. It worked fine without multithreading, but once I added the @Async annotation, I ended up with org.hibernate.SessionException: Session is closed! exceptions (stacktrace showed that the exception was thrown when trying to evict the entity in attemptEncryption()). I assume that moving the work to another thread is the cause, but I've used @Async in other parts of my application without any issues. The only major difference between this code and my other async code is that I'm evicting entities from the session; I don't do that anywhere else. Also, the database loads entities just fine with myRepo.getUnencrypted(). I feel like this should fail if I were doing something wrong with Hibernate.

Questions

  • Why is my session closed when I go to evict the entity?
  • Is there a better way to make my entity dirty so that Hibernate will run the pre-persist listener and flush to the DB?

Research

RustyTheBoyRobot
  • 5,891
  • 4
  • 36
  • 55

1 Answers1

0

This is because @Transaction isn't accounted for when you call the method from the bean inside (and use the standard AOP) mechanism. A more detailed answer is here: https://stackoverflow.com/a/4396530/66686

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348