@IdClass Instance Instance Id changed 'with JPA and Hibernate

For a JPA entity model using a case-insensitive database schema, when I use the @IdClass annotation, I sequentially get the "instance id changed" exception. For an object with the primary key β€œrow”, an error occurs when a row of one case exists in the database and the query is executed with the same row that differs only in the case.

I looked at other SO answers, and they have the form: a) do not change the primary key (I do not), and b) your implementations of equals () / hashCode () are wrong. For 'b', I tried using toLowerCase() and equalsIgnoringCase() , but to no avail. [In addition, it seems that the Hibernate code sets properties directly, rather than causing property settings when a β€œchange” occurs.]

Here is a specific error:

 Caused by: javax.persistence.PersistenceException: org.hibernate.HibernateException: identifier of an instance of db.Company was altered from {Company.Identity [62109154] ACURA} to {Company.Identity [63094242] Acura} 

Q: For a case-insensitive database containing Acura (as a primary key) using @IdClass, how can I find other capitalizations later on?

Here is the violation code (starting with an empty database):

 public class Main { public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("mobile.mysql"); EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); Company c1 = new Company ("Acura"); em.persist(c1); em.getTransaction().commit(); em.getTransaction().begin(); c1 = em.find (Company.class, new Company.Identity("ACURA")); em.getTransaction().commit(); em.close(); System.exit (0); } } 

and here is the implementation of 'db.Company':

 @Entity @IdClass(Company.Identity.class) public class Company implements Serializable { @Id protected String name; public Company(String name) { this.name = name; } public Company() { } @Override public int hashCode () { return name.hashCode(); } @Override public boolean equals (Object that) { return this == that || (that instanceof Company && this.name.equals(((Company) that).name));} @Override public String toString () { return "{ Company@ " + hashCode() + " " + name + "}"; } // public static class Identity implements Serializable { protected String name; public Identity(String name) { this.name = name; } public Identity() { } @Override public int hashCode () { return name.hashCode(); } @Override public boolean equals (Object that) { return this == that || (that instanceof Identity && this.name.equals(((Identity)that).name)); } @Override public String toString () { return "{Company.Identity [" + hashCode() + "] " + name + "}"; } } } 

Note. I know that using @IdClass not required when there is one primary key; the above is the simplest example of a problem.

As I said, I think this problem persists even when the hashCode () / equals () methods are made case insensitive; however, the proposals made.

 ... INFO: HHH000232: Schema update complete Hibernate: insert into Company (name) values (?) Hibernate: select company0_.name as name1_0_0_ from Company company0_ where company0_.name=? Exception in thread "main" javax.persistence.RollbackException: Error while committing the transaction at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:94) at com.lambdaspace.Main.main(Main.java:24) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134) Caused by: javax.persistence.PersistenceException: org.hibernate.HibernateException: identifier of an instance of db.Company was altered from {Company.Identity [62109154] ACURA} to {Company.Identity [63094242] Acura} at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677) at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:82) ... 6 more Caused by: org.hibernate.HibernateException: identifier of an instance of db.Company was altered from {Company.Identity [62109154] ACURA} to {Company.Identity [63094242] Acura} at org.hibernate.event.internal.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:80) at org.hibernate.event.internal.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:192) at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:152) at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:231) at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:102) at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:55) at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222) at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425) at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101) at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177) at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:77) ... 6 more 
+6
source share
2 answers

The reason for this error is due to a change in the identity of the managed entity.

During the PersistenceContext life cycle, there can be one and only one managed instance of any object. You cannot change the existing managed entity identifier for this.

In your example, even if you are starting a new transaction, you should remember that PersistenContext was not closed, so you still have the c1 managed entity attached to the Hibernate session.

When you are trying to find a company:

 c1 = em.find (Company.class, new Company.Identity("ACURA")); 

The identifier does not match the identifier for the Company, which is attached to the current session, so a request is issued:

 Hibernate: select company0_.name as name1_0_0_ from Company company0_ where company0_.name=? 

Since SQL is CASE INSENSITIVE, you practically select the same database row as the current managed company (persistent c1 ).

But you can only have one managed entity for the same database row, so Hibernate will reuse the managed entity instance, but it will update the identifier to:

 new Company.Identity("ACURA"); 

You can verify these assumptions with the following test:

 String oldId = c1.name; Company c2 = em.find (Company.class, new Company.Identity("ACURA")); assertSame(c1, c2); assertFalse(oldId.equals(c2.name)); 

When the second transaction is completed, the flash will try to update the object identifier (which has changed from "Acura" to "ACURA"), and therefore DefaultFlushEntityEventListener.checkId () does not work.

According to JavaDoc, this check is intended for:

make (ing) sure () user did not distort identifier

To fix this, you need to remove this call to the search method:

 c1 = em.find (Company.class, new Company.Identity("ACURA")); 

You can check that c1 already connected:

 assertTrue(em.contains(c1)); 
+7
source

you seem to manually assign identifiers to your permanent object, which is managed by the JPA itself, and you are trying to change the identifier that already exists with this object, which is not valid.

  Company c1 = new Company ("Acura"); em.persist(c1); em.getTransaction().commit(); em.getTransaction().begin(); c1 = em.find (Company.class, new Company.Identity("ACURA")); 

In the above code snippet, you tried to change "ACURA" to "Acura", which seems to be the main reason. and you use the same instance of c1 as the object with different identifiers, that is, 1 with "ACURA" and the second with "Acura".

0
source

Source: https://habr.com/ru/post/981749/


All Articles