How can we audit a field of an entity according to a condition? - hibernate-envers

To audit an entity conditionally, there is the option of using the Integrators and extends Envers event liteners.
But can we audit a field or a property conditionally ?
In our case , we have a blob column, and to avoid the increase of the volume of the audit tables , we want to set the value of this column only when a condition is valid. Is there any way to do it ?
Thanks

As indicated, the current and only way to do Conditional Auditing is to extend the Envers listeners as you described, register your custom ones via an Integrator and make sure to configure Envers not skip its event listener registration step.
As an example, lets assume you have extended EnversPostUpdateEventListenerImpl:
public class CustomPostUpdateEventListener extends EnversPostUpdateEventListenerImpl {
#Override
public void onPostUpdate(PostUpdateEvent event) {
if ( event.getEntity() instanceof YourCustomEntityType ) {
if ( !isSpecialConditionSet( event ) ) {
return;
}
}
super.onPostUpdate( event );
}
private boolean isSpecialConditionSet(PostUpdateEvent event) {
final Object conditionValue = event.getPersister()
.getPropertyValue( event.getEntity(), "nameOfPropertyCondition" );
/* check your condition and return true if you should not audit the entity */
}
}
One thing to consider when doing Conditional Auditing is when a PostInsertEvent for an entity is fired, it should not be considered conditional if you use the ValidityAuditStrategy to avoid downstream issues when you update that entity in a future transaction.

Related

TimeStamp with EF Core 2.1 and Sql Server?

I want to have basically on most of my tables a "Last Updated" column so that I can quickly check when it was last updated.
Right now I been just putting a datetime and every time I do an action in my C# code I will save the new DateTime to update that field. Of course this can lead to me forgetting to do this.
I want something more automatic. I don't need this really for auditing purposes so it does not need to be too advanced.
I tried
builder.Property(x => x.RowVersion).IsRowVersion();
what should make a timestamp but when I look at in the database I see 0x00000000000007D1 when I thought it would look more like a datetime.
edit
public class CompanyConfig : IEntityTypeConfiguration<Company>
{
public void Configure(EntityTypeBuilder<Company> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Id).ValueGeneratedOnAdd();
builder.Property<DateTime>("LastUpdated");
}
}
Assuming you have defined a shadow property LastModified for each entity you want that functionality as in your example
builder.Property<DateTime>("LastUpdated");
and the question is how to update it automatically.
The technique described in the David's answer is outdated. It's still possible to override SaveChanges (and SaveChangesAsync), but there is IMHO a better approach, shown in Populate Created and LastModified automagically in EF Core. It's interface based approach, but can easily be adjusted for shadow property (or any property - the methods used work for both shadow and explicit properties).
Quick recap: Starting with v2.1, EF Core provides State change events:
New Tracked And StateChanged events on ChangeTracker can be used to write logic that reacts to entities entering the DbContext or changing their state.
Here is how you can utilize them. Add the following to your derived context class:
void SubscribeStateChangeEvents()
{
ChangeTracker.Tracked += OnEntityTracked;
ChangeTracker.StateChanged += OnEntityStateChanged;
}
void OnEntityStateChanged(object sender, EntityStateChangedEventArgs e)
{
ProcessLastModified(e.Entry);
}
void OnEntityTracked(object sender, EntityTrackedEventArgs e)
{
if (!e.FromQuery)
ProcessLastModified(e.Entry);
}
void ProcessLastModified(EntityEntry entry)
{
if (entry.State == EntityState.Modified || entry.State == EntityState.Added)
{
var property = entry.Metadata.FindProperty("LastModified");
if (property != null && property.ClrType == typeof(DateTime))
entry.CurrentValues[property] = DateTime.UtcNow;
}
}
and add
SubscribeStateChangeEvents();
to your derived context constructor(s).
And that's all. Once the context is subscribed to these events, it will be notified anytime the entity is initially tracked or its state changes. You are interested only with Modified and Added state not triggered by query materialization (if you don't need Added, then just exclude it from the if condition). Then you check if the entity contains DateTime LastModified property using the EF Core metadata services and if yes, simply update it with the current date/time.
The pattern in EF Core for this is to make the "LastUpdated" a Shadow Property, and update it during SaveChanges().
See, eg: Implementing Common Audit Fields with EF Core’s Shadow Property
By looking at the DbContext class documentation, you can override SaveChanges method and set the LastUpdated values in there, before continuing with base.SaveChanges() at the end.
One last piece - inside SaveChanges method, you can find the modified/newly added entities using ChangeTracker property.

How to give a fixed Uid to my Action

Hy,
I'm trying to call my action with allways a fixed Uid (configured by TS) so I could put a plugin on my page to register for a specific Event. And don't have to go over a Event List click the Event click register.
I tried the following which did not work out:
public function newAction(
\XYZ\xyz\Domain\Model\Registration $newRegistration = NULL,
\XYZ\xyz\Domain\Model\Event $event = 'DD8B2164290B40DA240D843095A29904'
)
The next didn't one work either!
public function newAction(
\XYZ\xyz\Domain\Model\Registration $newRegistration = NULL,
\XYZ\xyz\Domain\Model\Event $event = Null
) {
$myinstance = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
'XYZ\\xyz\\Domain\\Model\\Event'
);
$event = $myinstance->findByUid('DD8B2164290B40DA240D843095A29904');
.......
}
So I was woundering is there a way to give my fixed Uid to the action?
In TYPO3 calling Extbase actions is done in the routing and dispatching components - to pass anything from the outside that is different from a numeric uid value a custom property TypeConverter would have to be implemented that transforms a particular string pattern into a value domain object of type Event.
However, there's a simpler approach by using configuration:
1) Provide configuration in TypoScript
Extbase uses a strong naming convention based on the extension name and optionally the plugin name. Thus, either tx_myextension or tx_myextension_someplugin can be used - latter is more specific for for according somePlugin. Besides that settings are automatically forwarded and provided in an Extbase controller context - accessible by $this->settings.
plugin.tx_xyz {
settings {
newActionEventIdentifier = DD8B2164290B40DA240D843095A29904
}
}
2) Retrieve data via repository
\XYZ\xyz\Domain\Repository\EventRepository
Use a dedicated EventRepository::findByIdentifier(string) method to retrieve the data. The property names are just assumptions since there are no explicit mentions how exactly the event data is persisted and whether it is persisted in a relational DBMS at all.
<?php
namespace XYZ\xyz\Domain\Repository;
class EventRepository
{
public function findByIdentifier($identifier)
{
$query = $this->createQuery();
$query->matching(
$query->equals('event_id', $identifier)
);
return $query->execute();
}
}
3) Putting all together in the according controller
The $event property was removed from the action since that entity is pre-defined and cannot be submitted from the outside (and to support the string to Event entity transformation a custom TypeConverter would be required as mentioned earlier).
public function newAction(
\XYZ\xyz\Domain\Model\Registration $newRegistration = null
) {
$event = $this->eventRepository->findByIdentifier(
$this->settings['newActionEventIdentifier']
);
if ($event === null) {
throw new \RuntimeException('No event found', 1522070079);
}
// the regular controller tasks
$this->view->assign(...);
}

change the OptimisticLockPolicy to use local-time

I'm using Eclipselink JPA, I have an Entity with a Timestamp field annotated with #Version por optimistic locking.
By default, this sets the entitymanager to use database time, so, if I have to do a batch update it doesn't work properly as it query the database for time each time it wants to do an insert.
How can I change the TimestampLockingPolicy to use LOCAL_TIME?
The class org.eclipse.persistence.descriptors.TimestampLockingPolicy.class has a public method useLocalTime() but I dont know how to use or, from where should I call it.
Found the answer:
first lets create a DescriptorCustomizer
public class LocalDateTimeCustomizer implements DescriptorCustomizer {
#Override
public void customize(ClassDescriptor descriptor) throws Exception {
OptimisticLockingPolicy policy = descriptor.getOptimisticLockingPolicy();
if (policy instanceof TimestampLockingPolicy) {
TimestampLockingPolicy p = (TimestampLockingPolicy) policy;
p.useLocalTime();
}
}
}
then annotate the entity that has the #Version with
#Customizer(LocalDateTimeCustomizer.class)

cassandra trigger create vs update

I need to implement a cassandra trigger in java that will take a different action for INSERT vs UPDATE operations.
I've seen how it is possible to identify a DELETE operation in the question Cassandra sample trigger to get the deleted row and column values but I can't see any methods in the ColumnFamily object that would allow allow the code to differentiate between INSERT vs UPDATE, is there a way to achieve this?
There isn't conceptually any difference between INSERT and UPDATE. Indeed an INSERT and an UPDATE are just mutation. Cassandra gives them different name so that people coming from the relational DB have familiar concepts
As #doanduyhai mentioned and according to Casssandra sources only INSERT operation updates row timestamp (LivenessInfo).
For other operations this timestamp is not updated and equals Long.MIN_VALUE.
Custom Trigger which checks for INSERT operation
public class CustomTrigger implements ITrigger {
#Override
public Collection<Mutation> augment(Partition partition) {
checkQueryType(partition));
return Collections.emptyList();
}
private void checkQueryType(Partition partition) {
UnfilteredRowIterator it = partition.unfilteredIterator();
while (it.hasNext()) {
Unfiltered unfiltered = it.next();
Row row = (Row) unfiltered;
if (isInsert(row)) {
// Implement insert related logic
}
}
}
private boolean isInsert(Row row) {
return row.primaryKeyLivenessInfo().timestamp() != Long.MIN_VALUE;
}
}

GWT - How to add more than 1 CellTables on the same page

I have a page using StackLayoutPanel.
In that 3 stacks requires separate CellTables.
Currently events I need to use are RangeChangeEvent, SelectionChangeEvent.
Now question is how to differentiate OnRangeChangeEvent from one another.
The source table is referenced by the event that you are catching. "event.getSource()"
always gives a reference to the object that caused the event.
For example, if you have three tables, and you attach the same handler as below:
RangeChangeEvent.Handler handler = new RangeChangeEvent.Handler() {
#Override
public void onRangeChange(RangeChangeEvent event) {
if(table1 == event.getSource()){
// first table
} else if (table2 == event.getSource()){
// second table
} else if (table3 == event.getSource()){
// third table
}
};
table1.addRangeChangeHandler(handler);
table2.addRangeChangeHandler(handler);
table3.addRangeChangeHandler(handler);
The above example assumes that there is no selection model specified on the tables. If there is, the selection model will be the source of the events.
Alternatively, you can just attach a table-specific handler to each table:
RangeChangeEvent.Handler handler1 = new RangeChangeEvent.Handler() {
#Override
public void onRangeChange(RangeChangeEvent event) {
// Handle stuff happening to table 1
}
};
RangeChangeEvent.Handler handler2 = new RangeChangeEvent.Handler() {
#Override
public void onRangeChange(RangeChangeEvent event) {
// Handle stuff happening to table 2
}
};
table1.addRangeChangeHandler(handler1);
table2.addRangeChangeHandler(handler2);
// And so on for any more tables
With this approach you won't need to worry about the event source, as you already know which handler corresponds to which table.
Were you setting the same event handlers for each CellTable? If so, don't. You can simply make a different handler for each CellTable. While it would theoretically be possible to detect the source as in filip-fku's example, it won't be if you are using SelectionModel.
Bottom line: you should not try to use the same handlers on multiple objects unless you absolutely have to.