Refresh Hibernate Formula - java

I have got two entities (example reduced as much as possible; each entity has got an id field):
#Entity
public class A {
#Column(nullable = false)
private double foo;
#Formula(value = "foo - (select coalesce(sum(x.foo), 0) from x where x.a_id = id)")
private double bar;
}
and
#Entity
public class X {
#ManyToOne(optional = false)
private A a;
#Column(nullable = false)
private double foo;
}
When I create a new X (new X(), beginTransaction(), save(x), commit()) the value of A.bar is not refreshed.
I think this happens because the old (and wrong) value is still in the first level cache (there is no 2nd level cache). I dont want to call Session.clear() since this method seems to invalidate existing entity-objects. What else can I do to solve this probelm?
EDIT: As requested here is the code to save X-objects:
// setters
getSession().beginTransaction(); // getSession() returns the current session
getSession().save(entity); // entity is an instance of X
getSession().getTransaction().commit();

Session.clear will remove all the cached objects from the session. instead you can use evict method on session and specify an object, which removes only the specified object from the cache.

I tried to solve the problem with clearing the cache but that was followed up by new problems and made the cache more or less useless because Xes are changed quite often. (In fact the subselect is much more complex than shown in the question. It uses more tables.)
Now I am using the StatelessSession that does not have a first-level cache. This solves the problem. Since the database is an embedded h2-Database the performance regression is not noticeable.

Related

Hibernate Envers wrong modified flag on list

I use Envers to audit my data and sometimes the value of the _MOD is incorrect. It stays at 0 instead of 1 when I am adding an element in my list. But it happens only in a specific case.
My entity:
#Entity
#Table(name = "PERSONNE")
#Audited(withModifiedFlag = true)
public class PersonEntity {
#Id
#Column(name = "ID_PERSONNE")
private Long id;
#Column(name = "NAME", length = 100)
private String name;
#Audited( withModifiedFlag = true, modifiedColumnName = "SERVICES_MOD")
private Set<PersonneServiceEntity> services = new HashSet<>(); // Entity with attributs, gettters, setters and envers annotations...
#Audited( withModifiedFlag = true, modifiedColumnName = "OPT_INS_MOD")
private Set<OptinEntity> optIns = new HashSet<>();// Entity with attributs, gettters, setters and envers annotations...
// more fields
// + getters, setteurs, equals, tostring
my service:
// personFromDB is retrieve via an Id
private void update(PersonEntity personFromRequest, PersonEntity personFromDB) {
personFromDB.setName(personFromRequest.getName());
updateServices(personFromRequest, personFromDB); // add new support to the list
updateOptins(personFromRequest, personFromDB); // add new services to the list
personDao.saveAndFlush(personFromDB);
}
This is were the magic happens: When I am updating name, services and optIns. Values in my database are all correct, my entity is correctly persisted, except one envers's column: OPT_INS_MOD ( OPT_INS_MOD == 0).
But if I am not updating the name ( line commented ) then everything is correctly persisted including all _MOD values ( OPT_INS_MOD == 1 and SERVICES_MOD ).
And finally if I am switching updateSupport(personFromRequest, personFromDB) and updateServices(personFromRequest, personFromDB), in this case OPT_INS_MOD is correct but not SERVICES_MOD.
My guess is that there is a problem when Envers is getting all modified fields. Because it does not make any sense to me.
Any ideas? I am using Envers version 4.3.11.Final
I'm not sure this will help you because it doesn't sound like the same problem but I've noticed a weirdness with modified flags and collections.
I get my entities back from the front end converted from JSON back to POJOs. In order to keep from having a transient object error from Hibernate, I need to reset the value in the #Id field (which was never sent to the FE). This works fine for 1-1 entities.
On collections, I found that if I create a new instance of the collection class and fill it with refreshed entities from the old collection and then assign that new collection to the old attribute, the modified flag is set to true.
However, if I fill a new collection with refreshed entities, clear() the old collection, then add all the items in the new collection, modified flag will be false unless there were actual changes to the collection.

Hibernate Envers - select "all" changes for entity ID

I have problem with Hibernate Envers.
I have classes like:
#Entity
#Table(name = "REVINFO")
#RevisionEntity(MyRevisionEntityListener.class)
public class RevEntity {
#Id
#RevisionNumber
#Column(name = "REV", nullable = false, updatable = false)
private Integer id;
#RevisionTimestamp
#Column(name = "REVTSTMP", nullable = false, updatable = false)
private Date timestamp;
#Column(name = "MODIFIED_BY", length = 100)
private String modifiedBy;
#Column(name = "COMMENT", length = 100)
private String comment;
public class MyRevisionEntityListener implements RevisionListener {
#Override
public void newRevision(Object revisionEntity) {
RevEntity a = (RevEntity) revisionEntity;
a.setComment("Some value");
}
}
How can i select every change for entity ID and their "REVINFO" object?
I've got something like this:
List resultList = AuditReaderFactory.get(entityManager)
.createQuery()
.forRevisionsOfEntityWithChanges(ClientType.class, true)
.add(AuditEntity.id().eq(entityId))
.getResultList();
And it's almost work good. I received every "change" but REVINFO looks strange. All fields are null - and there are 1 more object $$_hibernate_interceptor which actually hold "information" but i cannot acces it via code (or i dont know how). See example at the image.
So my question is:
1 - How can i get REVINFO values ?
2 - Do i realy have to use entityManager, or can it be achived with different approach ?
Edit 2:
Correct me if i am wrong, but does forRevisionsOfEntityWithChanges works as Lazy Initialization? I mean, if i try to receive for example modifiedBy field i actually get my data. Debugger log make me confused.
The call to forRevisionsOfEntityWithChanges returns an object array that contains:
Entity instance
Revision Entity
Revision Type
Property names that were changed.
How can i get REVINFO values ? 2 - Do i realy have to use entityManager, or can it be achived with different approach ?
So in your code, to get the revision info attributes, you would do the following. Note that in this code, the type of the revision-info object will depend on your configuration or if you're using a custom revision-info entity class in your deployment. Just be sure to cast it to the proper type.
for (Object entry : resultList) {
final Object[] row = (Object[]) entry;
final TheRevisionEntityClassType revisionInfo = row[1];
// now you can get the revision entity attributes from revisionInfo using getters
}
Correct me if i am wrong, but does forRevisionsOfEntityWithChanges works as Lazy Initialization? I mean, if i try to receive for example modifiedBy field i actually get my data. Debugger log make me confused.
Depending on the query, yes Hibernate may use proxies and its important to understand that in this case, the visual representation you get in the debugger may or may not be accurate depending if the object's internal state gets initialized by the debugger window or not.

Why are some modifications to object lost when I merge that object in Hibernate?

Here are the relevant pieces of the code I inherited. The object "process" is the old process that is passed to the method. The object "newProcess" is what I am replacing it with, using different fields of the user's choosing.
try
{
final EntityManager em = getEntityManager();
em.getTransaction().begin();
JpaProcessDAO pDao = new JpaProcessDAO(em);
Process newProcess = pDao.findById(processId);
newProcess.setName(process.getName());
newProcess.setDataBaseVersion(process.getDataBaseVersion());
newProcess.setNotes(process.getNotes());
newProcess.setReadyForUse(process.getReadyForUse();
newProcess.setSteps(process.getSteps());
em.merge(newProcess); <---- WHERE PROBLEM OCCURS
em.persist(newProcess);
em.getTrasaction().commit();
}
RESULT: Every field that I change is changed in newProcess EXCEPT "Steps". During the merge step in the code, that list goes back to whatever the steps were in the original object "process".
Now this could be because that "Step" is an object itself, not a primitive like all of the other fields I set in "newProcess":
Mapping in Process.java
#OneToMany(mappedBy="process")
private List<Step>
// getter, setter
In Step.java there is a collection of objects, some of which are lists of nonprimitive objects themselves.
Step.java
public class Step implements Serializable {
#Id
#Column(name = "step_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int stepId;
private String duration;
private String name;
private String notes;
private Integer sort;
#OneToMany(mappedBy="step", cascade=CascadeType.REMOVE)
private List<Constituent> constituents;
#OneToMany(mappedBy="step")
private List<Reference> references;
#ManyToOne
#JoinColumn(name ="process_id")
private Process process;
#OneToMany(mappedBy="step",cascade=CascadeType.REMOVE)
private List<StepEquipment> stepEquipments;
public Step() {
}
// getters/setters
}
Does anybody know what this inherited code I have could possibly do wrong?
ADDITIONS TO CODE ON 11/29:
public T findById(final Integer id) throws CPDPersistenceExceptin {
return findByPrimaryKey(id,templateClass);
}
public T findBYPrimaryKey(Object key, Class<T> clazz) {
T t = getEntityManager().find(clazz,key);
getEntityManager.merge(t);
getEntityManager.refresh(t);
return t; <-------------- newProcess is returned by this statement.
}
newProcess does not have the steps that were in the original process,nor does it have the ProcessCategories that were in process. The Hibernate logs say
that select is going on for process_id, database_version, process_name, process_notes, and process_ready_to_use only in the merge and refresh statements.
You need to synchronize both sides of the association. In your code you're only setting newProcess.setSteps(...), but each Step doesn't set a Process. From here:
However, we still need to have both sides in sync as otherwise, we break the Domain Model relationship consistency, and the entity state transitions are not guaranteed to work unless both sides are properly synchronized.
So in other words, you would need to do something along the lines of:
newProcess.setSteps(process.getSteps());
process.getSteps().forEach(s -> s.setProcess(newProcess));
As in answer from dyslexit told you need to set the Process to each Step.
But in addition you need to have the new Steps persisted and old ones removed. You can do this manually per Step but easier way would be to alter your code a bit.
Mofify the mapping annotation in step like:
#OneToMany(mappedBy = "process", cascade=CascadeType.PERSIST, orphanRemoval=true)
private List<Step> steps;
so tell persist to cascade to Steps also and to remove all Steps that are detached from Process.
Modify the update logic:
// newProcess.setSteps(process.getSteps());
// em.merge(newProcess); <---- WHERE PROBLEM OCCURS
// em.persist(newProcess);
newProcess.getSteps().clear(); // remove old steps
newProcess.getSteps().addAll(process.getSteps()); // add new steps
// You need to set the other side of association also as below
newProcess.getSteps().forEach(s -> s.setProcess(newProcess));
// em.persist(newProcess); // not sure if needed
SO: do not REPLACE the list but instead MODIFY the original list.
ALSO: there might not be a need for any merge/persist operation (and certainly doing both in series is not something that should ever be done). But because you use mystical JpaProcessDAO I can not be sure so check that.
And also see for what those are really used, great explanation here.
I am guessing that entity manager might handle everything just fine - without persist/merge stuff -because I think you already got managed entity when called pDao.findById(processId);, that is why I have commented it out.
Another story is then the mappings you have in your Step class. Those might also need changes to persistence & cascade setting.
As a side note: have also a look at this question how you might have update done easier with ModelMapper.

JPA Cacheable specific example on how to use it

I am kind of new to JPA and have read a bit about second level caching in JPA. And I think it should apply correctly to my scenario.
I have a table (say A) whose content will never change unless a new release is applied.
I need to query some data from the database : for that I have a JPQL working correctly and that uses a join between table A and an additional table (say B).
Since the contents of table A never change through the life of application, I could essentially mark this table A as Cacheable and reuse the content from Cache - rather than going to the database for that.
I have read about #NamedQuery that enables a one time JPQL to SQL translation for the life of the application. And it is not what I am looking for.
I want to know how should I go about using the Cacheable property for my purpose.
This is what I have done so far :
Marked tableA as cacheable -
#Entity
#Cacheable
#Table(name = "TableA")
public class Table{
#Id
#NotNull
#Column(updatable = false)
private String uuid;
#NotNull
#Size(min = 1)
private String description;
.
.
.
}
2. There is a DAO that does a find using the JPQL -
public Collection findAll(String description) {
final Cache cache = entityManager.getEntityManagerFactory().getCache();
if (cache.contains(TableA.class, "abc")) {
System.out.println("cached");
} else {
System.out.println("not cached");
}
final Query query = entityManager
.createQuery("Select distinct A from TableA A, IN(A.TableB) B where A.description = :description"); //$NON-NLS-1$
query.setParameter("description", description); //$NON-NLS-1$
return query.getResultList();
}
</code>
Can I take advantage of using Cacheable property in my scenario. If so, then could you suggest how?
Also, "not cached" gets printed always no matter what the value of string I use (from table) instead of "abc".
Appreciate your help. Thanks

How do I get the foreign key column value of a dependent Hibernate Entity without fetching the full entity?

I'm struggling with a problem that seems way too easy:
Setup is two Entities with a Many-To-One Relationsship in Hibernate 3:
#Entity
class M {
private N n;
#ManyToOne(fetch = FetchType.LAZY)
public N getN() { return n; }
public void setN(N n) { this.n = n; }
}
#Entity
class N {
private List<M> ms = new ArrayList<M>();
#OneToMany(mappedBy="n")
public List<M> getMs() { return ms; }
public void setMs(List<M> ms) { this.ms = ms; }
}
Easy enough. In my application, I have a list of Ms that either have an N or don't. This list is the input for a h:dataTable that shows different column content depending on whether the FK is null or not. But when I test m.getN() != null this causes hibernate to load N. How can I avoid this?
Edit: this is actually an error of mine, as pointed out by JBNizet in the comments. To at least make this useful to someone and keep with the layout above, I've rephrased the question to "How do I get the foreign key column value of a dependent Hibernate Entity without fetching the full entity?" as suggested by Aaron Digulla.
Edit 2: Turns out the new question is a duplicate of this one: How can I prevent Hibernate fetching joined entities when I access only the foreign key id? - so, close vote?
Create a projection mapping which contains M or several fields of M and e.g. id of N
Your query might liook sopething like
select new com.my.ProjectionObject(m, m.n.id) from M m where ...
How do you expect Hibernate to tell you something it doesn't know? Without loading the entity, Hibernate has no way to know whether it (still) exists.
If you step outside the Hibernate "entity mapper" box, you can query the database directly and, for example, count the number of Ns for your M.

Categories