Our application is using Hibernate envers to provide auditing on updates to entities. Whenever an entity is modified (or inserted), a backup of the entity (i.e. the table’s row) is saved to an "AUD" table.
This provides us with auditing functionality (which is a requirement) and has been working well up until now. But we are now facing issues because we need to migrate to using UUIDs as data keys. Our application will be run in a distributed environment with limited or intermittent internet access, so we will be using SymmetricDS to manage data synchronisation, and UUIDs will allow us to do that without causing data conflicts.
The problem we are facing with Hibernate envers is that the RevisionNumber annotation that is used to add the REV number into the AUD table will only work with int, Integer, long and Long identifiers.
We have implemented a class called AuditRevision (that extends the Hibernate class DefaultRevisionEntity) with some extra attributes, and have implemented an AuditRevisionListener (that implements Hibernate’s EntityTrackingRevisionListener) to manage saving and updating the extra data. However, in order to allow us to save revisions using UUIDs instead of an auto incrementing id, we rewrote AuditRevision so that it doesn't extend DefaultRevisionEntity and instead defines its own UUID id.
We then saw the following error in our application:
Caused by: org.hibernate.MappingException: The field annotated with #RevisionNumber must be of type int, Integer, long or Long
at org.hibernate.envers.configuration.internal.RevisionInfoConfiguration.searchForRevisionInfoCfgInProperties(RevisionInfoConfiguration.java:224)
at org.hibernate.envers.configuration.internal.RevisionInfoConfiguration.searchForRevisionInfoCfg(RevisionInfoConfiguration.java:304)
at org.hibernate.envers.configuration.internal.RevisionInfoConfiguration.configure(RevisionInfoConfiguration.java:347)
at org.hibernate.envers.configuration.spi.AuditConfiguration.<init>(AuditConfiguration.java:119)
at org.hibernate.envers.configuration.spi.AuditConfiguration.getFor(AuditConfiguration.java:180)
at org.hibernate.envers.event.spi.EnversIntegrator.integrate(EnversIntegrator.java:76)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:312)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1859)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:852)
... 41 more
We are unsure how to continue. If we can't replace the long primary key of the AuditRevision table to use a UUID id, we could add a UUID (or another id or key) to the AuditRevision table and then use it along with the auto increment id as a composite key. The AUD tables will then need to reference the UUID, which we thought we could manage using a database trigger because we don't have control over the AUD tables.
Another option is that we could also rewrite the Hibernate envers library or fork their code and make the necessary modifications to support UUID.
Does anyone have any experience of this problem? How would you solve it?
We are using Hibernate 4.3.7.Final with a MySQL 5.7 database
Related
I am using Spring-Boot and JPA.
For saving and also updating a record there is the one method save of CrudRepository.
I use a generated long id for primary key of my record.
Now I need to update a already existing record. That will fail with an duplication exception if the primary key id does not
match the value that is already in the database. If no id is given JPA would assume an insert and that fails because the old record already exists.
So what is the best strategy for that?
I do not want to look for the id of the existing record before doing a save for updating my record.
Is there some default value for id that I an use for marking it for update instead of insert?
I don't think Hibernate has such functionality. Some databases support this with their native capabilities (insert ignore in MySQL or on conflict do nothing in Postgres), but you'd have to write SQL yourself. And some databases don't have this feature at all.
Probably, the only way to do this with Hibernate is to handle constraint violation exceptions. Note though if it happens, you may not be able to proceed with the transaction/session since Hibernate warns you that Session/EntityManager can't be further used reliably if it threw an exception.
first of all, I don't speak english correctly, so, sorry for the mistakes.
I having a problem, when I try to create a entity (with id NULL, then the database will assign it) and the creation fails, the entity is left with the generated ID. I need that the id remain null if the creation fails, because in my service, I check if the entity passed to "save" method, contains ID or not, to make an update or an create.
I can set the ID in null if the creation fails, but when I making more complex transactions, is hard to change manually every ID in every entity, and some times, some objects are fetched from the database and others not.
I hope that I have explained correctly... Thanks.
PD: This is the field ID in the entity...
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
PD2: I using Spring framework by the way.
EDIT: I using PostgreSQL, and try every generation posible.
From the design perspective you should rely on exceptions not on null values.
In your service you can just catch the exception and you don't have to add if statement.
Much depends on the DB you are using , the generation method and the Hibernate Dialect.
In many situation the DB will assign the ID and Hibernate will just load the field with that value.
Looking a bit into both dialect and Postgres I think that you will have to undo id by hand, since postgres sequences are not affected by rollback (see this answer) so hibernate can get the current ID even for failed inserts.
You could use JPA lifecycle annotation to collect enough information about your entity to do a clean up after a failed save.
The IDENTITY generation strategy is not commonly preferred and here is why:
Hibernate cannot generate an identifier value before an INSERT operation. That's why Hibernate has to perform the INSERT query and SELECT immediately after it. In this way, Hibernate will populate the identifier value of your persisted entity instance.
I believe that PostgreSQL doesn't support the IDENTITY id generation strategy (Hibernate Identity, Sequence and Table (Sequence) Generator). If PostgreSQL supported the IDENTITY strategy, then your entities would not have id initialized because SELECT would never happen.
I'm using Java with Spring MVC and Hibernate. I have a bunch of entities and everything works fine. If, however, I add a column to one of my database columns, my service will start crashing until I also update the relevant java entity class with the new column.
Is there a way to tell Hibernate to ignore database columns it doesn't recognize? If you want to have a field in your entity that's not in your DB table, you would use #Transient on the field. I want the inverse of that.
If this is not possible, how do Hibernate services get deployed when there's a database update that has to go along with it?
Hibernate will not "crash" after new columns were added to a table managed by Hibernate. Hibernate scheme validation goes only as far as verifying that the mapped columns can be stored in the database, but will not look for unmapped columns in the database.
What is likely causing your problem is a new NOT-null field. Adding such a field will make it impossible for Hibernate to persist anything into that table since it is oblivious to the existence of this field and will not provide it at insertion-time. Solutions to this problem are:
Providing DEFAULT in the alter table operation for clients that do not use this field
Not marking the field not-null and performing nullability checks in another layer
Using pre-insert triggers to populate the empty fields
Alternatively you can even add the new field first, deploy your new version of your application, then mark the new field as not-null.
We have a number of object that have an id of type Long and are stored in MySql and use JPA/Hibernate for ORM. We are going to move some to Mongo in the future. Is it sensible to create an embeddable class for the Id field, e.g. ContentId and use this throughout the system in place of Long so that when we move to MongoDB or anothe noSql database without Long ids that we only have to change the internal representation of the ContentId class. I can only find references to using #EmbeddedId for composite keys. Is this a sensible thing to do? I don't want to have to go through all the code in a year or so when we change and replace Long with ObjectId.
MongoDB uses a generated OID as the default Id. You can also define your own using the _id attribute. The OID is basically a UUID, which maps best to a String. I would just use a UUID in MySQL, so you can use the same model on either. MongoDB does not support a composite id, so using a composite id is probably not a good idea.
EclipseLink supports JPA on both MySQL and MongoDB. EclipseLink also supports a #UuidGenerator that works with any database.
http://java-persistence-performance.blogspot.com/2012/04/eclipselink-jpa-supports-mongodb.html
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/NoSQL
I don't see what EmbeddedId would give you to gain portability .... best to focus on the value generators available and what the datastore would support, and look for how you can have something mappable on both datastores to ease the migration.
DataNucleus JPA obviously supports persistence to MongoDB and has for some time, allowing the full range of identities, whether it is the native MongoDB UUID ("identity" in JPA parlance), String-based (uuid, uuid-hex) or numeric ("table"). This gives portability and you can choose what suits your model best. It also supports persistence to many other types of datastores (RDBMS, Excel, ODF, ODBMS, HBase, AppEngine, LDAP, and others) should you need portability to other datastores too.
I set my entity property
#GeneratedValue
Long id;
and I able to generate id for the entity in database. My question is why all the entities are sharing the same incremental number? aren't each table should start counting from zero?
It depends on the underlying database.
GenerationType is AUTO by default, and Hibernate chooses one of the three variants depending on the database. If you want to use one in particular, set it as attribute of #GeneratedValue
This is database-dependent. JPA implementations use different ID generators depending on which database system they're using. For example, with Oracle, a single sequence will be created, and that sequence will be used to generate IDs for all entity types. By default, it will not create a sequence for each entity, since there's usually no reason to. The same logic applies to other database systems that use sequences rather than auto-increment columns.
I'm not 100% sure if the JPA API lets you change this behaviour, but I know that Hibernate annotations do. However, you haven't told us which database you're using or which JPA implementation you're using, so I can't give you much more advice than that.