Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Eclipselink history of related objects

I can create history of an entity with a HistoryCustomizer

@Entity
@Customizer(MyHistoryCustomizer.class)
public class Employee {..}

the HistoryCustomizer is something like this one:

public class MyHistoryCustomizer implements DescriptorCustomizer {
    public void customize(ClassDescriptor descriptor) {
        HistoryPolicy policy = new HistoryPolicy();
        policy.addHistoryTableName("EMPLOYEE_HIST");
        policy.addStartFieldName("START_DATE");
        policy.addEndFieldName("END_DATE");
        descriptor.setHistoryPolicy(policy);
    }
}

The history objects can be fetched with the "AS_OF" hint

javax.persistence.Query historyQuery = em
                    .createQuery("SELECT e FROM Employee e", Employee.class)
                    .setParameter("id", id)
                    .setHint(QueryHints.AS_OF, "yyyy/MM/dd HH:mm:ss.SSS")
                    .setHint(QueryHints.READ_ONLY, HintValues.TRUE)
                    .setHint(QueryHints.MAINTAIN_CACHE, HintValues.FALSE);

just fine BUT, if you start accessing objects referenced by this historical object, the referenced objects will be the actual version of them. So the Employee from last year (fetched by a historical query) will have the current Address assigned to it and no the one it used to have last year.

How can I tell EclipseLink (2.5.0) to fetch the related object from the past as well?

like image 745
pentike Avatar asked Jul 30 '14 08:07

pentike


1 Answers

In order to query the historical state of several - not just one like above - entities, we have to create an EclipseLink specific HistoricalSession. Queries run through this session will use the same historical timestamp and represent the proper historical state of the object graph.

I am using JPA in other parts of the code, so I will start with converting the JPA Query to an EclipseLink ReadAllQuery.

The HistoricalSession has its own entity cache, so that the historical entities do not mix with the normal ones.

        // Get the EclipseLink ServerSession from the JPA EntitiyManagerFactory
        Server serverSession = JpaHelper.getServerSession(emf);
        // Only a ClientSession can give us a HistoricalSession so ask one from the ServerSession 
        ClientSession session = serverSession.acquireClientSession();
        // Create the HistoricalSessions. A HistoricalSession is sticked to a point in the past and all the queries are executed at that time.
        Session historicalSessionAfterFirstChild = session.acquireHistoricalSession(new AsOfClause(afterFirstChildAdded));

        ReadAllQuery q; 

        Query jpaQuery = em.createQuery(query);
        jpaQuery.setParameter("root", "parent");
        // Extract the EclipseLink ReadAllQuery from the JPA Query. We can use named queries this way.
        q=JpaHelper.getReadAllQuery(jpaQuery);

        // This is a possible EclipseLink bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=441193
        List<Object> arguments = new Vector<Object>();
        arguments.add("parent");
        q.setArgumentValues(arguments);

        Vector<Parent> historyAwareParents ;

        // Execute the query
        historyAwareParents = (Vector<Parent>) historicalSessionAfterFirstChild.executeQuery(q);
        for (Child c : historyAwareParents.get(0).children) {
            System.out.println(c.getExtension() + " " + c.getRoot());
        }
like image 54
pentike Avatar answered Oct 14 '22 22:10

pentike