Enterprise Java Development@TOPIC@
Every entity must have a primary key
Primary keys must be unique
Map to one ("simple") or more ("composite") properties
Properties must be of type
Java primitive types -- including object wrappers
java.lang.String
Custom classes made up of legal property types
Persistence providers required to provide primary key generation
Specific type of generator specified through a strategy
Figure 48.1. Specifying Primary Key Generation thru Annotations
@Entity
@Table(name="ORMCORE_DRILL")
public class Drill {
@Id
@GeneratedValue( //AUTO is the default and could be left off here
strategy=GenerationType.AUTO)
private long id; //unassigned PK value must be zero
private String make;
...
Figure 48.2. Specifying Primary Key Generation thru orm.xml
<entity class="ejava.examples.orm.core.mapped.Drill" access="FIELD">
<table name="ORMCORE_DRILL"/>
<attributes>
<id name="id">
<generated-value strategy="AUTO"/>
</id>
</attributes>
</entity>
Three (+1) Types
SEQUENCE
Database generates unique value from a global sequence
IDENTITY
Database generates unique value on a per-table basis
TABLE
AUTO
Provider may choose any technique, including one not specified above
Figure 48.3. Entity with GenerationType.AUTO
@Entity
@Table(name="ORMCORE_DRILL")
public class Drill {
@Id
@GeneratedValue
private long id;
private String make;
...
Figure 48.4. AUTO (Success) Test
@Test
public void testAUTOGood() {
log.info("testAUTOGood");
//note that since PKs are generated, we must pass in an object that
//has not yet been assigned a PK value.
ejava.examples.orm.core.annotated.Drill drill = new Drill(0);
drill.setMake("acme");
//insert a row in the database
em.persist(drill);
log.info("created drill:" + drill);
assertFalse(drill.getId() == 0L);
}
-testAUTOGood Hibernate: insert into ORMCORE_DRILL (id, make) values (null, ?) -created drill:ejava.examples.orm.core.annotated.Drill@35853853, id=1, make=acme
Figure 48.5. AUTO (Failure) Test
@Test
public void testAUTOBad() {
log.info("testAUTOBad");
//he's not going to like they non-zero PK value here
ejava.examples.orm.core.annotated.Drill drill = new Drill(25L);
drill.setMake("BD");
//insert a row in the database
boolean exceptionThrown = false;
try {
assertFalse(drill.getId() == 0L);
log.info("trying to create drill with pre-exist pk:" + drill);
em.persist(drill);
}
catch (PersistenceException ex) {
log.info("got expected exception: " + ex);
exceptionThrown = true;
}
assertTrue(exceptionThrown);
}
-testAUTOBad -trying to create drill with pre-exist pk:ejava.examples.orm.core.annotated.Drill@76160af2, id=25, make=BD -got expected exception: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: ejava.examples.orm.core.annotated.Drill
Figure 48.6. Entity with GenerationType.IDENTITY
@Entity
@Table(name="ORMCORE_GADGET")
public class Gadget {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String make;
Figure 48.7. IDENTITY Database Schema
create table ORMCORE_GADGET ( id bigint generated by default as identity, make varchar(255), primary key (id) )
Figure 48.8. IDENTITY Test
@Test
public void testIDENTITY() {
ejava.examples.orm.core.annotated.Gadget gadget = new Gadget(0);
gadget.setMake("gizmo 1");
//insert a row in the database
em.persist(gadget);
log.info("created gadget (before flush):" + gadget);
em.flush();
log.info("created gadget (after flush):" + gadget);
assertFalse(gadget.getId() == 0L);
}
-testIDENTITY Hibernate: insert into ORMCORE_GADGET (id, make) values (null, ?) -created gadget (before flush):ejava.examples.orm.core.annotated.Gadget@7b61257e, id=1, make=gizmo 1 -created gadget (after flush):ejava.examples.orm.core.annotated.Gadget@7b61257e, id=1, make=gizmo 1
Figure 48.9. Follow-on IDENTITY Allocations
Hibernate: insert into ORMCORE_GADGET (id, make) values (null, ?) -created gadget:ejava.examples.orm.core.annotated.Gadget@581e495d, id=2, make=gizmo 2 Hibernate: insert into ORMCORE_GADGET (id, make) values (null, ?) -created gadget:ejava.examples.orm.core.annotated.Gadget@1f06d526, id=3, make=gizmo 3 Hibernate: insert into ORMCORE_GADGET (id, make) values (null, ?) -created gadget:ejava.examples.orm.core.annotated.Gadget@199bdabd, id=4, make=gizmo 4 ...
The provider must obtain the next primary key value from the database each time. Notice in the first case above -- the provider has already flushed the INSERT to the database during the persist and before our manual call to flush()
Figure 48.10. Entity with GenerationType.SEQUENCE
@Entity
@Table(name="ORMCORE_FAN")
@SequenceGenerator(
name="fanSequence", //required logical name
sequenceName="FAN_SEQ", //name in database
initialValue=4, //start with something odd to be noticeable
allocationSize=3) //number of IDs to internally assign per-sequence value
public class Fan {
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, //use DB sequence
generator="fanSequence") //point to logical def
private long id;
private String make;
...
Figure 48.11. SEQUENCE Database Schema
create table ORMCORE_FAN ( id bigint not null, make varchar(255), primary key (id) ) create sequence FAN_SEQ
Figure 48.12. SEQUENCE Test
@Test
public void testSEQUENCE() {
log.info("testSEQUENCE");
//note that since PKs are generated, we must pass in an object that
//has not yet been assigned a PK value.
ejava.examples.orm.core.annotated.Fan fan = new Fan(0);
fan.setMake("cool runner 1");
//insert a row in the database
em.persist(fan);
log.info("created fan (before flush):" + fan);
em.flush();
log.info("created fan (after flush):" + fan);
assertFalse(fan.getId() == 0L);
}
-testSEQUENCE Hibernate: call next value for FAN_SEQ -created fan (before flush):ejava.examples.orm.core.annotated.Fan@e18a174, id=3, make=cool runner 1 Hibernate: insert into ORMCORE_FAN (make, id) values (?, ?) -created fan (after flush):ejava.examples.orm.core.annotated.Fan@e18a174, id=3, make=cool runner 1
Figure 48.13. Follow-on SEQUENCE Allocations
-created fan:ejava.examples.orm.core.annotated.Fan@6806da29, id=4, make=cool runner 2 -created fan:ejava.examples.orm.core.annotated.Fan@77d5a139, id=5, make=cool runner 3 - call next value for FAN_SEQ -created fan:ejava.examples.orm.core.annotated.Fan@1c0cf528, id=6, make=cool runner 4 -created fan:ejava.examples.orm.core.annotated.Fan@27c3a4a3, id=7, make=cool runner 5 -created fan:ejava.examples.orm.core.annotated.Fan@17f7ed6e, id=8, make=cool runner 6 - call next value for FAN_SEQ ...
The provider generates allocationSize primary key values before performing a flush or follow-on poll of the sequence. An allocationSize greater than one (1) is much less communication with the database than the IDENTITY strategy -- which would be somewhat analogous to a SEQUENCE allocationSize=1.
Figure 48.14. Entity with GenerationType.TABLE
@Entity
@Table(name="ORMCORE_EGGBEATER")
@TableGenerator( //note that all but name are optional if generating schema
name="eggbeaterGenerator", //logical name of generator
table="ORMCORE_EB_UID", //name of table storing seq
pkColumnName="UID_ID", //pk column for seq table
pkColumnValue="ORMCORE_EGGBEATER", //pk value in pk column
valueColumnName="UID_VAL", //column for seq value
allocationSize=5 //increment UID_ID after using this many
)
public class EggBeater {
@Id
@GeneratedValue(strategy=GenerationType.TABLE, //use DB table
generator="eggbeaterGenerator") //point to logical def
private long id;
private String make;
...
Figure 48.15. TABLE Database Schema
create table ORMCORE_EGGBEATER ( id bigint not null, make varchar(255), primary key (id) ) create table ORMCORE_EB_UID ( UID_ID varchar(255), UID_VAL integer )
Figure 48.16. TABLE Test
@Test
public void testTABLE() {
log.info("testTABLE");
log.debug("table id before=" + getTableId());
//note that since PKs are generated, we must pass in an object that
//has not yet been assigned a PK value.
ejava.examples.orm.core.annotated.EggBeater eggbeater = new EggBeater(0);
eggbeater.setMake("done right");
//insert a row in the database
em.persist(eggbeater);
log.info("created eggbeater (before flush):" + eggbeater);
em.flush();
log.info("created eggbeater (after flush):" + eggbeater);
assertFalse(eggbeater.getId() == 0L);
}
-testTABLE Hibernate: select UID_VAL from ORMCORE_EB_UID where UID_ID='ORMCORE_EGGBEATER' -table id before=null Hibernate: select UID_VAL from ORMCORE_EB_UID where UID_ID = 'ORMCORE_EGGBEATER' for update Hibernate: insert into ORMCORE_EB_UID (UID_ID, UID_VAL) values ('ORMCORE_EGGBEATER', ?) Hibernate: update ORMCORE_EB_UID set UID_VAL = ? where UID_VAL = ? and UID_ID = 'ORMCORE_EGGBEATER' -created eggbeater (before flush):ejava.examples.orm.core.annotated.EggBeater@4eb8b5a9, id=1, make=done right Hibernate: insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -created eggbeater (after flush):ejava.examples.orm.core.annotated.EggBeater@4eb8b5a9, id=1, make=done right
Figure 48.17. Follow-on TABLE Allocations
-table id after=1 -created ehhbeater:ejava.examples.orm.core.annotated.EggBeater@3576465f, id=2, make=null -table id after[2]=1 -created ehhbeater:ejava.examples.orm.core.annotated.EggBeater@306435cd, id=3, make=null -table id after[3]=1 -created ehhbeater:ejava.examples.orm.core.annotated.EggBeater@35194a50, id=4, make=null -table id after[4]=1 Hibernate: select UID_VAL from ORMCORE_EB_UID where UID_ID = 'ORMCORE_EGGBEATER' for update Hibernate: update ORMCORE_EB_UID set UID_VAL = ? where UID_VAL = ? and UID_ID = 'ORMCORE_EGGBEATER' -created ehhbeater:ejava.examples.orm.core.annotated.EggBeater@288c819b, id=5, make=null -table id after[5]=2 -created ehhbeater:ejava.examples.orm.core.annotated.EggBeater@10508cb2, id=6, make=null -table id after[6]=2 -created ehhbeater:ejava.examples.orm.core.annotated.EggBeater@49250068, id=7, make=null -table id after[7]=2 -created ehhbeater:ejava.examples.orm.core.annotated.EggBeater@372b2a85, id=8, make=null -table id after[8]=2 -created ehhbeater:ejava.examples.orm.core.annotated.EggBeater@5e69cd5e, id=9, make=null -table id after[9]=2 Hibernate: select UID_VAL from ORMCORE_EB_UID where UID_ID = 'ORMCORE_EGGBEATER' for update Hibernate: update ORMCORE_EB_UID set UID_VAL = ? where UID_VAL = ? and UID_ID = 'ORMCORE_EGGBEATER' -created ehhbeater:ejava.examples.orm.core.annotated.EggBeater@1dbf9510, id=10, make=null -table id after[10]=3 ...
As will SEQUENCE, the TABLE strategy allows each client to generate an allocationSize amount of primary key values before requiring a flush of the current batch or polling for a new table value.