blob: cef09c06f40ca2d70b7497a8725c2b4e007c7299 [file] [log] [blame]
{{bug|361016}}
__NOTOC__
== EclipseLink JPA Futures Example ==
This example is intended to illustrate how entity versions can be stored and queried in a database. Unlike the history functionality of EclipseLink this example is focused on creating versions of an entity that may take effect in the future.
'''Requirements'''
* Optimize queries against current version. Queries against future versions
* Allow versions for the future to be created with a time they are expected to become the current
* When a future version is promoted to current its temporal storage is not required (no history remains)
* Allow multiple future versions and support changing when a future version takes effect
* Support defining a new entity that does not yet exist but may exist in the future
== Software ==
This example is being built using EclipseLink 2.3.1. To access the source of the example check it out of SVN at:
/svnroot/rt/org.eclipse.persistence/trunk/examples/org.eclipse.persistence.example.jpa.futures
== How it Works ==
Coming Soon...
== Examples ==
The following examples are from test cases.
=== Schema Generation ===
At the start of each test suite the schema is replaced. The resulting CREATE TABLE operations are:
<source lang="sql">
CREATE TABLE TPERSON (OID NUMBER(10) NOT NULL,
END_TS NUMBER(19) NULL,
F_NAME VARCHAR2(255) NULL,
L_NAME VARCHAR2(255) NULL,
START_TS NUMBER(19) NULL,
CID NUMBER(10) NULL,
ADDRESS_OID NUMBER(10) NULL,
PRIMARY KEY (OID))
CREATE TABLE TADDRESS (OID NUMBER(10) NOT NULL,
CITY VARCHAR2(255) NULL,
END_TS NUMBER(19) NULL,
START_TS NUMBER(19) NULL,
STATE VARCHAR2(255) NULL,
STREET VARCHAR2(255) NULL,
CID NUMBER(10) NULL,
PRIMARY KEY (OID))
ALTER TABLE TPERSON ADD CONSTRAINT FK_TPERSON_ADDRESS_OID FOREIGN KEY (ADDRESS_OID) REFERENCES TADDRESS (OID)
ALTER TABLE TPERSON ADD CONSTRAINT FK_TPERSON_CID FOREIGN KEY (CID) REFERENCES TPERSON (OID)
ALTER TABLE TADDRESS ADD CONSTRAINT FK_TADDRESS_CID FOREIGN KEY (CID) REFERENCES TADDRESS (OID)
</source>
=== Create Current Person ===
<source lang="java">
Person p = new Person();
p.setFirstName("Doug");
p.setLastName("Clarke");
Address a = new Address();
a.setStreet("45 O'Connor Street");
a.setCity("Ottawa");
a.setState("Ontario");
p.setAddress(a);
em.persist(a);
em.persist(p);
</source>
Resulting SQL:
<source lang="sql">
[EL Fine]: INSERT INTO TADDRESS (OID, CITY, END_TS, START_TS, STATE, STREET, CID) VALUES (?, ?, ?, ?, ?, ?, ?)
bind => [1, Ottawa, 9223372036854775807, 0, Ontario, 45 OConnor Street, null]
[EL Fine]: UPDATE TADDRESS SET STATE = ?, STREET = ?, CID = ?, CITY = ? WHERE (OID = ?)
bind => [Ontario, 45 OConnor Street, 1, Ottawa, 1]
[EL Fine]: INSERT INTO TPERSON (OID, END_TS, F_NAME, L_NAME, START_TS, CID, ADDRESS_OID) VALUES (?, ?, ?, ?, ?, ?, ?)
bind => [2, 9223372036854775807, Doug, Clarke, 0, null, null]
[EL Fine]: UPDATE TPERSON SET CID = ?, ADDRESS_OID = ?, F_NAME = ?, L_NAME = ? WHERE (OID = ?)
bind => [2, 1, Doug, Clarke, 2]
</source>
''Note: The INSERT followed by the UPDATE statement are to populate the self-referencing FK values. Optimized solution being investigated''
=== Create Person Edition ===
This example creates an edition of a current Person (id={currentPersonId}) which is set to take effect at T2 and be deleted or replaced at T4. The current version of the person is updated accordingly to end at T2.
<source lang="java">
Person currentPerson = em.find(Person.class, currentPersonId);
Person p = TemporalHelper.createEdition(em, currentPerson);
p.setFirstName("Dougie");
p.setStart(T2);
currentPerson.setEnd(T2);
p.setEnd(T4);
em.persist(p);
</source>
SQL:
<source lang="sql">
...
</source>
=== Create Future Person ===
This creates a new Person that does not currently exist but will exist as of T3.
<source lang="java">
Person p = new Person();
p.setFirstName("John");
p.setLastName("Doe");
p.setStart(T3);
em.persist(p);
</source>
SQL:
<source lang="sql">
[EL Fine]: INSERT INTO TPERSON (OID, END_TS, F_NAME, L_NAME, START_TS, CID, ADDRESS_OID) VALUES (?, ?, ?, ?, ?, ?, ?)
bind => [1, 9223372036854775807, John, Doe, 3, null, null]
[EL Fine]: UPDATE TPERSON SET CID = ?, START_TS = ?, F_NAME = ?, L_NAME = ? WHERE (OID = ?)
bind => [1, 3, John, Doe, 1]
</source>
=== Query for Current Person ===
<source lang="java">
EntityManager em = createEntityManager();
Person person = em.createQuery("SELECT p From Person p WHERE p.id = " + currentPersonId, Person.class).getSingleResult();
Address address = person.getAddress();
Assert.assertNotNull(person);
System.out.println("FIND CURRENT: " + person);
</source>
OUTPUT:
<source lang="sql">
[EL Fine]: SELECT OID, END_TS, F_NAME, L_NAME, START_TS, CID, ADDR_ID FROM TPERSON WHERE ((OID = ?) AND (START_TS = ?))
bind => [2, 0]
[EL Fine]: SELECT OID, CITY, END_TS, START_TS, STATE, STREET, CID FROM TADDRESS WHERE ((OID = ?) AND (START_TS = ?))
bind => [1, 0]
FIND CURRENT: Person(2)::Doug @ 0-2
</source>
=== Query for Current Person Joining Address ===
<source lang="java">
EntityManager em = createEntityManager();
Person person = em.createQuery("SELECT p From Person p JOIN FETCH p.address WHERE p.id = " + currentPersonId, Person.class).getSingleResult();
Address address = person.getAddress();
System.out.println("FIND CURRENT: " + person);
</source>
OUTPUT:
<source lang="sql">
[EL Fine]: SELECT t1.OID, t1.END_TS, t1.F_NAME, t1.L_NAME, t1.START_TS, t1.CID, t1.ADDR_ID, t0.OID, t0.CITY, t0.END_TS, t0.START_TS, t0.STATE, t0.STREET, t0.CID
FROM TADDRESS t0, TPERSON t1
WHERE (((t1.OID = ?) AND (t1.START_TS = ?)) AND ((t0.OID = t1.ADDR_ID) AND (t0.START_TS = ?)))
bind => [2, 0, 0]
FIND CURRENT: Person(2)::Doug @ 0-2
</source>
=== Find Current Person ===
<source lang="java">
Person person = em.find(Person.class, currentPersonId);
System.out.println("FIND CURRENT: " + person);
</source>
OUTPUT:
<source lang="sql">
[EL Fine]: SELECT OID, CITY, END_TS, START_TS, STATE, STREET, CID FROM TADDRESS WHERE ((OID = ?) AND (START_TS = ?))
bind => [1, 0]
FIND CURRENT: model.Person(2)::Doug @ 0-2
</source>
=== Query all Current Persons ===
<source lang="java">
List<Person> results = em.createQuery("SELECT p From Person p", Person.class).getResultList();
</source>
LOG:
<source lang="sql">
[EL Fine]: SELECT OID, END_TS, F_NAME, L_NAME, START_TS, CID, ADDRESS_OID FROM TPERSON WHERE (START_TS = ?)
bind => [0]
</source>
=== Find Person edition @ T3 ===
<source lang="java">
Map<String, Object> props = new HashMap<String, Object>();
props.put("EFF_TS", T3);
EntityManager em = emf.createEntityManager(props);
Person personEdition = em.createQuery("SELECT p From PersonEdition p WHERE p.cid = " + currentPersonId, Person.class).getSingleResult();
System.out.println("QUERY EFFECTIVE @ T3: " + personEdition);
</source>
Log Output:
<source lang="sql">
[EL Fine]: SELECT OID, END_TS, F_NAME, L_NAME, START_TS, CID, ADDRESS_OID FROM TPERSON WHERE ((CID = ?) AND ((? >= START_TS) AND (? < END_TS)))
bind => [2, 3, 3]
QUERY EFFECTIVE @ T3:
Person(3)::Dougie @ 2-4
</source>
=== Query Future Edition Person @ T2 with Address Joined ===
<source lang="java">
EntityManager em = createEntityManager();
em.setProperty("EFF_TS", T2);
Person person = em.createQuery("SELECT p From PersonEdition p JOIN FETCH p.address WHERE p.cid = " + currentPersonId, Person.class).getSingleResult();
Address address = pEdition.getAddress();
System.out.println("QUERY EDITION @ T2: " + person);
System.out.println("\t> " + address);
</source>
OUTPUT:
<source lang="sql">
[EL Fine]: SELECT t1.OID, t1.END_TS, t1.F_NAME, t1.L_NAME, t1.START_TS, t1.CID, t1.ADDR_ID, t0.OID, t0.CITY, t0.END_TS, t0.START_TS, t0.STATE, t0.STREET, t0.CID
FROM TADDRESS t0, TPERSON t1
WHERE (((t1.CID = ?) AND ((? >= t1.START_TS) AND (? < t1.END_TS))) AND ((t0.CID = t1.ADDR_ID) AND ((? >= t0.START_TS) AND (? < t0.END_TS))))
bind => [2, 2, 2, 2, 2]
QUERY EDITION @ T2: model.PersonEdition(3 @ 2::4)
> model.AddressEdition(4 @ 2::9223372036854775807)
</source>