Search code examples
javahibernatejpaplayframeworkplayframework-1.x

How to know an object has changed compared to database


I need to know if some fields of a model object has been changed before save because I need to compare the new values with the old ones.

I can't touch the model classes are they are generated.

My problem is that whenever I change an object in a controller and check the database to have the object as it is stored BEFORE I save the modified object, the returned object from database is the "same" as the modified object.

I'm using Play! 1.2.7 and basically I have this :

class MyModel extends Model {
    public String label;
}

class MyModels extends Controller {

    public static void save(Long id) {
        MyModel m = MyModel.findById(id); // at this point m.label is "original"

        m.label = "changed !";

        MyModel m2 = MyModel.findById(id); // m2.label is "changed !" but should be "original", shouldn't it ?

    }
}

Maybe the question could be : How to force JPA EntityManager to look into database for real instead of returning the object from context ? as it seems to be the real problem here.

Solution

So the final solution is :

class MyModels extends Controller {

    public static void save(Long id) {
        MyModel m = MyModel.findById(id); // at this point m.label is "original"

        m.label = "changed !";
        MyModel m2;
        JPAPlugin.startTx(false);
        try {
            m2 = MyModel.findById(id); m2.label is "original"
        } finally {
            JPAPlugin.closeTx(false);
        }
    }
}

Also an another way to achieve this is to create a new EntityManager like so :

EntityManager manager = JPA.entityManagerFactory.createEntityManager();
// set your new EntityManager as you like...
manager.setProperty("org.hibernate.readOnly", true);
Query q = manager.createQuery("select m from MyModel m where id = :id");
q.setMaxResults(1);
q.setParameter("id", m.getBaseId());

MyModel m2 = (MyModel) q.getResultList().get(0);

Solution

  • To force Hibernate to start a new transaction in the playframework and thus give you a new entity manager that will return the object as it is in the database and not a reference to it, you need to specifically ask the play framework to start a new transaction through the JPAPlugin.

    JPAPlugin.startTx(false);
    try {
        // your code
    } finally {
        JPAPlugin.closeTx(false);
    }
    

    See the javadoc of startTx and closeTx