Teneo supports auditing (from November 2012), a summary of the functionality:
- auditing can be enabled/disabled for the complete model or for specific types
- possible to navigate/iterate over the history of an object
- use HQL to query for version history using attribute and ereference values
Auditing can be enabled by calling setAuditing(true) on the datastore or setting the enable auditing option to true (see the 'Auditing Options' section). To prevent auditing of specific types you can use the @NoAuditing annotation. Set this annotation in the EMF EAnnotation of the EClass using source 'teneo.jpa' and key 'value'.
When auditing is enabled then at each database action on an entity its state is persisted in a separate table. To support this persistence, Teneo will generate a separate additional type for each audited type, these audit types/eclasses are generated and mapped at runtime.
Each audit EClass models the audit entries for a domain EClass, its audit entries are stored in a separate table. Teneo will store three types of events: ADD, UPDATE and DELETE.
Teneo provides an api to retrieve the audit entries, this is described in more detail below.
Teneo has many configuration options, the following options are specific for auditing:
- ENABLE_AUDITING (teneo.mapping.auditing.enable): default is false, enables auditing if set to "true"
- AUDITING_PRUNE_OLD_ENTRIES_DAYS (teneo.mapping.auditing.prune.days): default is 0, if set > 0, then audit entries older than this number of days are deleted, this is done during the operation of the system (every 1000 commits a prune check is done and old entries are removed).
- AUDITING_PRUNE_COMMIT_INTERVAL (teneo.mapping.auditing.prune.commit.interval): default is 1000, defines the number of commits before Teneo checks for to-be-pruned entries. Only commits which update the audit entry tables are counted. So with the default, every 1000 commits, all audit tables are checked for expired entries.
- AUDITING_DB_SCHEMA (teneo.mapping.auditing.database.schema): database schema for the auditing tables, default is not set.
- AUDITING_ENTITY_PREFIX (teneo.naming.auditing.entity.prefix): defines the prefix which is set before the EClass name, this is used to compute the entity name and the table name used to store auditing entries.
- AUDITING_ENTITY_POSTFIX (teneo.naming.auditing.entity.postfix): (default is 'Auditing') defines the prefix which is set before the EClass name, this is used to compute the entity name and the table name used to store auditing entries.
Retrieving Audit Entries
Audit entries can be retrieved using the AuditVersionProvider. The AuditVersionProvider can be retrieved from the data store using the getAuditVersionProvider method.
The AuditVersionProvider provides several methods to retrieve the history of an object. You can retrieve the latest audit entries or all audit entries for an object. See the getAllAuditEntries, getLatestAuditEntry methods.
From an audit entry you can find the next or previous entries using the getNextEntry and getPreviousEntry methods of the AuditVersionProvider.
Each audit type/EClass is mapped to hibernate as a standard type. This means that you can query for audit entries using HQL.
Some things to note when querying:
- Selecting on primitive types can be done in the same way as for the standard type.
- the EClass name of the audit entry is controlled by the options discussed above, as a default the term Auditing is appended to the original EClass name, so if the domain EClass is Book, then the auditing EClass will be BookAuditing.
- Teneo stores all references as strings, so association joining is not directly possible. Also when filtering on an EReference association you should use the id string representation of an entity. To get the id string representation call the getIdString method on the AuditVersionProvider.
This code snipper illustrates how to query for audit entries:
final AuditVersionProvider vp = testStore.getEmfDataStore().getAuditVersionProvider(); // select on pages and the author final Query qry = testStore.getSession().createQuery("select e from BookAuditing e where pages=1 and author=:author"); // set the author parameter, create the id string using the getIdString method. qry.setParameter("author", vp.getIdString(writer)); final List<?> list = qry.list();
The following test cases give examples on how to iterate over audit entries or query for audit entries:
An audit entry represents the state at a certain point in time. Teneo also provides an api to get the actual instance with the values/references at a certain timestamp. The special feature is that also referenced objects are in the same state.
For example, a Book references a Writer, then when retrieving a Book in the state it has on the 1st of September 2011, then the referenced Writer will also be in the state of the 1st of September 2011.
To get actual instances you can use the getRevision methods of the AuditVersionProvider class.
Note that the AuditVersionProvider internally uses a Resource to resolve proxies against the audit entries in the database. This means that revisions retrieved through the getRevision method are part of a resource.
If a referenced entity is not audited (so there are no audit entries in the database) then the referenced entity is read from the database directly.
Commit info, username
For each commit a separate commit info record is created in the database. To control the user name in the commit info a thread local is available in the AuditProcessHandler class. You can get the AuditProcessHandler from the data store (there is a getter).
You can tell Teneo to automatically prune older entries. This is controlled with 2 options:
- AUDITING_PRUNE_OLD_ENTRIES_DAYS option controls how many days old entries are removed
- AUDITING_PRUNE_COMMIT_INTERVAL option controls the interval by which Teneo checks the audit tables, so with a value of 1000, every 1000 commits, the audit tables are checked for expired entries, and they are removed. Only commits which update the audit entry tables are considered for the interval.
The pruning of old entries is done in a separate transaction.
Auditing database schema/model
For single-occurence (isMany==false) EAttributes the annotations defined in the teneo.jpa EAnnotations are copied over to the auditing model element. For all other types of model elements or for specific behavior you can control the auditing database schema through EAnnotations with a specific source (teneo.jpa.auditing).
You can set table names, column names, join tables etc. To do this, you can make use of the same annotation functionality as available for standard domain types. The auditing annotations are specified on the domain types using a specific annotation source: teneo.jpa.auditing.
See also the AUDITING_DB_SCHEMA database schema discussed above in the wiki page, this option controls the database schema of the auditing tables.
Auditing does not work in combination with specific functionality:
- EmbeddedId is not supported, this is neither supported with Hibernate envers.
- EAV mapping can not be combined with auditing. This can be solved, but requires some additional work, please ask on the newsgroup if relevant for your case.
- OneToOne in combination with orphan removal does not work currently because of details in the flush process.
- Embeddable/Embedded works fine in combination with auditing, but only if annotated in the model, so not using a separate xml/hbm mapping file. This can be solved, but needs some additional work, please ask on the newsgroup if relevant for your case.
- The delete audit entry does not contain any collection values