How to know an object has changed compared to database

1.4k views Asked by At

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

There are 4 answers

0
rochb On BEST ANSWER

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

1
Baldo On

It looks like you're using an extended session/entityManager. When are you opening the session? Did you try:

session.clear()/entityManager.clear()

before the second call to MyModel.findById(id); ?

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.clear() // I don't know if you have a MyModel.clear() but you should call session.clear()/entityManager.clear() here

        MyModel m2 = MyModel.findById(id);
    }
}
1
aurelius On

You can add a boolean field to the model (like "hasBeenModified" - initialized with false), without bounding it to the db, and whenever you update your object you change the value of hasBeenModified to true.

0
goblingift On

You could add a version field in your Entity/Table, like it´s used for optimistic locking in jpa.