-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 
Author Message
 Post subject: JPA/Hibernate - @IdClass Produces 'Identifier ... Altered'
PostPosted: Fri Jan 30, 2015 6:09 pm 
Newbie

Joined: Fri Jan 30, 2015 5:52 pm
Posts: 2
How do I use @IdClass with a case-insensitive String primary key? I put 'Acura' into the DB, Hibernate creates Company.Identity with 'Acura' - subsequent finds with 'ACURA' fail.

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 a string identifier exists in the database and a query is performed with the same string except that the query string differs in case.

Here is the specific error:
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}

The full error log is below.

Here is the offending code. Start with an empty database:

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);
   
        }
    }


and here is the 'db.Company' implementation:

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 &&
                            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 using `@IdClass` isn't needed when there is a single primary key; the above is the simplest example of the problem. I've seen the problem in a 'real' application involving multiple identifiers.

I have tried to make the hashCode()/equals() methods in Company.Identifier case insensitive - including adding a setName(String name) that forces the provided name to lower case. Somehow, Hibernate is avoiding that method, preferring to assign directly as `this.name =`

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


Top
 Profile  
 
 Post subject: Re: JPA/Hibernate - @IdClass Produces 'Identifier ... Altered'
PostPosted: Thu Feb 05, 2015 1:04 pm 
Newbie

Joined: Fri Jan 30, 2015 5:52 pm
Posts: 2
Anybody? It is a simple enough example that fails with an exception.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.