| package org.eclipse.osbp.jpa.services.history; |
| |
| import java.util.Calendar; |
| import java.util.Date; |
| |
| import org.eclipse.persistence.expressions.Expression; |
| import org.eclipse.persistence.expressions.ExpressionBuilder; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.internal.sessions.EmptyRecord; |
| import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; |
| import org.eclipse.persistence.queries.DatabaseQuery; |
| import org.eclipse.persistence.queries.InsertObjectQuery; |
| import org.eclipse.persistence.queries.QueryRedirector; |
| import org.eclipse.persistence.queries.ReadObjectQuery; |
| import org.eclipse.persistence.queries.UpdateObjectQuery; |
| import org.eclipse.persistence.queries.WriteObjectQuery; |
| import org.eclipse.persistence.sessions.Record; |
| import org.eclipse.persistence.sessions.Session; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Manages the versioning of historized and timedependent entities. |
| */ |
| @SuppressWarnings("serial") |
| public class HistorizedQueryRedirector implements QueryRedirector { |
| protected static Logger logger = LoggerFactory.getLogger(HistorizedQueryRedirector.class); |
| |
| @Override |
| public Object invokeQuery(DatabaseQuery query, Record arguments, Session session) { |
| WriteObjectQuery writeObjectQuery = (WriteObjectQuery) query; |
| |
| logger.debug("historized changes {}", writeObjectQuery.getObjectChangeSet().getChanges()); |
| |
| if(writeObjectQuery instanceof UpdateObjectQuery) { |
| return writeObjectQuery.getObject(); |
| } |
| |
| Object persistedObj = writeObjectQuery.getObject(); |
| |
| // the object that was persisted / merged |
| // |
| HistorizedObjectWrapper persistedObjWrapper = new HistorizedObjectWrapper(writeObjectQuery.getDescriptor(), |
| persistedObj); |
| |
| // if (persistedObjWrapper.getVersion() > 1) { |
| // throw new IllegalArgumentException( |
| // "Version must be 0. Changing historized records is not allowed: " + persistedObj.toString()); |
| // } |
| Object current = getCurrent(persistedObjWrapper, (AbstractSession) session); |
| |
| if (current == null) { |
| persistedObjWrapper.setId_ValidFrom(createNow(persistedObjWrapper)); |
| persistedObjWrapper.setValidUntil(getMaxDate()); |
| persistedObjWrapper.setCurrentHist(true); |
| |
| persistedObjWrapper.applyHistorizedDomainKey(); |
| |
| writeObjectQuery.setDoNotRedirect(true); |
| return writeObjectQuery.execute((AbstractSession) session, (AbstractRecord) arguments); |
| } else { |
| |
| // sleep 1ms for minimum time intervall between historized records |
| try { |
| Thread.sleep(1); |
| } catch (InterruptedException e) { // NOSONAR |
| } |
| |
| // update the address to be historized |
| long now = createNow(persistedObjWrapper); |
| Object histCurrentObj = getCurrentManaged( |
| new HistorizedObjectWrapper(writeObjectQuery.getDescriptor(), current), (AbstractSession) session); |
| Object histCurrentObjClone = null; |
| try { |
| histCurrentObjClone = histCurrentObj.getClass().newInstance(); |
| } catch (InstantiationException | IllegalAccessException e) { |
| logger.error("{}", e); |
| } |
| query.getDescriptor().getObjectBuilder().copyInto(histCurrentObj, histCurrentObjClone); |
| |
| // the current object in database with flag histCurrent = true |
| // |
| HistorizedObjectWrapper histCurrentObjWrapper = new HistorizedObjectWrapper( |
| writeObjectQuery.getDescriptor(), histCurrentObj); |
| |
| if (query.getDescriptor().getObjectBuilder().compareObjects(persistedObj, histCurrentObj, |
| (AbstractSession) session)) { |
| return persistedObj; |
| } |
| |
| // update the old record |
| // |
| histCurrentObjWrapper.setCurrentHist(false); |
| histCurrentObjWrapper.setValidUntil(now); |
| UpdateObjectQuery updateCurrent = new UpdateObjectQuery(histCurrentObj); |
| updateCurrent.setBackupClone(histCurrentObjClone); |
| updateCurrent.setDoNotRedirect(true); |
| updateCurrent.setIsUserDefined(true); |
| updateCurrent.execute((AbstractSession) session, (AbstractRecord) arguments); |
| |
| // insert the new record |
| // |
| persistedObjWrapper.setId_ValidFrom(now); |
| persistedObjWrapper.setValidUntil(getMaxDate()); |
| persistedObjWrapper.setCurrentHist(true); |
| persistedObjWrapper.setVersion((int) 0); |
| persistedObjWrapper.applyHistorizedDomainKey(); |
| |
| InsertObjectQuery insertObjectQ = new InsertObjectQuery(persistedObj); |
| insertObjectQ.setIsUserDefined(true); |
| insertObjectQ.setDoNotRedirect(true); |
| |
| return insertObjectQ.execute((AbstractSession) session, (AbstractRecord) arguments); |
| } |
| |
| } |
| |
| private long createNow(HistorizedObjectWrapper wrapper) { |
| if (wrapper.isCustomVersion()) { |
| return wrapper.getId_ValidFrom(); |
| } |
| return new Date().getTime(); |
| } |
| |
| private Object getCurrent(HistorizedObjectWrapper wrapper, AbstractSession session) { |
| |
| UnitOfWorkImpl uow = session.acquireNonSynchronizedUnitOfWork(); |
| |
| ReadObjectQuery rq = new ReadObjectQuery(wrapper.getEntityClass()); |
| |
| ExpressionBuilder eb = rq.getExpressionBuilder(); |
| Expression exp = eb.get(wrapper.getIdAttName()).get(wrapper.getId_IdAttName()).equal(wrapper.getID().id) |
| .and(eb.get(wrapper.getCurrentHistAttName()).equal(true)); |
| rq.setSelectionCriteria(exp); |
| rq.dontCheckCache(); |
| rq.dontMaintainCache(); |
| |
| Object current = rq.executeInUnitOfWork(uow, EmptyRecord.getEmptyRecord()); |
| |
| uow.clearForClose(true); |
| return current; |
| } |
| |
| private Object getCurrentManaged(HistorizedObjectWrapper wrapper, AbstractSession session) { |
| |
| ReadObjectQuery rq = new ReadObjectQuery(wrapper.getEntityClass()); |
| rq.conformResultsInUnitOfWork(); |
| |
| ExpressionBuilder eb = rq.getExpressionBuilder(); |
| Expression exp = eb.get(wrapper.getIdAttName()).equal(wrapper.getID()); |
| rq.setSelectionCriteria(exp); |
| |
| return rq.execute(session, EmptyRecord.getEmptyRecord()); |
| } |
| |
| private long getMaxDate() { |
| Calendar cal = Calendar.getInstance(); |
| cal.set(2099, Calendar.DECEMBER, 31, 0, 0, 0); |
| cal.set(Calendar.MILLISECOND, 0); |
| Date maxDate = cal.getTime(); |
| return maxDate.getTime(); |
| } |
| |
| } |