Grails: getting old instance after persisting - java

I'm facing a weird issue in Grails. When I make a findBy call after changing and saving values of a domain, I am still getting the old values even after the values get persisted to the database. I can see the values are changed in my table.
My code is something like this:
Car car = Car.findByCarId(carId)
car.modelName = "some_model_name"
car.save() // Not flushing here
Tire tire = Tire.findByIdAndCarId(tireId,carId)
tire.manufacturer = "some_manufacturer"
tire.save()
Light light = Light.findByIdAndCarId(lightId,carId)
light.manufacturer = "some_manufacturer"
light.save()
Mirror mirror = Mirror.findByIdAndCarId(mirrorId,carId)
mirror.manufacturer = "some_manufacturer"
mirror.save()
My domain also has a few one-to-many associations. Let's say something like this:
class Car {
String modelName
static hasMany = [tires : Tire, mirrors : Mirror, lights : Light]
}
After these changes, when I make a DB call for the Car domain, I still get the older values:
Car car = Car.findById(carId)
println car.modelName // This gives older value
I know this is because the values are yet to be persisted to the database. I want to know if I use car.save(flush: true) in the above code, will it cause collection was not processed by flush() error? However, I am also getting the older values even after the values are persisted to the database. (e.g. when I make the above query after a long time) I can see the values are changed in my tables, but when I do the above query, it gives me the old values. Does Hibernate cache this query automatically? I use the above query quite a lot of times.
When I use withNewSession, it retrieves the new values:
Car car
Car.withNewSession {
car = Car.findById(carId,[readOnly : true])
}
println car.modelName // Gives new value
I want to know how this is giving the new values everytime, since I'm not flushing the current session. Instead, I'm only using a new Hibernate session. Does readOnly command flush the current session? The above code works fine for me. Should I use withNewSession instead of flushing while saving?
Thanks.

All your business logic has to be inside a transactional service. All services in grails are transactional by default. Take care about notations as #Transactional or #NotTransactional
If you want to manage data in a controller (not recommended) you should surround your code into a Transaction. An allow hibernate to manage the transaction, not breaking it with flush.
All changes are commited after a transaction finishes.
Remember that you could also use the refresh method which re-reads the state of the given instance from the underlying database.
domainInstance.refresh()

Related

Spring JPA always caches data [duplicate]

This question already has answers here:
Spring Data JPA Update #Query not updating?
(5 answers)
Closed 2 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
Let's suppose to have this situation:
We have Spring Data configured in the standard way, there is a Respository object, an Entity object and all works well.
Now for some complex motivations I have to use EntityManager (or JdbcTemplate, whatever is at a lower level than Spring Data) directly to update the table associated to my Entity, with a native SQL query. So, I'm not using Entity object, but simply doing a database update manually on the table I use as entity (it's more correct to say the table from which I get values, see next rows).
The reason is that I had to bind my spring-data Entity to a MySQL view that makes UNION of multiple tables, not directly to the table I need to update.
What happens is:
In a functional test, I call the "manual" update method (on table from which the MySQL view is created) as previously described (through entity-manager) and if I make a simple Respository.findOne(objectId), I get the old object (not updated one). I have to call Entitymanager.refresh(object) to get the updated object.
Why?
Is there a way to "synchronize" (out of the box) objects (or force some refresh) in spring-data? Or am I asking for a miracle?
I'm not ironical, but maybe I'm not so expert, maybe (or probably) is my ignorance. If so please explain me why and (if you want) share some advanced knowledge about this amazing framework.
If I make a simple Respository.findOne(objectId) I get old object (not
updated one). I've to call Entitymanager.refresh(object) to get
updated object.
Why?
The first-level cache is active for the duration of a session. Any object entity previously retrieved in the context of a session will be retrieved from the first-level cache unless there is reason to go back to the database.
Is there a reason to go back to the database after your SQL update? Well, as the book Pro JPA 2 notes (p199) regarding bulk update statements (either via JPQL or SQL):
The first issue for developers to consider when using these [bulk update] statements
is that the persistence context is not updated to reflect the results
of the operation. Bulk operations are issued as SQL against the
database, bypassing the in-memory structures of the persistence
context.
which is what you are seeing. That is why you need to call refresh to force the entity to be reloaded from the database as the persistence context is not aware of any potential modifications.
The book also notes the following about using Native SQL statements (rather than JPQL bulk update):
■ CAUTION Native SQL update and delete operations should not be
executed on tables mapped by an entity. The JP QL operations tell the
provider what cached entity state must be invalidated in order to
remain consistent with the database. Native SQL operations bypass such
checks and can quickly lead to situations where the inmemory cache is
out of date with respect to the database.
Essentially then, should you have a 2nd level cache configured then updating any entity currently in the cache via a native SQL statement is likely to result in stale data in the cache.
In Spring Boot JpaRepository:
If our modifying query changes entities contained in the persistence context, then this context becomes outdated.
In order to fetch the entities from the database with latest record.
Use #Modifying(clearAutomatically = true)
#Modifying annotation has clearAutomatically attribute which defines whether it should clear the underlying persistence context after executing the modifying query.
Example:
#Modifying(clearAutomatically = true)
#Query("UPDATE NetworkEntity n SET n.network_status = :network_status WHERE n.network_id = :network_id")
int expireNetwork(#Param("network_id") Integer network_id, #Param("network_status") String network_status);
Based on the way you described your usage, fetching from the repo should retrieve the updated object without the need to refresh the object as long as the method which used the entity manager to merge has #transactional
here's a sample test
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ApplicationConfig.class)
#EnableJpaRepositories(basePackages = "com.foo")
public class SampleSegmentTest {
#Resource
SampleJpaRepository segmentJpaRepository;
#PersistenceContext
private EntityManager entityManager;
#Transactional
#Test
public void test() {
Segment segment = new Segment();
ReflectionTestUtils.setField(segment, "value", "foo");
ReflectionTestUtils.setField(segment, "description", "bar");
segmentJpaRepository.save(segment);
assertNotNull(segment.getId());
assertEquals("foo", segment.getValue());
assertEquals("bar",segment.getDescription());
ReflectionTestUtils.setField(segment, "value", "foo2");
entityManager.merge(segment);
Segment updatedSegment = segmentJpaRepository.findOne(segment.getId());
assertEquals("foo2", updatedSegment.getValue());
}
}

Is there an easier way to tell that a JPA Entity has been modified other than looping through each field?

I have a custom EmptyInterceptor that I use to set information on the creation date, last modification date, created by user, and last modified user by overriding onSave and onFlushDirty.
This system has worked pretty well for us but we just found an issue where we have code that is blindly calling the setters on an entity using some input data. In this case, even if the data has not changed, hibernate will fire our custom interceptor and we'll set the last modified user and date. This causes Hibernate to update the entity in the database. We know this because we have an insert and update trigger on this table. If we disable the interceptor, Hibernate will not update the entity in the database.
We'd like to avoid setting the user and date if the entity hasn't really changed so no update happens in those situations. I've read about the way Hibernate does its dirty entity checking and I have the previousState and currentState arrays passed into onFlushDirty and I can loop through to do this check myself. Is there any easier way to do that?
I looked at HibernateSession.isDirty() but that doesn't tell me if this particular entity has changed, just if there's a changed entity in the session.
UPDATE
It turns out that the offending code blindly calling setters was not the issue. It was the offending code setting a Collection of child objects instead of modifying the Collection that was already there. In this case, Hibernate thinks the entity has changed - or at least thinks so enough to call the Interceptor.
From the design perspective, this check should really be done on the front-end/client side. If the front-end determines that the record is modified by the user, then it should submit an update to the server.
If you want to do this in the middle tier (server-side), then you should think about the lifecycle of a Hibernate entity: Transient, Persistent, Detached, Removed, and also think about how session.save() is different from session.merge() and session.saveOrUpdate().
Furthermore, you also have to consider the different design patterns when managing session, such as "session-per-operation" anti-pattern, session-per-request, or session-per-conversation patterns, etc...
If you have an open session, and your entity is already in the session (from another operation), then Hibernate can in fact do the dirty checking for you. But if you have a detached entity and that entity doesn't exist in the session, and say you do a merge, Hibernate will first fetch that entity from the data store (database) by issuing a SELECT and puts that managed entity in the persistent context, and then it will merge the two entities, the one you provide and the one hibernate fetches and puts in the persistence context, and then it checks to see if anything has changed, hence dirty-checking.
In your case, since you want to exclude the last-modified-user name and time from dirty checking, you might as well get the entity by ID (since you presumably have the ID on the detached entity) and then use equals() or hashCode() to do your own version of dirty checking, and if they are not, then call merge.
You may think this is an extra trip to the DB, but it's not, because even in normal cases, Hibernate still makes the extra trip to the DB, if the entity is not already in the persistent context (i.e. session), and if it is, and you do a get-by-id, Hibernate will just return to you what it already has in the session, and won't hit the DB.
This argument doesn't apply to saveOrUpdate, in which case, Hibernate will simply push the updates to the DB without dirty checking (if the entity is not already in the session), and if it is already in the session, it will throw an exception saying that the entity is already in the session.
In case anyone needs to solve the same problem without changing the code that causes this issue, I implemented the compare as:
/**
* Called upon entity UPDATE. For our BaseEntity, populates updated by with logged in user ID and
* updated date-time.
* <p>
* Note that this method is called even if an object has NOT changed but someone's called a setter on
* a Collection related to a child object (as opposed to modifying the Collection itself). Because of
* that, the comparisons below are necessary to make sure the entity has really changed before setting
* the update date and user.
*
* #see org.hibernate.EmptyInterceptor#onFlushDirty(java.lang.Object, java.io.Serializable, java.lang.Object[], java.lang.Object[], java.lang.String[], org.hibernate.type.Type[])
*/
#Override
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState,
String[] propertyNames, Type[] types)
{
boolean changed = false;
if (entity instanceof BaseEntity) {
logger.debug("onFlushDirty method called on " + entity.getClass().getCanonicalName());
// Check to see if this entity really changed(see Javadoc above).
boolean reallyChanged = false;
for (int i = 0; i < propertyNames.length; i++) {
// Don't care about the collection types because those can change and we still don't consider
// this object changed when that happens.
if (!(types[i] instanceof CollectionType)) {
boolean equals = Objects.equals(previousState[i], currentState[i]);
if (!equals) {
reallyChanged = true;
break;
}
}
}
if (reallyChanged) {
String userId = somehowGetUserIdForTheUserThatMadeTheRequest();
Date dateTimeStamp = new Date();
// Locate the correct field and update it.
for (int i = 0; i < propertyNames.length; i++) {
if (UPDATE_BY_FIELD_NAME.equals(propertyNames[i])) {
currentState[i] = userId;
changed = true;
}
if (UPDATE_DATE_FIELD_NAME.equals(propertyNames[i])) {
currentState[i] = dateTimeStamp;
changed = true;
}
}
}
}
return changed;
}
}

How to create new Row's without JPA consider it an update?

I'm working in a project right now, here is a piece of code:
public boolean getAll() {
TypedQuery<Tag> query = em.createQuery("SELECT c FROM Tag c WHERE (c.tagName !=?1 AND c.tagName !=?2 AND c.tagName !=?3) ", Tag.class);
query.setParameter(1, "Complete");
query.setParameter(2, "GroupA");
query.setParameter(3, "GroupB");
List<Tag> Tag= query.getResultList();
But when I try to do something like this:
Tag.get(2).setTagName = "Hello";
em.persist(Tag.get(2));
It considers it to be an update instead of a create? How can I make JPA understand that it's not database related, to detach the chains with the Database and create new register only changing its name for example?
Thanks a lot for any help!
Best regards!
EDIT:
Using the em.detach just before changing it values and persisting each of the list worked just fine!
Thanks everyone!
You haven't showed us how you are obtaining your list, but there are two key points here:
everything read in from an EntityManager is managed - JPA checks
these managed objects for changes and will synchronize them with the
database when required (either by committing the transaction or
calling flush).
Calling persist on a managed entity is a no-op - the entity is
already managed, and will be synchronized with the database if it
isn't in there yet.
So the first Tag.get(2).setTagName = "Hello"; call is what causes your update, while the persist is a no-op.
What you need do to instead is create a new instance of your tag object and set the field. Create a clone method on your object that copies everything but the ID field, and then call persist on the result to get an insert for a new Entity.
The decision whether to update or create a new entity object is done based on the primary key. You're probably using an ID on every object. Change or remove it and persist then. This should create a new entry.
If that doesn't work, you might need to detach the object from the Entity Manager first:
em.detach(tagObj);
and persist it afterwards:
em.persist(tagObj);
You can also force an update instead of creation by using
em.merge(tagObj)
There is no equivalent for forced creation AFAIK. persist will do both depending on PK.

Spring #Transactional DAO calls return same object

We are using Spring and IBatis and I have discovered something interesting in the way a service method with #Transactional handles multiple DAO calls that return the same record. Here is an example of a method that does not work.
#Transactional
public void processIndividualTrans(IndvTrans trans) {
Individual individual = individualDAO.selectByPrimaryKey(trans.getPartyId());
individual.setFirstName(trans.getFirstName());
individual.setMiddleName(trans.getMiddleName());
individual.setLastName(trans.getLastName());
Individual oldIndvRecord = individualDAO.selectByPrimaryKey(trans.getPartyId());
individualHistoryDAO.insert(oldIndvRecord);
individualDAO.updateByPrimaryKey(individual);
}
The problem with the above method is that the 2nd execution of the line
individualDAO.selectByPrimaryKey(trans.getPartyId())
returns the exact object returned from the first call.
This means that oldIndvRecord and individual are the same object, and the line
individualHistoryDAO.insert(oldIndvRecord);
adds a row to the history table that contains the changes (which we do not want).
In order for it to work it must look like this.
#Transactional
public void processIndividualTrans(IndvTrans trans) {
Individual individual = individualDAO.selectByPrimaryKey(trans.getPartyId());
individualHistoryDAO.insert(individual);
individual.setFirstName(trans.getFirstName());
individual.setMiddleName(trans.getMiddleName());
individual.setLastName(trans.getLastName());
individualDAO.updateByPrimaryKey(individual);
}
We wanted to write a service called updateIndividual that we could use for all updates of this table that would store a row in the IndividualHistory table before performing the update.
#Transactional
public void updateIndividual(Individual individual) {
Individual oldIndvRecord = individualDAO.selectByPrimaryKey(trans.getPartyId());
individualHistoryDAO.insert(oldIndvRecord);
individualDAO.updateByPrimaryKey(individual);
}
But it does not store the row as it was before the object changed. We can even explicitly instantiate different objects before the DAO calls and the second one becomes the same object as the first.
I have looked through the Spring documentation and cannot determine why this is happening.
Can anyone explain this?
Is there a setting that can allow the 2nd DAO call to return the database contents and not the previously returned object?
You are using Hibernate as ORM and this behavior is perfectly described in the Hibernate documentation. In the Transaction chapter:
Through Session, which is also a transaction-scoped cache, Hibernate provides repeatable reads for lookup by identifier and entity queries and not reporting queries that return scalar values.
Same goes for IBatis
MyBatis uses two caches: a local cache and a second level cache. Each
time a new session is created MyBatis creates a local cache and
attaches it to the session. Any query executed within the session will
be stored in the local cache so further executions of the same query
with the same input parameters will not hit the database. The local
cache is cleared upon update, commit, rollback and close.

Return Hibernate envers Audit revision with modified flags

I'm using Hibernate Envers in my app to track changes in all fields of my entities.
I'm using #Audited(withModifiedFlag=true) annotation to do it.
The records are been correcty recorded at database and the _mod fields correctly indicate the changed fields.
I want to get a particular revision from some entity and the information of what fields have been changed. I'm using the follow method to do it:
List<Object[]> results = reader.createQuery()
.forRevisionsOfEntity(this.getDao().getClazz(), false, true)
.add(AuditEntity.id().eq(id))
.getResultList();
This method returns an list of an object array with my entity as first element.
The problem is that the returned entity doesn't have any information about the changed fields. So, my question is: how to get the information about the changed fields?
I know that this question is a bit old now but I was trying to do this and didn't really find any answers.
There doesn't seem to be a nice way to achieve this, but here is how I went about it.
Firstly you need to use projections, which no longer gives you a nice entity model already mapped for you. You'll still get back an array of Objects but each object in the array corresponds to each projection that you added (in order).
final List<Object[]> resultList = reader.createQuery()
.forRevisionsOfEntity(this.getDao().getClazz(), false, true)
// if you want revision properties like revision number/type etc
.addProjection(AuditEntity.revisionNumber())
// for your normal entity properties
.addProjection(AuditEntity.id())
.addProjection(AuditEntity.property("title")) // for each of your entity's properties
// for the modification properties
.addProjection(new AuditProperty<Object>(new ModifiedFlagPropertyName(new EntityPropertyName("title"))))
.add(AuditEntity.id().eq(id))
.getResultList();
You then need to map each result manually. This part is up to you, but I'm use a separate class as a revision model as it contains extra data to the normal entity data. If you wanted you could probably achieve this with #Transient properties on your entity class though.
final List<MyEntityRevision> results = resultList.stream().map(this::transformRevisionResult)
.collect(Collectors.toList());
private MyEntityRevision transformRevisionResult(Object[] revisionObjects) {
final MyEntityRevision rev = new MyEntityRevision();
rev.setRevisionNumber((Integer) revisionObjects[0]);
rev.setId((Long) revisionObjects[1]);
rev.setTitle((String) revisionObjects[2]);
rev.setTitleModified((Boolean) revisionObjects[3]);
return rev;
}

Categories