Thanks Mike! Although the approach I’m working on is a little different (and meant to be reusable with any Cayenne installation so can’t depend on superclass template modifications), it’s very helpful to see code from other Cayenne folks.
Cheers, - hugi // Hugi Thordarson // http://www.loftfar.is/ <http://www.loftfar.is/> // s. 895-6688 > On 10. ágú. 2015, at 12:53, Mike Kienenberger <mkien...@gmail.com> wrote: > > I set up auditing using a different approach in one project many years > ago back in Cayenne 1.1, and I've continued using it up to this point > in 3.x. I generated special setter, addTo, and removeFrom methods as > well as a create method which created the logger object at that point. > > To get the primary key, I set up one-way object relationships between > the primary key of the logged object and the foreign key storage field > in the audit log, one for each object entity. When the commit > happened, the relationships automatically populated the audit log > fields. You probably can do the same thing. There's probably a > better way to set up the object relationships than manually defining > them in your model these days. > > In a different Cayenne 1.2 project, I used something similar to your > pre-persist hook, although we didn't have prePersist yet. For this > one, I had two foreign record key fields, one used for a single column > primary key and one used for compound primary keys. I'm not sure if > the code will still work outside of 1.2 since this project was never > upgraded, but here it is in case you want to try this approach and > adapt it to a more recent version of Cayenne. > setForeignKeyRepresentation(Map pkAttributes, DbEntity dbEntity, Map > auditRecordMap) and the code that calls it after setting pkAttributes > would likely be what you want to reference for fetching the primary > key dynamically. > > > import java.io.Serializable; > import java.util.ArrayList; > import java.util.Collections; > import java.util.Date; > import java.util.HashMap; > import java.util.Iterator; > import java.util.List; > import java.util.Map; > > import org.objectstyle.cayenne.CayenneRuntimeException; > import org.objectstyle.cayenne.ObjectId; > import org.objectstyle.cayenne.access.DataContext; > import org.objectstyle.cayenne.access.DataDomainFlushObserver; > import org.objectstyle.cayenne.access.DataNode; > import org.objectstyle.cayenne.access.DefaultDataContextDelegate; > import org.objectstyle.cayenne.dba.PkGenerator; > import org.objectstyle.cayenne.map.DataMap; > import org.objectstyle.cayenne.map.DbAttribute; > import org.objectstyle.cayenne.map.DbEntity; > import org.objectstyle.cayenne.map.ObjEntity; > import org.objectstyle.cayenne.query.BatchQuery; > import org.objectstyle.cayenne.query.DeleteBatchQuery; > import org.objectstyle.cayenne.query.InsertBatchQuery; > import org.objectstyle.cayenne.query.UpdateBatchQuery; > > public class AuditLoggingDataContextDelegate extends > DefaultDataContextDelegate implements Serializable > { > public static final String MOD_TYPE_INSERT = "I"; > public static final String MOD_TYPE_UPDATE = "U"; > public static final String MOD_TYPE_DELETE = "D"; > > public void finishedRunQueries(DataContext dataContext, List queryList) { > super.finishedRunQueries(dataContext, queryList); > > Date modificationDate = new Date(); > > List auditRecordMapList = new ArrayList(); > > Iterator queryIterator = queryList.iterator(); > while (queryIterator.hasNext()) { > BatchQuery batchQuery = (BatchQuery) queryIterator.next(); > > if (batchQuery instanceof InsertBatchQuery) > { > InsertBatchQuery insertBatchQuery = > (InsertBatchQuery)batchQuery; > insertBatchQuery.reset(); > > List dbAttributes = insertBatchQuery.getDbAttributes(); > while(insertBatchQuery.next()) { > for(int i = 0; i < dbAttributes.size(); i++) { > Map auditRecordMap = new HashMap(); > > DbAttribute dbAttribute = (DbAttribute) > dbAttributes.get(i); > Object value = insertBatchQuery.getValue(i); > > DbEntity dbEntity = (DbEntity)dbAttribute.getEntity(); > auditRecordMap.put("MOD_TIME", modificationDate); > auditRecordMap.put("SCHEMA_NAME", dbEntity.getSchema()); > auditRecordMap.put("TBL_NAME", dbEntity.getName()); > auditRecordMap.put("COL_NAME", dbAttribute.getName()); > auditRecordMap.put("MOD_TYPE", MOD_TYPE_INSERT); > if (null != value) > { > auditRecordMap.put("NEW_VALUE", value.toString()); > } > > Map pkAttributes; > ObjectId objectId = insertBatchQuery.getObjectId(); > if (null != objectId) > { > pkAttributes = objectId.getIdSnapshot(); > } > else > { > pkAttributes = > insertBatchQuery.getCurrentObjectSnapshot(); > } > setForeignKeyRepresentation(pkAttributes, > dbEntity, auditRecordMap); > > auditRecordMapList.add(auditRecordMap); > } > } > } > else if (batchQuery instanceof DeleteBatchQuery) > { > DeleteBatchQuery deleteBatchQuery = > (DeleteBatchQuery)batchQuery; > deleteBatchQuery.reset(); > > List dbAttributes = deleteBatchQuery.getDbAttributes(); > while(deleteBatchQuery.next()) { > for(int i = 0; i < dbAttributes.size(); i++) { > Map auditRecordMap = new HashMap(); > > DbAttribute dbAttribute = (DbAttribute) > dbAttributes.get(i); > > DbEntity dbEntity = (DbEntity)dbAttribute.getEntity(); > auditRecordMap.put("MOD_TIME", modificationDate); > auditRecordMap.put("SCHEMA_NAME", > dbEntity.getSchema()); > auditRecordMap.put("TBL_NAME", dbEntity.getName()); > auditRecordMap.put("COL_NAME", dbAttribute.getName()); > auditRecordMap.put("MOD_TYPE", MOD_TYPE_DELETE); > > > setForeignKeyRepresentation(deleteBatchQuery.getCurrentQualifier(), > dbEntity, auditRecordMap); > > auditRecordMapList.add(auditRecordMap); > } > } > } > else if (batchQuery instanceof UpdateBatchQuery) > { > UpdateBatchQuery updateBatchQuery = > (UpdateBatchQuery)batchQuery; > updateBatchQuery.reset(); > > List dbAttributeList = updateBatchQuery.getUpdatedAttributes(); > while(updateBatchQuery.next()) { > for(int i = 0; i < dbAttributeList.size(); i++) { > Map auditRecordMap = new HashMap(); > > DbAttribute dbAttribute = (DbAttribute) > dbAttributeList.get(i); > Object newValue = updateBatchQuery.getValue(i); > > Object oldValue = updateBatchQuery.getOldValue(i); > > DbEntity dbEntity = (DbEntity)dbAttribute.getEntity(); > auditRecordMap.put("MOD_TIME", modificationDate); > auditRecordMap.put("SCHEMA_NAME", dbEntity.getSchema()); > auditRecordMap.put("TBL_NAME", dbEntity.getName()); > auditRecordMap.put("COL_NAME", dbAttribute.getName()); > auditRecordMap.put("MOD_TYPE", MOD_TYPE_UPDATE); > if (null != oldValue) > { > auditRecordMap.put("OLD_VALUE", > oldValue.toString()); > } > if (null != newValue) > { > auditRecordMap.put("NEW_VALUE", > newValue.toString()); > } > > > setForeignKeyRepresentation(batchQuery.getObjectId().getIdSnapshot(), > dbEntity, auditRecordMap); > > auditRecordMapList.add(auditRecordMap); > } > } > } > } > > processAuditRecordMapList(dataContext, auditRecordMapList); > } > > protected void processAuditRecordMapList(DataContext dataContext, > List auditRecordMapList) > { > SecIndividual secIndividual = > (SecIndividual)dataContext.getUserProperty("secIndividual"); > SecSystem secSystem = > (SecSystem)dataContext.getUserProperty("secSystem"); > > // Sort into ChangeLog records > Map changeLogMap = new HashMap(); > Iterator auditRecordMapIterator = auditRecordMapList.iterator(); > while (auditRecordMapIterator.hasNext()) { > Map auditRecordMap = (Map) auditRecordMapIterator.next(); > > auditRecordMap.put("SYSTEM_ID", secSystem.getPrimaryKey()); > auditRecordMap.put("REAL_USER_ID", secIndividual.getPrimaryKey()); > auditRecordMap.put("EFFECTIVE_USER_ID", > secIndividual.getPrimaryKey()); > > String tableName = (String)auditRecordMap.get("TBL_NAME"); > DbEntity dbEntity = > dataContext.getEntityResolver().getDbEntity(tableName); > DataMap dataMap = dbEntity.getDataMap(); > String changeLogObjEntityName = "ChangeLog" + dataMap.getName(); > ObjEntity changeLogObjEntity = > dataMap.getObjEntity(changeLogObjEntityName); > DbEntity changeLogDbEntity = changeLogObjEntity.getDbEntity(); > > List changeLogList = (List)changeLogMap.get(changeLogDbEntity); > if (null == changeLogList) > { > changeLogList = new ArrayList(); > changeLogMap.put(changeLogDbEntity, changeLogList); > } > > changeLogList.add(auditRecordMap); > } > > Iterator changeLogMapEntryIterator = > changeLogMap.entrySet().iterator(); > while (changeLogMapEntryIterator.hasNext()) { > Map.Entry changeLogEntry = (Map.Entry) > changeLogMapEntryIterator.next(); > DbEntity changeLogDbEntity = (DbEntity)changeLogEntry.getKey(); > List changeLogList = (List)changeLogEntry.getValue(); > > InsertBatchQuery batch = new > InsertBatchQuery(changeLogDbEntity, changeLogList.size()); > > DataNode node = > dataContext.getParentDataDomain().lookupDataNode(changeLogDbEntity.getDataMap()); > PkGenerator pkGenerator = node.getAdapter().getPkGenerator(); > List dbAttributeList = changeLogDbEntity.getPrimaryKey(); > if (1 != dbAttributeList.size()) > { > throw new CayenneRuntimeException("Compound primary key"); > } > DbAttribute keyAttribute = (DbAttribute)dbAttributeList.get(0); > String key = keyAttribute.getName(); > Iterator changeLogIterator = changeLogList.iterator(); > while (changeLogIterator.hasNext()) { > Map auditRecordMap = (Map) changeLogIterator.next(); > ObjectId id = > createObjectIdForAuditLog(changeLogDbEntity, node, pkGenerator, key); > auditRecordMap.put(key, id.getIdSnapshot().get(key)); > batch.add(auditRecordMap, id); > } > > DataDomainFlushObserver observer = new DataDomainFlushObserver(); > node.performQueries(Collections.singletonList(batch), observer); > } > > } > > private ObjectId createObjectIdForAuditLog(DbEntity > changeLogDbEntity, DataNode node, PkGenerator pkGenerator, String key) > throws CayenneRuntimeException { > Object pkValue; > try { > pkValue = pkGenerator.generatePkForDbEntity(node, > changeLogDbEntity); > } catch (Exception e) { > throw new CayenneRuntimeException("Error generating audit > log primary keys", e); > } > ObjectId id = new ObjectId(changeLogDbEntity.getName(), key, pkValue); > return id; > } > > private void setForeignKeyRepresentation(Map pkAttributes, > DbEntity dbEntity, Map auditRecordMap) { > Integer primaryKeyOfRecord = null; > String primaryKeysString = null; > > // References to the record that was changed (FK_C is for > compound keys, FK is for a single integer key). > > if (1 == pkAttributes.size()) > { > Iterator pkIterator = pkAttributes.keySet().iterator(); > String primaryKeyName = (String) pkIterator.next(); > Object pkObject = pkAttributes.get(primaryKeyName); > if (pkObject instanceof Integer) > { > primaryKeyOfRecord = (Integer)pkObject; > } > } > > if (null == primaryKeyOfRecord) > { > Iterator pkIterator = pkAttributes.keySet().iterator(); > while (pkIterator.hasNext()) > { > String primaryKeyName = (String) pkIterator.next(); > Object primaryKeyValue = pkAttributes.get(primaryKeyName); > > if (null == primaryKeysString) > { > primaryKeysString = primaryKeyName + "=" + primaryKeyValue; > } > else > { > primaryKeysString = primaryKeysString + "," + > primaryKeyName + "=" + primaryKeyValue; > } > } > } > > if (null != primaryKeyOfRecord) > { > auditRecordMap.put("FOREIGN_KEY", primaryKeyOfRecord); > } > if (null != primaryKeysString) > { > auditRecordMap.put("FKEY_CONDITION", primaryKeysString); > } > } > } > > > > On Mon, Aug 10, 2015 at 7:27 AM, Aristedes Maniatis <a...@maniatis.org> wrote: >> On 10/08/2015 8:31pm, Hugi Thordarson wrote: >>> Is it possible for me to obtain the primary key for a Cayenne DataObject >>> before committing changes? I’m writing an audit log and I need the key for >>> the object during PrePersist (where I’m constructing the log object). >> >> How will it have a primary key before the record is written to the database? >> Or do you want to hang onto the temporary ObjectId and then replace it with >> the real PK after the commit? >> >> Ari >> >> >> -- >> --------------------------> >> Aristedes Maniatis >> GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A