I am currently evaluating Javers on whether or not we could use it to audit our data.
We currently have an "in house" solution which is not doing the job.
However there is one functionality that I cannot find in Javers and I'm wondering if it's because I don't know how to look or if it is because it does not exist.
Is it possible, with Javers, to store contextual informations like, for example, the source of the change (manual change, event change...).
Our need is not just to identify what has been changed but also in what context.
What I have found so far, is the properties map that can be provided when commiting. But I'm wondering if you can add actual entities to the context and then be able to use Javers queries to search by context.
Example :
I have Entity1, Entity2 and Entity3:
* when I commit changes on Entity3, I add to the context Entity1
* when I commit changes on Entity2, I add to the context Entity1
I am then able to search all commits that have impacted Entity1 via this context.
I realize that this may be out of scope for an audit engine.
Can anyone help me ?
Thanks.
Related
I want to persist an entity that has a #OneToMany relationship to a child entity. I'm using Quarkus 1.13.1 with Quarkus Panache.
Example
public class User {
private List<Item> items;
#OneToMany(cascade = CascadeType.ALL)
public List<Item> getItems()...
}
If I want to persist a user (user.persist()) with a few items that already exist in the item table, then I get of course a "duplicate key" exception. So far so good.
But I was wondering if there is a descent way to skip/ignore an insert if an item already exists in the table items.
Of course, I could query the database to check if the child value exists, but this seems somehow tedious and bloats the code with data checks, so I was wondering if there was some annotation or other shortcut to handle this.
A persist operation should be used exclusively to create (store) new objects in the database, and makes the Java objects managed by Hibernate until the Session is closed.
It's really important that you know which objects are managed, and which are not, and distinguish wich ones are newly made persistent rather than just represent an existing object in the database.
To this end, it would indeed be better to load the existing Items first; if you know for sure which ones are already existing in the DB you can use a lazy proxy to represent them and put those in the list before persisting the User.
If you don't know which Items already exist in the database, then you should indeed have to query the database first. There is no shortcut for this operation; I guess we could explore some improvements but generally automating such things is tricky.
I would suggest implement the checks explicitly so you have full control over the strategy. It might be a good idea to make Item a cached entity so you can implement safe validations without performance drawbacks.
I would like to make complex validations when save or update an Entity.
For example I'd like to check is one of the entity's property is unique, but trough complex conditions I can't declare in unique constraints.
I use #PrePersist for new entities, and #Pre/PostUpdate for existing ones. #PrePersist works well in all cases, but different errors occurred while updating existing entities.
If I inject my CRUD service into listener, and check is there any existing records based on property value I get stack overflow exception - I think because every time I call CRUD service find method Hibernate tries to update the entity before run query, and the causes SO-.
It is not a good practice to user CRUD service in EntityListener?
The other problem I don't know how to solve, if value cannot be persisted, I'd like to throw custom exception to inform the frontend about it.
If I call saveAndFlush() just my exception is thrown. But If I use just save() a TransactionSystemException is also thrown after my custom exception and that TransactionSystemException will be populated to frontend instead of my exception.
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
How can I prevent RollbackException?
Is it a good idea at all to check these restrictions in EntityListener? My goal is to implement a layer where these restrictions automatically validated.
I would like to make complex validations when save or update an Entity. For example I'd like to check is one of the entity's property is unique, but trough complex conditions I can't declare in unique constraints.
You should probably use database that has support for this then because you will have a hard time getting this right and fast without that. PostgreSQL allows you to specify partial unique indexes, which essentially are unique constraints for a subset of the data. You can do e.g. create unique index abc on tbl (tenant_id, some_code) where deleted = false
If this doesn't work for you, you will probably have to use the SERIALIZABLE isolation level to ensure correctness, or use some kind of global lock.
I have an entity with several fields, but on one view i want to only edit one of the fields. for example... I have a user entity, user has, id, name, address, username, pwd, and so on. on one of the views i want to be able to change the pwd(and only the pwd). so the view only knows of the id and sends the pwd. I want to update my entity without loading the rest of the fields(there are many many more) and changing the one pwd field and then saving them ALL back to the database. has anyone tried this. or know where i can look. all help is greatly appreciated.
Thx in advance.
PS
i should have given more detail. im using hibernate, roo is creating my entities. I agree that each view should have its own entity, problem is, im only building controllers, everything was done before. we were finders from the service layer, but we wanted to use some other finders, they seemed to not be accessible through the service layer, the decision was made to blow away the service layer and just interact with the entities directly (through the finders), the UserService.update(user) is no longer an option. i have recently found a User.persist() and a User.merge(), does the merge update all the fields on the object or only the ones that are not null, or if i want one to now be null how would it know the difference?
Which technologies except Spring are you using?
First of all have separate DTOs for every view, stripped only to what's needed. One DTO for id+password, another for address data, etc. Remember that DTOs can inherit from each other, so you can avoid duplication. And never pass business/ORM entities directly to view. It is too risky, leaks in some frameworks might allow users to modify fields which you haven't intended.
After the DTO comes back from the view (most web frameworks work like this) simply load the whole entity and fill only the fields that are present in the DTO.
But it seems like it's the persistence that is troubling you. Assuming you are using Hibernate, you can take advantage of dynamic-update setting:
dynamic-update (optional - defaults to false): specifies that UPDATE SQL should be generated at runtime and can contain only those columns whose values have changed.
In this case you are still loading the whole entity into memory, but Hibernate will generate as small UPDATE as possible, including only modified (dirty) fields.
Another approach is to have separate entities for each use-case/view. So you'll have an entity with only id and password, entity with only address data, etc. All of them are mapped to the same table, but to different subset of columns. This easily becomes a mess and should be treated as a last resort.
See the hibernate reference here
For persist()
persist() makes a transient instance persistent. However, it does not guarantee that the
identifier value will be assigned to the persistent instance immediately, the assignment
might happen at flush time. persist() also guarantees that it will not execute an INSERT
statement if it is called outside of transaction boundaries. This is useful in long-running
conversations with an extended Session/persistence context.
For merge
if there is a persistent instance with the same identifier currently associated with the session, copy the state of the given object onto the persistent instance
if there is no persistent instance currently associated with the session, try to load it from the database, or create a new persistent instance
the persistent instance is returned
the given instance does not become associated with the session, it remains detached
persist() and merge() has nothing to do with the fact that the columns are modified or not .Use dynamic-update as #Tomasz Nurkiewicz has suggested for saving only the modified columns .Use dynamic-insert for inserting not null columns .
Some JPA providers such as EclipseLink support fetch groups. So you can load a partial instance and update it.
See,
http://wiki.eclipse.org/EclipseLink/Examples/JPA/AttributeGroup
I'm working a custom workflow activity and would like to allow the user to select one of the entities available from within the workflow. This would be like selecting the entity in an update status activity. The list would include the primary entity, all of its associated parent entities, and any entities created within the workflow (e.g. if I created a task with the create activity, that task would be in the list).
Is there some way to do this?
Thanks!
Workflow has major shortcomings when it comes to things like this. You can add metadata to dependencies ( [CrmInput] and [CrmReferenceTarget("account")] ), but you'll need a property per entity you intend to support. I don't know if you could include multiple CrmReferenceTarget tags per property.
So even if you go through setting up a workflow activity with every lookup (which will require a code change for every new entity), you still have to take the care to set the right lookup in your code - and choose from ALL (not filtered based on your entity). Which is clearly not what you want.
I've made a custom entity that will work as an data modification audit (any entity modified will trigger creating an instance of this entity). So far I have the plugin working fine (tracking old and new versions of properties changed).
I'd like to also keep track of what entity this is related to. At first I added a N:1 from DataHistory to Task (eg.) and I can indeed link back to the original task (via a "new_tasksid" attribute I added to DataHistory).
The problem is every entity I want to log will need a separate attribute id (and an additional entry in the form!)
Looking at how phone, task, etc utilize a "regardingobjectid", this is what I should do. Unfortunately, when I try to add a "dataobjectid" and map it to eg Task and PhoneCall, it complains (on the second save), that the reference needs to be unique. How does the CRM get around this and can I emulate it?
You could create your generic "dataobjectid" field, but make it a text field and store the guid of the object there. You would lose the native grids for looking at the audit records, and you wouldn't be able to join these entities through advanced find, fetch or query expressions, but if that's not important, then you can whip up an ASPX page that displays the audit logs for that record in whatever format you choose and avoid making new relationships for every entity you want to audit.
CRM has a special lookup type that can lookup to many entity types. That functionality isn't available to us customizers, unfortunately. Your best bet is to add each relationship that could be regarding and hide the lookups that aren't in use for this particular entity.