-->
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.  [ 14 posts ] 
Author Message
 Post subject: Problem with mutiple comlumns PK
PostPosted: Sat Jul 28, 2007 8:50 am 
Beginner
Beginner

Joined: Fri Jun 15, 2007 6:32 am
Posts: 23
Code:
   @Id
   @Column(name="TIME")
   public Timestamp getTime() {return time;}
   public void setTime(Timestamp time) {this.time = time;}
   
   @Id
   @Column(name="PAPERID")
   public String getPaperId() {return paperId;}
   public void setPaperId(String paperId) {this.paperId = paperId;}


getting
Code:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session

and there's no double mapping actualy being made, it falls on the second line added and here it is :
Code:
2007-04-29 09:45:00.046 TA25 1069.58 0.0
2007-04-29 09:46:00.093 TA25 1069.61 34.282


mapped in DB as two columns PK not that it even gets that far ...


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 28, 2007 9:44 am 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
Have you created and referenced a separate class for your composite primary key? You can't create a composite key by just putting @Id against more than one property of your class.

What's probably happening is hibernate is ignoring the Time field and just using PageId as the key. When you load 2 instances with the same PageId you'll get this exception.

Check the annotations docs.

You'll probably want something like this:

Paper.java
Code:
@Entity
@IdClass(PaperPk.class)
class Paper {
   @Id
   public Timestamp getTime() {return time;}
   public void setTime(Timestamp time) {this.time = time;}
   
   @Id
   public String getPaperId() {return paperId;}
   public void setPaperId(String paperId) {this.paperId = paperId;}
}


PaperPk.java
Code:
@Embeddable
public class PaperPk implements Serializable {

   private Timestamp time;
   private String paperId;
   
   @Column(name="PAPERID")
   public String getPaperId() {
      return paperId;
   }
   public void setPaperId(String paperId) {
      this.paperId = paperId;
   }

   @Column(name="TIME")
   public Timestamp getTime() {
      return time;
   }
   public void setTime(Timestamp time) {
      this.time = time;
   }

   public int hashCode() {
      return time.hashCode() + paperId.hashCode();
   }
   
   public boolean equals(Object other) {
      if (other == null || !(other instanceof PaperPk)) {
         return false;
      }
      PaperPk otherPk = (PaperPk)other;
      return (time.equals(otherPk.time) && paperId.equals(otherPk.paperId));
   }
}


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 28, 2007 9:56 am 
Beginner
Beginner

Joined: Fri Jun 15, 2007 6:32 am
Posts: 23
Code:
import java.io.Serializable;
import java.sql.Timestamp;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.AccessType;


@Entity
@AccessType("property")
@Table(name = "100KSYSTEM")
public class KSystem {

   
   @Embeddable
   public class KSystemmPK implements Serializable{
      public String paperId;
      Timestamp time;
      
      @Column(name="TIME")
      public Timestamp getTime() {return time;}
      public void setTime(Timestamp time) {this.time = time;}
      
      @Column(name="PAPERID")
      public String getPaperId() {return paperId;}
      public void setPaperId(String paperId) {this.paperId = paperId;}
   }
   
   public KSystemmPK kSystemmPK;
   Timestamp time ;
   String paperId;
   double price,volume;
   
   public KSystem() {
      kSystemmPK = new KSystemmPK();
   }
   
   @Id
   public KSystemmPK getKSystemmPK() {return kSystemmPK;}
   public void setKSystemmPK(KSystemmPK systemmPK) {kSystemmPK = systemmPK;}
   
   @Column(name="PRICE")
   public double getPrice() {return price;}
   public void setPrice(double price) {this.price = price;}
   
   @Column(name="VOLUME")
   public double getVolume() {return volume;}
   public void setVolume(double volume) {this.volume = volume;}
   
   @Override
   public String toString() {
      return kSystemmPK.getTime()+" "+kSystemmPK.getPaperId()+" "+price+" "+volume;
   }
   
   
}



thanks mate , i've solved it by this way and it works (so far so good)
but i see that you've declared paper and time on both PK class and entity
why do that ? and is there a way that the pk's member will intialize the entity's member ?
becaus i have to do
KSystem k.get..PK().getTime(somevalue) ;
which doesn't seem right to me


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 28, 2007 10:07 am 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
As I said there are a few ways to specify the key. If you want the key fields directly accessible from the class (my example) i.e. getPaperId() rather than getPk().getPaperId() then use @IdClass(PaperPk.class) and declare methods in the class that mirror those of the PK.

Having the PK as a separate object is the alternative (your example) and probably more intuitive.

Don't forget to define equals and hashCode in your PK class.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 28, 2007 10:09 am 
Beginner
Beginner

Joined: Fri Jun 15, 2007 6:32 am
Posts: 23
thatmikewilliams wrote:
Don't forget to define equals and hashCode in your PK class.


why is that important ?


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 28, 2007 10:21 am 
Beginner
Beginner

Joined: Fri Jun 15, 2007 6:32 am
Posts: 23
i like your way better changed it , many thanks mate .


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 28, 2007 10:25 am 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
Otherwise 2 database objects with the same key would be considered as different objects in Java.

If the key was a simple Long then
k1.equals(k2) would be true when k1 and k2 had the same value.

With your new composite key, if k1 and k2 had the same time and paperId, k1.equals(k2) would be FALSE!

When you override equals you must always override hashCode such that if k1 equals k2 then k1.hashCode == k2.hashCode.


Top
 Profile  
 
 Post subject: Doesn't work when one of them has a generated value :-/
PostPosted: Wed Aug 01, 2007 7:22 am 
Beginner
Beginner

Joined: Fri Apr 20, 2007 10:48 am
Posts: 49
Location: France
Hi thatmikewilliams and kernel77,

I've spent a huge time on this task, I've stopped it read completely the docs then I retried and it doesn't work anyway:

I have a similar example as kernel77 except that I need my value to be generated (kernel77: in the docs I've read it's not recommended to use a timestamp as an id).

I need to have two id fields: one that identifies the item, and a second that tells in which revision we are (a sort of archiving mecanism).

after reading your posts I've made a last attempt, it doesn't work too. Here's my code

Archivable.java
Code:
@MappedSuperclass
@EntityListeners(ArchivableListener.class)
public class Archivable {
   protected ArchiveEntry entry;
   protected ArchiveId archiveId;

   
   
   public Archivable() {
      entry = new ArchiveEntry();
      archiveId = new ArchiveId();
   }

   @Embedded
   public ArchiveEntry getEntry() {
      return entry;
   }

   public void setEntry(ArchiveEntry entry) {
      this.entry = entry;
   }

   @EmbeddedId
   public ArchiveId getArchiveId() {
      return archiveId;
   }

   public void setArchiveId(ArchiveId archiveId) {
      this.archiveId = archiveId;
   }

}


ArchiveId.java
Code:
@Embeddable
public class ArchiveId implements Serializable{
   private static final long   serialVersionUID   = 1L;
   
   protected long   id;
   protected long   revision;

   public ArchiveId() {
   }

   public ArchiveId(long id, long revision) {
      this.id = id;
      this.revision = revision;
   }

   public long getId() {
      return this.id;
   }

   public void setId(long id) {
      this.id = id;
   }

   @GeneratedValue(strategy=IDENTITY)
   @Column(insertable=false, updatable = false)
   public long getRevision() {
      return revision;
   }

   public void setRevision(long revision) {
      this.revision = revision;
   }

   @Override
   public int hashCode() {
      int hash = 0;
      hash += this.id ^ revision;
      return hash;
   }

   @SuppressWarnings("unchecked")
   @Override
   public boolean equals(Object object) {
      if( !(object instanceof ArchiveId) ) {
         return false;
      }
      ArchiveId other = (ArchiveId) object;
      if( this.id != other.id )
         return false;
      if( this.revision != other.revision )
         return false;
      return true;
   }

}


ArchiveEntry.java
Code:
@Embeddable
public class ArchiveEntry implements Serializable {

   private static final long serialVersionUID = 1;

   protected Date startDate, endDate;
   protected String note;
   protected ArchiveEntry originalEntry;
   /**
    * true ifaoif this element is the last in all revisions (optimizes reading
    * from db)
    */
   protected boolean lastOccurence;


   @Temporal(TemporalType.TIMESTAMP)
   @Column(updatable=false)
   public Date getStartDate() {
      return startDate;
   }

   public void setStartDate(Date startDate) {
      this.startDate = startDate;
   }

   @Temporal(TemporalType.TIMESTAMP)
   public Date getEndDate() {
      return endDate;
   }

   public void setEndDate(Date endDate) {
      this.endDate = endDate;
   }

   public String getNote() {
      return note;
   }

   public void setNote(String note) {
      this.note = note;
   }

   public boolean isLastOccurence() {
      return lastOccurence;
   }

   public void setLastOccurence(boolean lastOccurence) {
      this.lastOccurence = lastOccurence;
   }

}


and finally the concrete class

Person.java
Code:
@Entity
public class Person extends Archivable {
}


When I try to enter an instance two times (by executing the main method twice) it doesn't work and throws a java.sql.SQLIntegrityConstraintViolationException

Here's my testing code:
Code:
public class JPAMain {

   public static void main(String[] args) throws NamingException {
      // InitialContext context = new InitialContext();
      // context.lookup("jdbc/intoDB_DS");
      EntityManagerFactory emf = Persistence
            .createEntityManagerFactory("IntoJPA");
      EntityManager em = emf.createEntityManager();

      try {
         EntityTransaction transaction = em.getTransaction();
         transaction.begin();
         Person zied = new Person();
         zied.getArchiveId().setId( 1 );
         zied.getEntry().setNote( "première" );
         zied.getEntry().setStartDate( new Date() );
         em.persist(zied);
//         zied = new Person();
//         zied.setId( 1 );
//         zied.setRevision( 0 );
//         zied.getEntry().setNote( "deuxième" );
//         zied.getEntry().setStartDate( new Date() );
//         em.persist(zied);         
         transaction.commit();
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         em.close();
         emf.close();
      }
   }
}


and the exception is the sam whether I use the @ClassId() or the @Embeddable class, it doesn't help if I use the @GeneratedValue(strategy=IDENTITY) or @GeneratedValue(strategy=TABLE) and move the ids to Archivable using this declaration for revision:
Code:
   @Id
   @GeneratedValue(strategy = TABLE, generator = "persistent_gen")
   @TableGenerator(name = "persistent_gen", table = "persistent_key_gen", pkColumnName = "seq_name",
         valueColumnName = "seq_value", pkColumnValue = "Persistence_seq", initialValue = 1, allocationSize = 1)


In all cases it works if I have a non composite id (revision), but as soon as I add the second field as an id I get this exception on the second insert

Code:
javax.persistence.RollbackException: Error while commiting the transaction
   at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71)
   at main.JPAMain.main(JPAMain.java:36)
Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [fr.into.tests.Person]
   at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
   at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
   at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2267)
   at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2660)
   at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:56)
   at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:250)
   at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:234)
   at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:141)
   at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
   at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
   at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
   at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
   at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
   at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:54)
   ... 1 more
Caused by: java.sql.SQLIntegrityConstraintViolationException: L'instruction a été abandonnée parce qu'elle aurait entraîné la duplication d'une valeur de clé dans une contrainte de clé ou d'index unique identifié par 'SQL070801004830000' définie sur 'PERSON'.
   at org.apache.derby.client.am.SQLExceptionFactory40.getSQLException(Unknown Source)
   at org.apache.derby.client.am.SqlException.getSQLException(Unknown Source)
   at org.apache.derby.client.am.PreparedStatement.executeUpdate(Unknown Source)
   at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:23)
   at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2247)
   ... 12 more
Caused by: org.apache.derby.client.am.SqlException: L'instruction a été abandonnée parce qu'elle aurait entraîné la duplication d'une valeur de clé dans une contrainte de clé ou d'index unique identifié par 'SQL070801004830000' définie sur 'PERSON'.
   at org.apache.derby.client.am.Statement.completeExecute(Unknown Source)
   at org.apache.derby.client.net.NetStatementReply.parseEXCSQLSTTreply(Unknown Source)
   at org.apache.derby.client.net.NetStatementReply.readExecute(Unknown Source)
   at org.apache.derby.client.net.StatementReply.readExecute(Unknown Source)
   at org.apache.derby.client.net.NetPreparedStatement.readExecute_(Unknown Source)
   at org.apache.derby.client.am.PreparedStatement.readExecute(Unknown Source)
   at org.apache.derby.client.am.PreparedStatement.flowExecute(Unknown Source)
   at org.apache.derby.client.am.PreparedStatement.executeUpdateX(Unknown Source)
   ... 15 more


Here's my log for the case of @Embeddable id when the database is generated:

Code:
2007-08-01 13:16:26,984 [main] INFO  org.hibernate.tool.hbm2ddl.SchemaExport.execute(154) - Running hbm2ddl schema export
2007-08-01 13:16:26,984 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport.execute(170) - import file not found: /import.sql
2007-08-01 13:16:26,984 [main] INFO  org.hibernate.tool.hbm2ddl.SchemaExport.execute(179) - exporting generated schema to database
2007-08-01 13:16:26,984 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport.execute(303) - drop table Person
2007-08-01 13:16:27,125 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport.execute(303) - create table Person (id bigint not null, revision bigint not null, startDate timestamp, endDate timestamp, note varchar(255), lastOccurence smallint not null, primary key (id, revision))
2007-08-01 13:16:27,312 [main] INFO  org.hibernate.tool.hbm2ddl.SchemaExport.execute(196) - schema export complete


and finally here's a ready to use with derby standalone persistence.xml file

Code:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
   xmlns="http://java.sun.com/xml/ns/persistence"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
   <persistence-unit name="IntoJPA">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <class>fr.into.tests.Archivable</class>
      <class>fr.into.tests.ArchiveEntry</class>
      <class>fr.into.tests.Person</class>
      <!-- <jta-data-source>jdbc/intoDB_DS</jta-data-source> -->
      <exclude-unlisted-classes>false</exclude-unlisted-classes>
      <properties>
         <property name="hibernate.dialect"
            value="org.hibernate.dialect.DerbyDialect" />
         <property name="hibernate.show_sql" value="true" />
         <property name="hibernate.hbm2ddl.auto" value="create" />

         <property name="hibernate.connection.driver_class"
            value="org.apache.derby.jdbc.ClientDriver" />
         <property name="hibernate.connection.username" value="app" />
         <property name="hibernate.connection.password" value="app" />
         <property name="hibernate.connection.url"
            value="jdbc:derby://localhost:1527/sample" />

         <property name="hibernate.show_sql"
            value="true" />
         <!-- <property name="hibernate.use_sql_comments"
            value="true" /> -->            
      </properties>
   </persistence-unit>
</persistence>


I'm really in trouble, help if you can please :-)

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 01, 2007 7:46 am 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
Hi Zied

Even though your problem seems related you should create a separate post for your problem. People are more likely to respond to a new post and you will be able to credit those who help solve your problem. You can't credit on this post because you aren't the original author.

Mike


Top
 Profile  
 
 Post subject: ok, sorry ;-)
PostPosted: Wed Aug 01, 2007 8:26 am 
Beginner
Beginner

Joined: Fri Apr 20, 2007 10:48 am
Posts: 49
Location: France
Hi Mike,

I'm new to this forum, I thought people can browse disussions by searching keywords like I did ex: "@IdClass @GeneratedValue" and the search engine finds the most related discussions, so I concluded it's a good idea to have what gets together in the same discussion. By the was I've sent another post (still in that same erratum logic) before I received your answer: http://forum.hibernate.org/viewtopic.php?t=970843

Sorry for that post too :-)

I've made a new post:
http://forum.hibernate.org/viewtopic.ph ... 15#2360915

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject: Re: ok, sorry ;-)
PostPosted: Wed Aug 01, 2007 12:07 pm 
Beginner
Beginner

Joined: Fri Jun 15, 2007 6:32 am
Posts: 23
zhamdi wrote:
(kernel77: in the docs I've read it's not recommended to use a timestamp as an id).


would appreciate a link


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 01, 2007 12:14 pm 
Beginner
Beginner

Joined: Fri Apr 20, 2007 10:48 am
Posts: 49
Location: France
Hi kernel,

in the book
http://www.manning.com/panda/

chapter 7.2.3 Specifying entity identity:
Code:
For example, let’s assume that we are
using float data as the identity, and specify 103.789 and 103.787 as the identity
values for two separate entity instances. If the database rounds these values to
two-digit decimal precision before storing the record, both of these values would
Licensed to Luciano Fiandesio <lfiandesio@yahoo.com>
Implementing domain objects with JPA 235
map to 103.79 and we would have a primary key violation! Another type you
should avoid choosing as identifier is TimeStamp.


I hope I didn't violate some rights with this paragraph :-)

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 01, 2007 12:15 pm 
Beginner
Beginner

Joined: Fri Apr 20, 2007 10:48 am
Posts: 49
Location: France
Hi kernel,

in the book
http://www.manning.com/panda/

chapter 7.2.3 Specifying entity identity:
Code:
For example, let’s assume that we are
using float data as the identity, and specify 103.789 and 103.787 as the identity
values for two separate entity instances. If the database rounds these values to
two-digit decimal precision before storing the record, both of these values would

map to 103.79 and we would have a primary key violation! Another type you
should avoid choosing as identifier is TimeStamp.


I hope I didn't violate some rights with this paragraph :-)

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 01, 2007 12:50 pm 
Beginner
Beginner

Joined: Fri Jun 15, 2007 6:32 am
Posts: 23
thanks mate , the q. is why would a DB do something like that data integrity is kind'a of a must have isn't it ? :)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 14 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.