title: @IdClass Produces 'Identifier of an Instance was Altered' with JPA and Hibernate
body: <p>For a JPA entity model using a case-insensitive database schema, when I use a @IdClass annotation I consistently get 'identifier of an instance was altered' exception.  For an object with a 'string' primary key, the error occurs when an string of one case exists in the database and a query is performed with the same string differing only in case.</p>
  
  <p>I've looked at other SO answers and they are of the form: a) don't modify the primary key (I'm not) and b) your equals()/hashCode() implementations are flawed.  For 'b' I've tried using <code>toLowerCase()</code> and <code>equalsIgnoringCase()</code> but to no avail.  [Additionally, it seems the Hibernate code is directly setting properties, rather than calling property setters when the 'altering' occurs.]</p>
  
  <p>Here is the specific error:</p>
  
  <pre><code>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}
  </code></pre>
  
  <p><em>Q: For a case insensitive DB containing a company 'Acura' (as primary key), using @IdClass how do I subsequently find other capitalizations?</em></p>
  
  <p>Here is the offending code (starting with an empty database):</p>
  
  <pre><code>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);    
      }
  }
  </code></pre>
  
  <p>and here is the 'db.Company' implementation:</p>
  
  <pre><code>@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 &amp;&amp;
                          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 &amp;&amp;
                          this.name.equals(((Identity)that).name));
          }
  
          @Override
          public String toString () {
              return "{Company.Identity [" + hashCode() + "] " + name + "}";
          }
      }
  }
  </code></pre>
  
  <p>Note: I know using <code>@IdClass</code> isn't needed when there is a single primary key; the above is the simplest example of the problem.</p>
  
  <p>As I said, I believe this problem persists even when the hashCode()/equals() methods are made case insensitive; however, suggestions taken.</p>
  
  <pre><code>...
  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
  </code></pre>