<br>A company has associated some stuff like houses, persons, buildings.
The objects and associations change over time as well as in quality (e.g. building is rented, owned ...).
Now the company wants to build reports of the company state for certain different points of time in past,
without having to adapt the report definition to recognize the point of time definition or specific historization tools like Hibernate Envers.
There is a currently effective set of objects which can be easily queried by hibernate (JPQL) selects.
If some changes are made, this can be documented via Hibernate Envers, storing old object versions in separate tables.
But if you want to retrieve a complete set of objects valid for a certain due date (or timestamp value),
you have to do some special Envers queries which causes many extra coding.
If you want to do some reporting using JasperReports or BIRT navigating via JPQL you can not use these tools
without writing new report definitions (if possible with envers at all).
<p><b>Proposed solution principle:</b>
<li>In every session must be defined a query effectivity item (date or timestamp) which defines the point of interest in time for which all queries within this session will be processed.
<br>If not set, the current time is used as point of interest in time.</li>
<li>Every entity relevant for historization must define an effectivity time span, which will define the set of of entity objects valid for all queries.</li>
<li>The different versions of the same object build up an logical domain.
<br>If an query effectivity item is set in session, then at most ONE object of every object domain will be found by a any query.<br>
<p><b>Proposed hibernate solution (theory):</b>
<br><b>All following definitions apply only to entities relevant for historization.</b><br>Entities not relevant for historization are handled as before.
<li>The session accepts an Calender value which defines the current point of interest in time for all subsequent queries. Lets call it "Session Effectivity Criteria".
<br>For possibly influencing all formerly read objects, the object cache of the session must be cleared , when this value is set.
<br>If not set, the current time is used as criteria.
Of course, a flush operation may be neccessary, too.</li>
<li>Every entity must define two temporal attributes which define the effectivity time span for an object version.
<br>Lets call them "Effectivity Start" and "Effectivity End"
<br>When persisting an objects version there are three rules to be checked:
<li>"Effectivity Start" and "Effectivity End" must both not be null.<br>This is because it's easier and probably faster to search with only one "between" expression.</li>
<li>"Effectivity Start" must be less than "Effectivity End".</li>
<li>Within an object domain there must not be other object versions which overlap the same effectivity time span as the object to be persisted.</li>
<li>For identification of the objects different time dependent versions of the same object (object domain) the entity must defined an attribute which works as a link between the separate versions.
Lets call it "Object Domain Id".<br>All different versions of an object have an "Object Domain Id" of the same value.</li>
<li>All association definitions like @ManyToOne, @OneToOne, @OneToMany targeting to an entity relevant for historization, do not implicitly relate to the objects id, but to the "Object Domain Id"</li>
<li>All select operations for an entity add an additional fragment to the where clause which limit the found objects to this condition :<br><i>"Effectivity Start" <b><=</b> "Session Effectivity Criteria" <b><</b> "Effectivity End".
<br>This applies to all select operations except those which define load operations needing explicitely use of the object id (e.g. load for UPGRADE, PESSIMISTIC_READ etc.).
<p><b>Proposed hibernate solution (practice):</b>
<li>The interface "org.hibernate.Session" (and its derived classes) must be expanded by follwing methodes:
<li>setEffectivityCriteria(java.util.Calendar)<br>Set the "Session Effectivity Criteria",so that this point of time is used as effectivity criteria within further queries of the session.</li>
<li>resetEffectivityCriteria()<br>Reset the "Session Effectivity Criteria",so that the current time is used as effectivity criteria within further queries of the session.</li>
<li>List<java.util.Calendar>getEffectivityStarts(java.lang.Object entityObject, java.util.Calendar earliestStart, java.util.Calendar latestStart, int maxResults)
<br>Returns an ordered by timestamp list of effectivity start points of time for a certain object (respectively the domain of this object).
<br>The parameter "earliestStart" means that, if it exists, the effectivity start point of the object should returned, which has been valid at earliestStart.
<br>That means the first returned effectivity start point can be less than the value of the "earliestStart" parameter.
<br>This method can be used to determine which versions of an object exist.</li>
<li>An Annotation "@EffectivtyDomainId" will be introduced which defines the "Object Domain Id"-Attribute of the entity.
<br>May be this should automatically imply "nullable = false" and "updateable = false".</li>
<li>Two Annotations "@EffectivtyStart" and "@EffectivtyStart" will be introduced which define the "Effectivity Start" and "Effectivity End"-Attributes of the entity.
<br>The annotated attribute must be annotated by @Temporal, too.
<br>May be this should automatically imply "nullable = false".
<li>Adaption of the classes "org.hibernate.hql.internal.antlr.HqlSqlBaseWalker" and "org.hibernate.hql.internal.ast.HqlSqlWalker"
and may be others for application of the "Object Domain Id"-Attribute instead of the "Id"-Attribute for all applicable queries.
Especially regarding joins between and to historizable entities<br></li>
<p><b>Possible cooperation with Envers:</b>
<br>If the value of an entity attribute changes without change of the effectivity span of the object, Envers could be used for tracking the changes (regarding what / who).
If the value of an entity attribute changes from a certain point of time on (e.g. directly chronologically following),
then the "old" version of the object will be changed in "Effectivity End" and a new copy of the object with new effectivity bounds will be created.
<br>In this case Envers tracks the values too (for forensic purposes), but effectivty sensible reports will only read the main entities.