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
All providers required to provide a default identity strategy
Can be specifically referenced using "strategy=GenerationType.AUTO"
Figure 48.3. GeneratedValue.strategy default
package javax.persistence;
...
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface GeneratedValue {
/**
* (Optional) The primary key generation strategy
* that the persistence provider must use to
* generate the annotated entity primary key.
*/
GenerationType strategy() default AUTO;
Figure 48.4. Entity with Default GenerationType (AUTO)
@Entity
@Table(name="ORMCORE_DRILL")
public class Drill {
@Id
@GeneratedValue
private long id;
private String make;
...
Figure 48.5. AUTO (Success) Test
Test
logger.info("testAUTOGood");
//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
logger.info("just before persist(tx={}): {}", txActive(), drill);
em.persist(drill);
logger.info("created drill (after persist and before flush, tx={}): {}", txActive(), drill);
em.flush();
logger.info("created drill (after flush, tx={}): {}", txActive(), drill);
assertNotEquals(0, drill.getId());
Output
-testAUTOGood -just before persist(tx=true): 1171672359, id=0, make=acme -call next value for hibernate_sequence -created drill (after persist and before flush, tx=true): 1171672359, id=1, make=acme -insert into ORMCORE_DRILL (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [acme] -binding parameter [2] as [BIGINT] - [1] -created drill (after flush, tx=true): 1171672359, id=1, make=acme
AUTO in this case meant SEQUENCE -- more details in sequence section
Figure 48.6. AUTO (Failure) Test
Test
logger.info("testAUTOBad");
//provider will not like the non-zero PK value here
//because we told it to generate the PK
ejava.examples.orm.core.annotated.Drill drill = new Drill(25L);
drill.setMake("BD");
//insert a row in the database
boolean exceptionThrown = false;
try {
assertNotEquals(0, drill.getId());
logger.info("trying to create drill with pre-exist pk: {}", drill);
em.persist(drill);
}
catch (PersistenceException ex) {
logger.info("got expected exception: " + ex);
exceptionThrown = true;
}
assertTrue(exceptionThrown);
Output
-testAUTOBad -trying to create drill with pre-exist pk: 1171672359, id=25, make=BD -got expected exception: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: ejava.examples.orm.core.annotated.Drill
If an entity is defined to have its primary key automatically generated -- no matter the strategy -- the provider will insist on implementing the value. It is always an error to pass a detached/transient object to persist() with an id already assigned.
One should always identify what the GenerationStrategy should be so that the strategy remains consistent over time. For example, Hibernate use to default to a form of IDENTITY in JavaSE environments and SEQUENCE in JavaEE environments.
Uses database identity column type
If value provided to database -- it is used
If value not provided to database -- a follow-up call required to obtain value used
Not supported by all databases (e.g., Oracle)
Figure 48.7. 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.8. IDENTITY Database Schema
create table ORMCORE_GADGET (
id bigint generated by default as identity,
make varchar(255),
primary key (id)
)
Figure 48.9. IDENTITY Test (with Active Transaction)
Test (with Transaction Active)
ejava.examples.orm.core.annotated.Gadget gadget = new Gadget(0);
gadget.setMake("gizmo 1");
//insert a row in the database
//start with a tx already active
logger.info("gadget (before persist; tx={}): {}", txActive(), gadget);
em.persist(gadget);
logger.info("created gadget (after persist, before flush; tx={}): {}", txActive(), gadget);
em.flush();
logger.info("created gadget (after flush; tx={}): {}", txActive(), gadget);
assertNotEquals(0, gadget.getId());
Output (with Transaction Active)
-gadget (before persist; tx=true): 1798443618, id=0, make=gizmo 1 -insert into ORMCORE_GADGET (id, make) values (null, ?) -binding parameter [1] as [VARCHAR] - [gizmo 1] -created gadget (after persist, before flush; tx=true): 1798443618, id=1, make=gizmo 1 -created gadget (after flush; tx=true): 1798443618, id=1, make=gizmo 1
Follow-on IDENTITY Allocations (with Transaction Active)
-insert into ORMCORE_GADGET (id, make) values (null, ?) -binding parameter [1] as [VARCHAR] - [gizmo 2] -created gadget(tx=true): 370055648, id=2, make=gizmo 2 -insert into ORMCORE_GADGET (id, make) values (null, ?) -binding parameter [1] as [VARCHAR] - [gizmo 3] -created gadget(tx=true): 911933063, id=3, make=gizmo 3 -insert into ORMCORE_GADGET (id, make) values (null, ?) -binding parameter [1] as [VARCHAR] - [gizmo 4] -created gadget(tx=true): 568613487, id=4, make=gizmo 4
Provider inserts row into database with null ID
Provider queries database (not shown in debug) for primary key generated for the row
Provider eagerly inserts row during persist() and before flush() when transaction is open -- to determine primary key
When using the IDENTITY strategy, the provider must make at least two calls to the database. One for the INSERT and one to determine the primary key value generated by the database.
Figure 48.10. IDENTITY Test (persist() with Inactive Transaction)
Test
em.getTransaction().rollback();
logger.info("rolled back tx(tx={})", txActive());
for (int i=0; i<3; i++) {
Gadget g = new Gadget();
g.setMake("gizmo " + counter++);
em.persist(g);
logger.info("created gadget(tx={}): {}", txActive(), g);
if (i==0) {
gadget=g;
}
}
logger.info("starting tx(tx={}): {}", txActive(), gadget);
em.getTransaction().begin();
logger.info("tx started, flushing (tx={}): {}", txActive(), gadget);
em.flush();
logger.info("cache flushed (tx={}): {}", txActive(), gadget);
em.getTransaction().commit();
logger.info("tx committed (tx={}): {}", txActive(), gadget);
Output (persist() with inactive transaction)
-rolled back tx(tx=false)
-created gadget(tx=false): 1372646511, id=0, make=gizmo 5
-created gadget(tx=false): 1202178366, id=0, make=gizmo 6
-created gadget(tx=false): 1872410525, id=0, make=gizmo 7
-starting tx(tx=false): 1372646511, id=0, make=gizmo 5
Provider could not insert rows or determine primary key while transaction inactive
Business logic could not rely on PK values being set (e.g., send event or log)
Output (active transaction following persist)
-tx started, flushing (tx=true): 1372646511, id=0, make=gizmo 5
-insert into ORMCORE_GADGET (id, make) values (null, ?)
-binding parameter [1] as [VARCHAR] - [gizmo 5]
-insert into ORMCORE_GADGET (id, make) values (null, ?)
-binding parameter [1] as [VARCHAR] - [gizmo 6]
-insert into ORMCORE_GADGET (id, make) values (null, ?)
-binding parameter [1] as [VARCHAR] - [gizmo 7]
-cache flushed (tx=true): 1372646511, id=5, make=gizmo 5
-tx committed (tx=false): 1372646511, id=5, make=gizmo 5
Provider waited for next flush() cycle (not tx start) to perform INSERTs and determine primary key
Uses a formal database SEQUENCE construct to generate a unique ID
SEQUENCE is not part of a transaction -- cannot rollback the generation of a sequence value
Database SEQUENCE value may have gaps
Common, but not supported by all databases (e.g., HSQL)
Figure 48.11. Entity with GenerationType.SEQUENCE
@Entity
@Table(name="ORMCORE_FAN")
@SequenceGenerator(
name="fanSequence", //required logical name
sequenceName="FAN_SEQ", //name in database
initialValue=5, //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.12. SEQUENCE Database Schema
create table ORMCORE_FAN ( id bigint not null, make varchar(255), primary key (id) ) create sequence FAN_SEQ start with 5 increment by 3
Provider generates schema with SEQUENCE starting with 5 with increment of 3
Inspecting database server through UI prior to start -- showed current value of 2 (2+3=5)
Figure 48.13. SEQUENCE Test (with active transaction)
Test (with active transaction)
Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("sql.sequences", "true")));
ejava.examples.orm.core.annotated.Fan fan = new Fan(0);
fan.setMake("cool runner 1");
//insert a row in the database
logger.info("persisting fan(tx={}): {}", txActive(), fan);
em.persist(fan);
logger.info("created fan (before flush, tx={}):", txActive(), fan);
em.flush();
logger.info("created fan (after flush; tx={}): {}", txActive(), fan);
assertNotEquals(0, fan.getId());
Output (with active transaction)
-persisting fan(tx=true): 1413306467, id=0, make=cool runner 1 -call next value for FAN_SEQ -call next value for FAN_SEQ #Current value=8 after this call, next result will be 11 -created fan (before flush, tx=true): 1413306467, id=5, make=cool runner 1 -insert into ORMCORE_FAN (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [cool runner 1] -binding parameter [2] as [BIGINT] - [5] -created fan (after flush; tx=true): 1413306467, id=5, make=cool runner 1 -persisting fan(tx=true): 1413306467, id=0, make=cool runner 1 -call next value for FAN_SEQ -call next value for FAN_SEQ -created fan (before flush, tx=true): 1413306467, id=5, make=cool runner 1 -insert into ORMCORE_FAN (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [cool runner 1] -binding parameter [2] as [BIGINT] - [5] -created fan (after flush; tx=true): 1413306467, id=5, make=cool runner 1
Provider obtains primary key value prior to inserting row (x2 calls addressed later)
First primary key value (5) corresponds with the @SequenceGenerator.initialValue property
Primary key value available to business logic prior to insert into database
Provider inserts the row during next flush cycle
Inspecting database server through UI -- shows current value of 8 (2x calls to next value)
The database advances its SEQUENCE by the allocationSize on each call to next value. The provider may use the returned value and allacationSize values above the returned value before returning to the database for a new value. The database and provider *must* have the same increment/allocationSize configured.
Figure 48.14. Follow-on SEQUENCE Allocations
Output prior to flush/commit
-created fan(tx=true): 1289462509, id=6, make=cool runner 2 -created fan(tx=true): 740265405, id=7, make=cool runner 3 -created fan(tx=true): 1439003682, id=8, make=cool runner 4 -call next value for FAN_SEQ -created fan(tx=true): 578969118, id=9, make=cool runner 5 -created fan(tx=true): 493310435, id=10, make=cool runner 6 -created fan(tx=true): 757436159, id=11, make=cool runner 7 -call next value for FAN_SEQ -created fan(tx=true): 1682973478, id=12, make=cool runner 8 ... -created fan(tx=true): 1501844857, id=22, make=cool runner 18 -created fan(tx=true): 817994751, id=23, make=cool runner 19 -call next value for FAN_SEQ -created fan(tx=true): 1312250810, id=24, make=cool runner 20 -created fan(tx=true): 1296316112, id=25, make=cool runner 21 ...
Provider calling database to get next allocation prior to exhausting current allocation
Provider knows to self-generate next allocationSize values for value returned prior to obtaining next allocation
Provider waits for next flush cycle to insert rows into database
Output during commit
-committing (tx=true): 1289462509, id=6, make=cool runner 2 -insert into ORMCORE_FAN (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [cool runner 2] -binding parameter [2] as [BIGINT] - [6] -insert into ORMCORE_FAN (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [cool runner 3] -binding parameter [2] as [BIGINT] - [7] -insert into ORMCORE_FAN (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [cool runner 4] ... -insert into ORMCORE_FAN (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [cool runner 21] -binding parameter [2] as [BIGINT] - [25] -tx committed (tx=false): 1289462509, id=6, make=cool runner 2
Rows inserted into database during next flush cycle triggered by commit
Database makes no correlation between sequence numbers returned and primary key value assigned to a row
The default increment/allocationSize requires two (2) calls per insert. A larger increment/allocationSize can reduce the number of calls by up to 50%, but will potentially leave gaps and exhaust the unique sequence values earlier in the lifetime of the database if clients terminate and restart prior to exhausting an allocation.
Figure 48.15. SEQUENCE Test (persist without active transaction)
Test
logger.info("tx(tx={})", txActive());
for (int i=0; i<20; i++) {
Fan f = new Fan();
f.setMake("cool runner " + counter++);
em.persist(f);
logger.info("created fan(tx={}): {}", txActive(), f);
if (i==0) {
fan=f;
}
}
logger.info("starting tx(tx={}): {}", txActive(), fan);
em.getTransaction().begin();
logger.info("tx started, flushing (tx={}): {}", txActive(), fan);
em.flush();
logger.info("cache flushed (tx={}): {}", txActive(), fan);
em.getTransaction().commit();
logger.info("tx committed (tx={}): {}", txActive(), fan);
Output while transaction inactive
-tx(tx=false) -created fan(tx=false): 1451387509, id=26, make=cool runner 22 -call next value for FAN_SEQ -created fan(tx=false): 1238209644, id=27, make=cool runner 23 -created fan(tx=false): 1371953731, id=28, make=cool runner 24 -created fan(tx=false): 1947060963, id=29, make=cool runner 25 -call next value for FAN_SEQ -created fan(tx=false): 1309934743, id=30, make=cool runner 26 -created fan(tx=false): 833420622, id=31, make=cool runner 27 -created fan(tx=false): 1601333072, id=32, make=cool runner 28 ... -call next value for FAN_SEQ -created fan(tx=false): 1591063329, id=42, make=cool runner 38 -created fan(tx=false): 2129344690, id=43, make=cool runner 39 -created fan(tx=false): 223662325, id=44, make=cool runner 40 -call next value for FAN_SEQ -created fan(tx=false): 1835794313, id=45, make=cool runner 41
Same as transaction active so far
Output when transaction activated
-starting tx(tx=false): 1451387509, id=26, make=cool runner 22 -tx started, flushing (tx=true): 1451387509, id=26, make=cool runner 22 -insert into ORMCORE_FAN (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [cool runner 22] -binding parameter [2] as [BIGINT] - [26] -insert into ORMCORE_FAN (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [cool runner 23] -binding parameter [2] as [BIGINT] - [27] ... -insert into ORMCORE_FAN (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [cool runner 41] -binding parameter [2] as [BIGINT] - [45] -cache flushed (tx=true): 1451387509, id=26, make=cool runner 22 -tx committed (tx=false): 1451387509, id=26, make=cool runner 22
As with previous case -- nothing started with transaction opening
All inserts are delayed until next flush cycle
A poor man's SEQUENCE
A generic answer to "some databases don't support ..."
Both can allocate IDs in blocks and allow the client provider to assign IDs with less requests to the database
SEQUENCES are implemented in the database as non-transactional resources for this specific purpose
The TABLEs strategy is implemented using RDBMS Table and is transactional
The deterministic read and update of a RDBMS table requires a transaction with a at least a row lock that will allow a read, increment, and update a column value for a row
Transactions and locks required on the ID table row -- may induce sequential access to inserts
Figure 48.16. 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
initialValue=7, //first value database should provide
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.17. 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 ) -insert into ORMCORE_EB_UID(UID_ID, UID_VAL) values ('ORMCORE_EGGBEATER',7) -insert into ORMCORE_EB_UID(UID_ID, UID_VAL) values ('ORMCORE_EGGBEATER',7)
Row insert initializes ID row
Duplicate row inserts likely caused by demonstration environment having annotated and mapped classes define same table mapping
Figure 48.18. TABLE Test
logger.info("testTABLE");
logger.debug("table id before(tx={})={}", txActive(), 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 1");
//insert a row in the database
logger.info("persisting eggbeater (tx={}): {}", txActive(), eggbeater);
em.persist(eggbeater);
logger.info("created eggbeater (before flush; tx={}): {}", txActive(), eggbeater);
em.flush();
logger.info("created eggbeater (after flush; tx={}): {}", txActive(), eggbeater);
assertNotEquals(0, eggbeater.getId());
logger.debug("table id after(tx={})={}", txActive(), getTableId());
-testTABLE -select UID_VAL from ORMCORE_EB_UID where UID_ID='ORMCORE_EGGBEATER' -extracted value ([UID_VAL] : [NUMERIC]) - [7] -table id before(tx=true)=7 -persisting eggbeater (tx=true): 1049628186, id=0, make=done right 1 -select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update -update ORMCORE_EB_UID set UID_VAL=? where UID_VAL=? and UID_ID=? -select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update -update ORMCORE_EB_UID set UID_VAL=? where UID_VAL=? and UID_ID=? -created eggbeater (before flush; tx=true): 1049628186, id=8, make=done right 1 -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 1] -binding parameter [2] as [BIGINT] - [8] -created eggbeater (after flush; tx=true): 1049628186, id=8, make=done right 1 -select UID_VAL from ORMCORE_EB_UID where UID_ID='ORMCORE_EGGBEATER' -extracted value ([UID_VAL] : [NUMERIC]) - [17] -table id after(tx=true)=17
Provider gets a primary key value during persist
Provider locks row (SELECT FOR UPDATE) during transaction and updates with new value
Provider inserts row during flush cycle with generated primary key value
Cannot explain the initial 2x ID requests but both incremented the value to a result of 17 (7+5+5=17)
Figure 48.19. Follow-on TABLE Allocations
-created ehhbeater(tx=true): 1275580924, id=9, make=done right 2 -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 2] -binding parameter [2] as [BIGINT] - [9] -table id after[2](tx=true)=17 -created ehhbeater(tx=true): 1726759945, id=10, make=done right 3 -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 3] -binding parameter [2] as [BIGINT] - [10] -table id after[3](tx=true)=17 ... -created ehhbeater(tx=true): 154468798, id=13, make=done right 6 -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 6] -binding parameter [2] as [BIGINT] - [13] -table id after[6](tx=true)=17 -select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update -update ORMCORE_EB_UID set UID_VAL=? where UID_VAL=? and UID_ID=? -created ehhbeater(tx=true): 490475818, id=14, make=done right 7 -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 7] -binding parameter [2] as [BIGINT] - [14] -table id after[7](tx=true)=22 ... -created ehhbeater(tx=true): 360233196, id=18, make=done right 11 -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 11] -binding parameter [2] as [BIGINT] - [18] -table id after[11](tx=true)=22 -select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update -update ORMCORE_EB_UID set UID_VAL=? where UID_VAL=? and UID_ID=? -created ehhbeater(tx=true): 1912769093, id=19, make=done right 12 -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 12] -binding parameter [2] as [BIGINT] - [19] -table id after[12](tx=true)=27 ... -created ehhbeater(tx=true): 1947681232, id=23, make=done right 16 -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 16] -binding parameter [2] as [BIGINT] - [23] -table id after[16](tx=true)=27 -select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update -update ORMCORE_EB_UID set UID_VAL=? where UID_VAL=? and UID_ID=? -created ehhbeater(tx=true): 783682673, id=24, make=done right 17 -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 17] -binding parameter [2] as [BIGINT] - [24] -table id after[17](tx=true)=32 ... -committing (tx=true): 1275580924, id=9, make=done right 2 -tx committed (tx=false): 1275580924, id=9, make=done right 2 ...
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.
Figure 48.20. TABLE Test (persist without active transaction)
Output prior to transaction
-tx(tx=false) -created ehhbeater(tx=false): 150835665, id=29, make=done right 22 -table id after[22](tx=false)=37 -created ehhbeater(tx=false): 315885065, id=30, make=done right 23 -table id after[23](tx=false)=37 -created ehhbeater(tx=false): 423539130, id=31, make=done right 24 -table id after[24](tx=false)=37 -created ehhbeater(tx=false): 841313896, id=32, make=done right 25 -table id after[25](tx=false)=37 -created ehhbeater(tx=false): 1673518027, id=33, make=done right 26 -table id after[26](tx=false)=37 -select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update -update ORMCORE_EB_UID set UID_VAL=? where UID_VAL=? and UID_ID=? -created ehhbeater(tx=false): 1042223174, id=34, make=done right 27 -table id after[27](tx=false)=42 ... -created ehhbeater(tx=false): 584643821, id=38, make=done right 31 -table id after[31](tx=false)=42 -select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update -update ORMCORE_EB_UID set UID_VAL=? where UID_VAL=? and UID_ID=? -created ehhbeater(tx=false): 999999316, id=39, make=done right 32 -table id after[32](tx=false)=47 ... -created ehhbeater(tx=false): 1815337594, id=43, make=done right 36 -table id after[36](tx=false)=47 -select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update -update ORMCORE_EB_UID set UID_VAL=? where UID_VAL=? and UID_ID=? -created ehhbeater(tx=false): 362311125, id=44, make=done right 37 -table id after[37](tx=false)=52 ... -created ehhbeater(tx=false): 1292683326, id=48, make=done right 41 -table id after[41](tx=false)=52
Provider obtains next allocation value outside of transaction where rows inserted
Next allocation value requires transaction to update -- results in extra transaction per allocation
Provider assigns ID to object from allocation prior to transaction where rows are inserted
Output once transaction started
-starting tx(tx=false): 150835665, id=29, make=done right 22 -tx started, flushing (tx=true): 150835665, id=29, make=done right 22 -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 22] -binding parameter [2] as [BIGINT] - [29] -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 23] -binding parameter [2] as [BIGINT] - [30] -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 24] -binding parameter [2] as [BIGINT] - [31] -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 25] -binding parameter [2] as [BIGINT] - [32] -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 26] -binding parameter [2] as [BIGINT] - [33] ... -insert into ORMCORE_EGGBEATER (make, id) values (?, ?) -binding parameter [1] as [VARCHAR] - [done right 41] -binding parameter [2] as [BIGINT] - [48] -cache flushed (tx=true): 150835665, id=29, make=done right 22 -tx committed (tx=false): 150835665, id=29, make=done right 22
Provider inserts rows from cache during next flush cycle
Each row has a pre-assigned ID from provider
Database makes no correlation between ID within row and the table maintaining the IDs