I have a JPA 2 web application (Struts 2, Hibernate 4 as JPA implementation only).
The current requirement is to add a (non-id) numeric sequential field, filled for certain rows only, to an existing entity. When inserting a new row, based on a certain condition, I need to set the new field to its highest value + 1 or to NULL.
For example:
ID NEW_FIELD DESCRIPTION -------------------------------- 1 1 bla bla 2 bla bla <--- unmatched: not needed here 3 bla bla <--- unmatched: not needed here 4 2 bla bla 5 3 bla bla 6 4 bla bla 7 bla bla <--- unmatched: not needed here 8 5 bla bla 9 bla bla <--- unmatched: not needed here 10 6 bla bla In the good old SQL, it would be something like:
INSERT INTO myTable ( id, new_field, description ) VALUES ( myIdSequence.nextVal, (CASE myCondition WHEN true THEN myNewFieldSequence.nextVal ELSE NULL END), 'Lorem Ipsum and so on....' ) But I've no clue on how to achieve it with JPA 2.
I know I can define callbacks methods, but JSR-000317 Persistence Specification for Eval 2.0 Eval discourages some specific operations from inside it:
3.5 Entity Listeners and Callback Methods
- Lifecycle callbacks can invoke JNDI, JDBC, JMS, and enterprise beans.
- In general, the lifecycle method of a portable application should not invoke EntityManager or Query operations, access other entity instances, or modify relationships within the same persistence context.[43] A lifecycle callback method may modify the non-relationship state of the entity on which it is invoked.[43] The semantics of such operations may be standardized in a future release of this specification.
Summarizing, yes to JDBC (!) and EJB, no to EntityManager and other Entities.
#EDIT
I'm trying to achieve the solution described in the answer from @anttix, but I'm encoutering some problem, so please correct me where I'm wrong.
Table
MyTable ------------------------- ID number (PK) NEW_FIELD number DESCRIPTION text Main Entity
@Entity @Table(name="MyTable") public class MyEntity implements Serializable { @Id @SequenceGenerator(name="seq_id", sequenceName="seq_id", allocationSize=1) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_id") private Long id; @OneToOne(cascade= CascadeType.PERSIST) private FooSequence newField; private String description /* Getters and Setters */ } Sub entity
@Entity public class FooSequence { @Id @SequenceGenerator(name="seq_foo", sequenceName="seq_foo", allocationSize=1) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_foo") private Long value; /* Getter and Setter */ } DAO
myEntity.setNewField(new FooSequence()); entityManager.persist(myEntity); Exception
Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
[...]
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: ERROR: relation "new_field" does not exist
[...]
Caused by: org.hibernate.exception.SQLGrammarException: ERROR: relation "new_field" does not exist
[...]
Caused by: org.postgresql.util.PSQLException: ERROR: relation "new_field" does not exist
What am I doing wrong ? I'm pretty new to JPA 2 and I've never used an entity not associated to a physical table... this approach is totally new to me.
I guess I need to put the @Column definition somewhere: how could JPA possibly know that the newField column (mapped through ImprovedNamingStrategy to new_field on the database) is retrieved through the value property of the FooSequence entity ?
Some pieces of the puzzle are missing.
EDIT
As asked in comments, this is the persistence.xml:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.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_2_0.xsd"> <persistence-unit name="MyService" transaction-type="JTA"> <jta-data-source>java:jboss/datasources/myDS</jta-data-source> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" /> <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/> <property name="hibernate.query.substitutions" value="true 'Y', false 'N'"/> <property name="hibernate.show_sql" value="true" /> <property name="format_sql" value="true" /> <property name="use_sql_comments" value="true" /> </properties> </persistence-unit> </persistence>