Hibernate automatically updating Entity1 when querying Entity2 - java

I'm not expecting a concrete answer for this question because it's way too complex to explain in detail, just some guidance on where the problem could be.
Im summary, I have an entity Ship, with foreign keys to entities Origin, Destination and Country. I fetch a Ship from the database, then modify other field (date), and then query the database for related Origin, Destination and Country. When I query for Origin and Country, it goes as expected, but when I query for Destination, when executing query.getResultList() , and before the select a from DESTINATION, Hibernate automatically executes update SHIP set ... and it sets all the Ship fields except for IDN_DEST.
Any idea of what could this be happening?
My guess is that since the entity has been modified within the Session, Hibernates somehow things it needs to be updated, but that's all.
For what I've seen, there are no differences in the Ship.java:
// bi-directional many-to-one association
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "IDN_ORIGIN", insertable = false, updatable = false)
private Origin tOrigin;
#Column(name = "IDN_ORIGIN")
private Integer idnOrigin;
// bi-directional many-to-one association
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "IDN_DEST", insertable = false, updatable = false)
private Destination tDest;
#Column(name = "IDN_DEST", updatable = false)
private Integer idnDest;
// bi-directional many-to-one association
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "IDN_COUNTRY", insertable = false, updatable = false)
private Country tCountry;
#Column(name = "IDN_COUNTRY")
private Integer idnCountry;

About your question: Hibernate before a Query.list() perform a dirty check and automatically do a flush() to prevent inconsistent state.
To prevent that change Session.flushMode to COMMIT if you don't want automatic flush, but only at commit time.
About your example, two things:
Why you are not using a getter to get tOrigin, tCountry, tDest?
May you read tOrigin, tCountry, tDest before Ship update?

Tis is happening because when you select a Destination hibernate auto-flushes the session, so the state of the Ship is updated in the database. This means that when you modify other field (date) the ship object is attached to the session.

That should be not done only with Destination as tibtof told when you fetch data from database it creates persistent object and when you modify it hibernate will update it in database when it flushes connection, what you can try is after fetching Origin or Country flush session manually you should get updated data as what happened with 'Destination'.

Related

Hibernate: BigInteger vs Long from native query to be used in JPQL query

In our Java EE EJB application, we have the following JPA/Hibernate mapping of a class:
#Entity
#Table(name="T")
#TableGenerator( /* all annotation attributes */)
public class T {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name="SEQ_T", nullable = false)
private long seqT;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true,
mappedBy = "t",
fetch = FetchType.LAZY
)
private List<W> wu;
}
and these are the classes which are in relation with it:
#Entity
#Table(name="W")
#TableGenerator( /* all annotation attributes */)
public class W {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name="SEQ_W", nullable = false)
private long seqW;
#Column(name="SEQ_T", nullable = false)
private long seqT;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SEQ_T", insertable = false, updatable = false)
private T t;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true,
mappedBy = "w",
fetch = FetchType.LAZY
)
private List<WA> wua;
}
#Entity
#Table(name="WA")
#TableGenerator( /* all annotation attributes */)
public class WA {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name="SEQ_W_A", nullable = false)
private long seqWA;
#Column(name="SEQ_W", nullable = false)
private long seqW;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SEQ_W", insertable = false, updatable = false)
private W w;
}
Moreover, we have a scheduled job which is executed periodically by TimerService EJB.
First of all, this job must understand if there is something to execute so it performs a native sql query like the following to recover a list of pk from T table according several conditions:
List<Long> seqTs = (List<Long>)em.createNativeQuery("select SEQ_T from T").getResultList();
where em is an instance of EntityManager. The query is not obviously that simple but very complex as it derives from some JOIN and subqueries with other tables.
If the returned list is not empty, then the job can do its work and this JPQL is performed to load the entities it manipulates:
String queryJPQL = "select wu from W wu JOIN FECTCH wu.wua where wu.seqT in :seqTs";
List<Workup> wus = em.createQuery(queryJPQL, W.class)
.setParameter("seqTs", seqTs)
.getResultList();
This query is performed because even if we always need the data in #OneToMany relation, if we set that relation as EAGER then N+1 queries is performed. Instead, with JOIN FETCH a unique query is performed recovering a sort of view and then entities and relations are associated by Hibernate.
Well, the problem is that this exception is raised when .setParameter() is called:
Exception in thread "main" java.lang.IllegalArgumentException: Parameter value element [1] did not match expected type [java.lang.Long (n/a)]
Reading many posts here, and set a breakpoint in Eclipse, I discovered that not a List<Long> is returned from a native query but a List<BigInteger> (according to native type of PK in database), without any ClassCastException or similar. Why this?
So, I guess I should perform something like this before:
List<Long> seqTLong = new ArrayList<Long>();
for(BigInteger seqNative : seqTs)
seqTLong.add(seqNative.longValue());
and pass it to the query.
Anyway, is this the right solution? Is it safe? This because our application supports 3 DB and it is built accordingly in 3 JARs by ANT: Oracle, PostgreSQL and SQL Server.
Can I assume the value of PK is always BigInteger for each DB? In Oracle we use Number(19), in PostgreSQL we use BigInt...I don't remember about SQL Server.
This entities are then passed to DRools and when rules have been applied, this job uses EntityManager to persist data. That's why I need JPA entities to be loaded, otherwise I would get a
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist
or I would have to call .find() again for each fact modified by DRools and set its attributes by calling getters from the others. Which still causes N+1 queries.
A safer method would be to use Number instead of BigInteger
List<Long> seqTLong = new ArrayList<Long>();
for(Number seqNative : seqTs) {
seqTLong.add(seqNative.longValue());
}

Return an empty list on #OneToMany that is indeed 0 to many (nullable)

I have a FK that recursively points to the very same entity, but it can't be nullable
Person entity:
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "personId", referencedColumnName = "managerId", updatable = false, insertable = false, nullable = true)
private List<Person> managers;
But this is returning null instead of an empty list when the join is null. The problem is that I am not sure it's an error, or a design fault. While debugging, child person shows managers as a persistent bag, but orphan persons have no empty persistent bag but a null value.
But in this question, a user affirms the list should always be initialized: To initialize or not initialize JPA relationship mappings?
Just for your info, theres no LazyLoadingExceptions as all the code is within a #Transactional
In this other question (JPA OneToMany - Collection is null) there's an explanation about JPA using noargs constructor and setting fields directly from db, so I am wondering if the problem is that JPA sets the list to null because the fb returns the join column as null. If that were the case, would it be a way to tell JPA to send an empty list instead of null?
The code for fetching is a test:
#Test
#Transactional
public void fetchManagersOfCEO(){
Person person = this.personRepository.findOne(1L);
assertThat(result.getManagers().size()).isEqualTo(0);
}
This return expected managers when the id is the one of an employee with managers above
P.S> After a lot of debugging, the problem is that JPA is setting the collection to null, no "COLLECTION NOT NULL" ObjectMarker, and empty CollectionReferenceInitializers
I don't like it but the only way I found to work out the issue is:
public List<Person> getManagers(){
return Optional.ofNullable(this.managers).orElseGet(ArrayList::new);
}
Just do this:
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "personId", referencedColumnName = "managerId", updatable = false, insertable = false, nullable = true)
private List<Person> managers = new ArrayList<>();
Let's say we have entity A that has one to many entity B.
You can then replicate your problem by:
Creating A with no (null) list of B.
Persisting A.
Fetching A in the same transaction.
This is because Hibernate refers to the same object in a single transaction and the object has null as a list of B.
However, if you are in two separate transactions or if you manually clear the session (SessionFactory.clear or EntityManager.clear) then the list should properly be initialized with an empty persistent bag.

How do I maintain consistency of cached ManyToOne collections with READ_WRITE CacheConcurrencyStrategy in Hibernate?

I'm running into a difference between NONSTRICT_READ_WRITE and READ_WRITE CacheConcurrencyStrategy when writing "denormalized" collections... the idea being that I have a join table modeled as an entity but it also contains read only links to the tables it joins to.
My entities, roughly:
#Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
class Actor {
#Id
Integer id;
#Column
String name;
}
#Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
class Movie {
#Id
Integer id;
#Column
String title;
}
#Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
class Credit {
#Column
String roleName;
#ManyToOne(targetEntity = Movie.class, optional = true)
#JoinColumn(name = "movie_id", insertable = false, updatable = false)
#NotFound(action = NotFoundAction.IGNORE)
Movie movie;
#Column(name = "movie_id")
Long movieId;
#ManyToOne(targetEntity = Actor.class, optional = true)
#JoinColumn(name = "actor_id", insertable = false, updatable = false)
#NotFound(action = NotFoundAction.IGNORE)
Actor actor;
#Column(name = "actor_id")
Long actorId;
}
Second level object cache is enabled (with ehcache).
My application writes Movies and Actors... and sometime later, it links them together by writing Credit. When Credit is written, I only fill in the roleName, movieId, and actorId fields, I do not provide the Movie and Actor objects.
Using NONSTRICT_READ_WRITE caching, I am then able to read back that Credit object and it will contain the referenced Movie and Actor objects.
Using READ_WRITE caching, reading back the Credit will return a Credit with empty Movie and Actor fields. If I clear the hibernate cache, reading back that Credit then contains the Movie and Actor objects as expected. This is also the behavior with TRANSACTIONAL caching (but of course not with NONE caching).
So it would seem that hibernate is inserting Credit into the 2nd level cache with null Actor and Movie fields when using READ_WRITE cache. Is there a way to prevent this from happening and always read from the database to get back these joined fields? I've tried annotating just the fields with CacheConcurrencyStrategy.NONE, but this does not work.
I think you have probably stumbled across a hibernate bug because of your weird mapping (at least non standard mapping). There is no real reason for having two fields one with id and one with entity.
You can turn an id into an entity reference using session.load - which just creates a proxy, doesn't load the data from DB.
If you get rid of the movieId and actorId field and remove the insertable / updatable false on the movie / actor field it should work the same irrespective of READ_WRITE or NON_STRICT_READ_WRITE
Credit c = new Credit()
Movie m = session.load(movieId);
Actor a = session.load(actorId);
c.setMovie(m);
c.setActor(a);
session.save(c);
Hibernate doesn't save the complete objects in the second level cache. I stores it in a flattened form (hydrated - more like db tables) and reconstructs the object from that. So your assertion that it stored the object with null in the cache and not updating it is incorrect. There is something else going on.
The main difference between READ_WRITE and NON_STRICT_READ_WRITE is that the entry in the cache will be locked while updating in case of READ_WRITE and won't be locked in NON_STRICT_READ_WRITE. This will only impact if you are updating this entity in multiple threads concurrently.

#OrderColumn generates a request to update the primary key

I use a list. The list is comprised of a compound primary key which is also used for sorting the list.
The problem is that if I delete an element in the list (key compound),
annotation #OrderColumn generates a request to update a primary key, and the cost rises an exception of type:
[26-05-2011 10:34:18:835] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000
[26-05-2011 10:34:18:835] ERROR org.hibernate.util.JDBCExceptionReporter -Duplicate entry '10-10' for key 'PRIMARY'
[26-05-2011 10:34:18:835] ERROR org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
Here is the definition of the mapping :
#ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "chapter_item", joinColumns = { #JoinColumn(name = "chapter_id", nullable = false, updatable = false) }, inverseJoinColumns = { #JoinColumn(name = "item_id", nullable = false, updatable = false) })
#OrderColumn(name="iorder")
public List<Item> getItems() {
return items;
}
Here is the update query where I have a problem:
Hibernate:
update
chapter_item
set
item_id=?
where
chapter_id=?
and iorder=?
I wonder if this is a known bug, and if anyone has a solution?
Regarding #OrderColumn, following documentation is found at http://docs.oracle.com/javaee/6/api/javax/persistence/OrderColumn.html
Specifies a column that is used to maintain the persistent order of a
list. The persistence provider is responsible for maintaining the
order upon retrieval and in the database. The persistence provider is
responsible for updating the ordering upon flushing to the database to
reflect any insertion, deletion, or reordering affecting the list.
So we see that the persistence provider i.e. hibernate is responsible for updating the column named iorder. It is also said that:
The OrderColumn annotation is specified on a OneToMany or ManyToMany
relationship or on an element collection. The OrderColumn annotation
is specified on the side of the relationship that references the
collection that is to be ordered. The order column is not visible as
part of the state of the entity or embeddable class.
Please take note of the sentence that says:
The order column is not visible as part of the state of the entity or
embeddable class.
So, may I suggest you to consider not selecting the column iorder for #OrderColumn since it is a part of your composite key and hibernate is sure to update this value when you delete or insert an element in list (List<Item>).
Hope, this helps.
Maybe one option could be change the order annotation and use:
#ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "chapter_item", joinColumns = { #JoinColumn(name = "chapter_id", nullable = false, updatable = false) }, inverseJoinColumns = {#JoinColumn(name = "item_id", nullable = false, updatable = false) })
#org.hibernate.annotations.Sort(type = SortType.COMPARATOR, comparator = ItemComparator)
public List<Item> getItems() {
return items;
}
https://dzone.com/articles/sorting-collections-hibernate
Check performance solution because maybe is too slow for big amount of data, and if you can share if was a possible solution would be great to know

Bi-directional Many to Many JPA

I having a hard time with JPA hopefully someone can help me.
I have 3 tables:
Rol (CPE_ROL)
TipoUsuario (GTV_TIPOUSU)
RolTipoUsuario (CPE_ROLTUS - Join Table)
Rol.java
#JoinTable(name = "CPE_ROLTUS", joinColumns = {
#JoinColumn(name = "CPE_ROLTUS_TIPOUSU_ID", referencedColumnName = "GTV_TIPOUSU_ID")}, inverseJoinColumns = {
#JoinColumn(name = "CPE_ROLTUS_ROL_ID", referencedColumnName = "CPE_ROL_ID")})
#ManyToMany(fetch = FetchType.LAZY, cascade={CascadeType.REFRESH})
private List<TipoUsuario> tipoUsuarioList;
TipoUsuario.java
#ManyToMany(mappedBy = "tipoUsuarioList", fetch = FetchType.LAZY, cascade={CascadeType.REFRESH})
private List<Rol> rolesDefault;
For some reason rolesDefault is never filled up, I wondering if I'm missing something.
Thanks in advance.
Daniel
My guess is when you create the objects you are not setting both sides of the relationship. You must maintain bi-directional relationships in JPA. When you add to one side, also add to the other.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side
You most likely have caching enabled, or are using the same EntityManager, so when reading you get objects from the cache. You could also disable the shared cache, or refresh the object, but fixing your persist code is best.
Otherwise, enable logging on finest and see what SQL is executed.

Categories

Resources