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 :-)