-->
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.  [ 17 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: AutoIncrement doesn't work on column if in a composite id
PostPosted: Wed Aug 01, 2007 8:23 am 
Beginner
Beginner

Joined: Fri Apr 20, 2007 10:48 am
Posts: 49
Location: France
Hibernate version: 3.2

Mapping documents:

Code between sessionFactory.openSession() and session.close():
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();
      }
   }
}



Full stack trace of any exception that occurs:
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)


Name and version of the database you are using:Derby 10.2.2.0

The generated SQL (show_sql=true):
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
persisting première
2007-08-01 13:16:27,406 [main] DEBUG org.hibernate.SQL.log(401) - insert into Person (startDate, endDate, note, lastOccurence, id, revision) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into Person (startDate, endDate, note, lastOccurence, id, revision) values (?, ?, ?, ?, ?, ?)



Hi Gurus ;-),

I have a strange behaviour on a field when it becomes a composite id:
I've tried to declare the field as an id at first, insertions work fine and values are auto-incremented as they should. As soon as I make the same field a part of a composite id, no incremention is made anymore. Here's my case (sorry for being verbose, I'm trying my best to be short)

I need to have a history of the evolution of an item, I opted to make every item have its id, and a revision number. To make things automatic I've decided to have the revision number auto-incremented.

So this is my implementation:

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

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>


When you execute the code of JPAMain twice, (having hibernate.hbm2ddl.auto unset) you will get the exception. Even a more strange thing: the sql insert contains revision as if it doesn't know it's an automatic field.

Thanks in advance for your help ;-)

Note: if your problem is different you may be interested in these posts:
http://forum.hibernate.org/viewtopic.php?t=978022
http://forum.hibernate.org/viewtopic.php?t=970843

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 01, 2007 9:53 am 
Expert
Expert

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

@GeneratedValue simply does not work with composite keys. This makes some sense. By specifying a composite key across 2 fields you're saying that each field in itself is not unique but the combination of the 2 fields is unique and can be used to identify the object. If you include a generated value in a composite key it no longer needs to be composite because the generated value alone is enough to uniquely identify the object.

Additionally, a composite between id and revision, with an associated rule that the revision should increment on update doesn't allow objects to be truely updated. You'd have to create a new row in the database for each update. This would effectively create a new object. Any relationships from other classes to your object would remain pointed at the first revision.

Using an increment column for revision might also be confusing when looking at the history. if you create Person A then B, then update A then B the revision numbers of A would be 1,3 and B 2,4.

Is this what you're trying to achieve...
When an object is created it gets an automatically assigned id and a revision number of 0.
When the object is updated the revision number is incremented and the new data saved but a record of the original data in revision 0 stored somewhere.
When an object is deleted a copy of the deleted data is stored somewhere.

Mike


Top
 Profile  
 
 Post subject: not exactly
PostPosted: Wed Aug 01, 2007 10:33 am 
Beginner
Beginner

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

Thans for the fast reply and sorry fot my latent one, I didn't see you answered (thought I'll get an email notification, as I told you i'm new to this forum)

What I'm trying to do is to have a history storing mecanism.

I imagined the solution could work this way, let's say personId is composed of id and revision:
    1. a new person is inserted, it's given a new id and a revison of 1
    2. the person is modified, at the update phase, it's intercepted and a new entry is created with a revision number of 2 and the old values, then the new values are stored in the same row: revison 1. (that's how all pointing objects will remain linked for cascading)


I know this is counter intuitive as the revision number will grow in a furtive manner, but I have timestamps that will say more about each entry, the revision field is there only to allow me to have one id per item: this is practical when I need to have all history (rather than having a parent child relationship).

So now that I know I'll never get my value incremented automatically, and that Derby doesn't supprt sequences, I know I'm really lucky :-). I still have a possibility to get rid of this hell:

if i have the possibility to get the next value from a table generator, I'm saved and dry.

Do you know sth about how it works? I've tried to decrypt this mecanism vainly. But I saw a call like 'values ...' that reminds me sequences. Do you know how to get the next value?

Antoher problem is that the table generator is not even created, do I have to put the annotation at the class level?

Thanks Mike again, you saved me time by explaining why Hibernate considers it should not increment, even if I thougth we can use generated values for any field (beside ids).

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject: any idea?
PostPosted: Wed Aug 01, 2007 11:13 am 
Beginner
Beginner

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

Do you think I could resolve the needs in another way than with the compound key? (and the parent-child relationship to revisions)

If you have another solution I'll take it gladely :-)

Regards,
Zied

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject: yes
PostPosted: Wed Aug 01, 2007 11:18 am 
Beginner
Beginner

Joined: Fri Apr 20, 2007 10:48 am
Posts: 49
Location: France
Is this what you're trying to achieve...
When an object is created it gets an automatically assigned id and a revision number of 0.
When the object is updated the revision number is incremented and the new data saved but a record of the original data in revision 0 stored somewhere.
When an object is deleted a copy of the deleted data is stored somewhere.


Yes it's exactly what I'm trying to do. (Sorry I didn't undersant it's a questio at the first reading)

Regards,
Zied

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject: did I undersant your question?
PostPosted: Wed Aug 01, 2007 11:54 am 
Beginner
Beginner

Joined: Fri Apr 20, 2007 10:48 am
Posts: 49
Location: France
Did you suggest me to do the incrementals in java to avoid having A[1,3] and B[2,4]?

Again I didn't see you're suggesting that, I'll be ok if there's a way to lock the table from JPA...

_________________
Regards,
Zied Hamdi


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

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
Sorry Zied I know nothing about table generators - never used one before.

This kind of auditing is quite a common requirement so there might be something out there already. Actually, there's a good article in the community area about audit logging that might be useful:
http://www.hibernate.org/318.html

Probably won't meet your needs exactly but might be a useful starter or reference.

With the increased usage of spring-framework there might also be AOP style systems available.

Over a year ago I had a similar requirement on a proof of concept project. I wanted to keep a copy of the original data, in a separate table, in such a form that I could restore a previous revision if necessary. I hacked something together but it wasn't very elegant and required some code duplication and extra code in the DAOs to do the copies. Perhaps I'll post a sample here - someone could no doubt improve it.

I'm sure people will be interested in this topic so please keep us updated on your progress.

Mike


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 01, 2007 12:16 pm 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
Quote:
Did you suggest me to do the incrementals in java to avoid having A[1,3] and B[2,4]?

Yes, but only if you care that versions numbers only go up by 1 each time. From your posts it sounds like that isn't a concern so a table generator would be fine.


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

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

I've sent a mail to emmanuel from hibernate (emmanuel@hibernate.org), hope he will be interested by the topic too :-), I think I'll really increment my revision in java and if two persons attempt to insert the same revision I'll do a catch with a corresponding message.

I thought about saving the history in another table, but I asked my self: why should I duplicate the database structure? my objective is to allow any object to be "archivable" just by extending the Archivable class, this way, if a person has a list of Phone, and Phone extends Archivable, changes on a Phone will be saved through Person saving: and this will trigger the old list archiving too (insertions with the same revision number as Person for the archive). That's why I wanted revision to be managed from the database side.

I'm talking but I still have nothing done. I'll keep you informed... :-)

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject: continuation
PostPosted: Thu Aug 02, 2007 10:04 am 
Beginner
Beginner

Joined: Fri Apr 20, 2007 10:48 am
Posts: 49
Location: France
Hi there, I think I have a hibernate bug...

I continued on the same idea of making granular archiving of entities without relationships, this make the database smaller, and we can have data integrity on all elements where revision=0. For other elements the revision number will be ignored, and data will be retreived agains timestamp conditions.

So what I've done today is an attempt to validate the feasability of the pattern. steps are the following:
    *Get the old version of the element to modify (which normally has a revision=0, checks are not implemented today)
    *Set each relationship (OneToOne, OneToMany etc...) to null so it's saved without cascading
    *Insert this nude old version in a new row
    *And finally Update the row


The routine calls are made in an interceptor @PreUpdate on Archivable instances (So subclasses are handled automatically).

Giving it a try now leads me to

Code:
2007-08-02 15:20:03,140 [main] DEBUG org.hibernate.SQL.log(401) - insert into Person (startDate, endDate, note, lastOccurence, id, revision) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into Person (startDate, endDate, note, lastOccurence, id, revision) values (?, ?, ?, ?, ?, ?)
2007-08-02 15:20:03,140 [main] DEBUG org.hibernate.SQL.log(401) - update Person set startDate=?, endDate=?, note=?, lastOccurence=? where id=? and revision=?
Hibernate: update Person set startDate=?, endDate=?, note=?, lastOccurence=? where id=? and revision=?
2007-08-02 15:20:03,171 [main] ERROR org.hibernate.AssertionFailure.<init>(22) - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: possible nonthreadsafe access to session
   at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:107)
   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:142)
   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)
   at main.JPAMain.save(JPAMain.java:46)
   at main.JPAMain.main(JPAMain.java:19)
javax.persistence.RollbackException: Error while commiting the transaction
   at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71)
   at main.JPAMain.save(JPAMain.java:46)
   at main.JPAMain.main(JPAMain.java:19)
Caused by: org.hibernate.AssertionFailure: possible nonthreadsafe access to session
   at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:107)
   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:142)
   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)
   ... 2 more
2007-08-02 15:20:03,203 [main] INFO  org.hibernate.impl.SessionFactoryImpl.close(769) - closing
2007-08-02 15:20:03,203 [main] INFO  org.hibernate.connection.DriverManagerConnectionProvider.close(147) - cleaning up connection pool: jdbc:derby://localhost:1527/sample


This is my code:
ArchivableListener.java
Code:
package fr.into.tests.listeners.persistence;

import javax.persistence.EntityManager;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;

import main.JPAMain;
import fr.into.tests.Archivable;

public class ArchivableListener {

   @PrePersist
   public void persisting(Archivable archivable) {
      System.out.println( "persisting "+ archivable.getEntry().getNote() );
//      archivable.getEntry().setStartDate( new Date() );
   }
   @PreUpdate
   public void updating(Archivable archivable) {
      EntityManager em = getEntityManager();
      //must clear to avoid getting the same instance as archivable (from cache)
      em.clear();
      Archivable old = em.find( archivable.getClass(), archivable.getArchiveId() );
      //must clear to oblige hibernate to insert a new row (elwhere even calling persist attempts an update)
      em.clear();
      old.removeRelationships();
      old.setRevision( archivable.nextRevision() );
      System.out.println( "persisting old archive with new revision number: "+ old );
      em.persist( old );
      //no more clear needed: archivable and old are distinct instances.
   }
   
   protected EntityManager getEntityManager() {
      //FIXME must replace with injection after test validation
      return JPAMain.EM.get();
   }
   
   
}


Testing code JPAMain.java
Code:
package main;

import java.util.Date;

import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

import fr.into.tests.ArchiveId;
import fr.into.tests.Person;

public class JPAMain {
   public static ThreadLocal<EntityManager>   EM   = new ThreadLocal<EntityManager>();

   public static void main(String[] args) throws NamingException {
      save( false );
      save( true );
   }

   private static void save(boolean update) {
      EntityManagerFactory emf = Persistence.createEntityManagerFactory( "IntoJPA" );
      EntityManager em = emf.createEntityManager();

      try {
         EntityTransaction transaction = em.getTransaction();
         transaction.begin();
         Person zied;
         if( update )
            zied = em.find( Person.class, new ArchiveId( 1, 1 ) );
         else
            zied = new Person( new ArchiveId( 1, 1 ) );

         zied.getEntry().setNote(  update ? "second" : "first"  );
         zied.getEntry().setStartDate( new Date() );
         JPAMain.EM.set( em );
         
         if( update ) {
            System.out.println( "Updating person: " + zied );
            em.merge( zied );
         } else {
            System.out.println( "Inserting person: " + zied );
            em.persist( zied );
         }
         transaction.commit();
      } catch( Exception ex ) {
         ex.printStackTrace();
      } finally {
         em.close();
         emf.close();
      }
   }
}


What happens is that the call in hibernate:
Code:
getSession().getPersistenceContext().getEntry( instance );
returns null. (EntityUpdateAction.execute() : 107)

I'm saying to my self maybe it's because i call em.clear() in the interceptor. A sort of cutting the grasp under hibernate's feet ;-p... I'm obliged to do that because hibernate works still with its old code of (saveOrUpdate) that attempts to know if it has to do an update or an insert. This is absurde because with JPA we say explicitely we want to do an insert, and hibernate does an update instead (it throws an exception by the way).

This is what happens if I don't call clear() :

Code:
      
// disabling causes update on persist call
// em.clear();
old.removeRelationships();
old.setRevision( archivable.nextRevision() );
em.persist( old );


Even if I change the id, the object is still cached (by reference) in the session, and Hibernate does a set of checking that ends up with the execution an update in place of an insert.

Anyone has a solution? I'm really getting stuck...

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 02, 2007 6:00 pm 
Expert
Expert

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

I'm still messing around with this. Today I've come across a big problem with using the EntityListener to implement the kind of scheme you want i.e. storing a copy of the object in the same table. It seems really obvious now its happened but never occurred to me before... Persisting a copy of your object in the listener's PrePersist handler causes a stack overflow because the listener gets called again to create a copy of the copy etc.

I'll have to have a rethink.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 02, 2007 8:23 pm 
Beginner
Beginner

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

I don't listen to PrePersist but to PreUpdate.

Regards,
Zied

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject: bug report
PostPosted: Fri Aug 03, 2007 4:49 am 
Beginner
Beginner

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

I've submitted a bug, you can have all the involved code in it:
http://opensource.atlassian.com/project ... e/HHH-2765

maybe you can figure out what's going wrong...

Regards,
Zied

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 05, 2007 3:36 am 
Beginner
Beginner

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

You wrote:
>This kind of auditing is quite a common requirement so there might be something out there already. Actually, there's a good article in the community area about audit logging that might be useful:
http://www.hibernate.org/318.html

Sorry I took a look at it today only (before going to the beach :-p), I have forgotten you pointed me to that link. It doesn't really help my needs (since I don't need the delta but the real data at a given time (svn knows how to do that from deltas but it's a product in it self)), but I really appreciate the time (even if maybe little) you spent to give me this link. I've just added you a credit :-).

Have a nice week end.

_________________
Regards,
Zied Hamdi


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 07, 2007 1:42 pm 
Expert
Expert

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

I've just been reading the JPA spec regarding lifecycle callbacks. About callback methods the spec states:
Quote:
In general, portable applications should not invoke EntityManager or Query operations,
access other entity instances, or modify relationships in a lifecycle callback method

So I don't think this is a hibernate bug, rather an illegal use of the EntityManager. I suggest you close your critical JIRA issue..

Looks like JPA callbacks won't work for auditing which is a shame. Oracle toplink has a separate, vendor specific callback feature. There's an auditing example here:
http://tapestryofthoughts.blogspot.com/ ... gging.html
It even gives you easy access to the entity manager. Hibernate may have something similar.

Mike


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

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.