hibernate #GeneratedValue correct? - java

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.

Related

Issue with implementing UUIDs with Hibernate Envers

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

Hibernate (JPA) left ID's assigned on failed inserts

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.

Is embedded id the right way to move to mongo from jpa

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.

Why most hibernate applications are using sequence for id generation?

Why most hibernate application are using sequence for id generation?
Why not use the default GenerationType=AUTO in #GeneratedValue annotation?
P.S. In my professional career I see everybody is use sequences, but I don't understand why they bother with harder to deploy solution (there is always sequence create SQL command in deployment instructions).
I see several reasons:
The most used database in enterprise apps is probably Oracle, and Oracle doesn't have auto-generated IDs, but sequences.
Sequences allows having the ID before inserting a new row, rather than after inserting the new row. This is easier to use and more efficient because you can batch insert statements at the end of the transaction but still have IDs definned in the middle of the transaction.
Sequences allow using hilo algorithms (which is the default with the hibernate sequence generation), and thus make only one DB call for several inserts, thus increasing performance.
AUTO varies between databases, whereas sequence always uses the same strategy.
From the excellent book Pro JPA 2 Mastering Java Persistence API by Mike Keith and Merrick Schincario.
From Chapter 4: Object Relational Mapping, section Identifier Generation.
[...] If an application does not care what
kind of generation is used by the
provider but wants generation to
occur, it can specify a strategy of
AUTO.
There is a catch to using AUTO,
though. The provider gets to pick its
own strategy to store the identifiers,
but it needs to have some kind of
persistent resource in order to do so.
For example, if it chooses a
table-based strategy, it needs to
create a table; if it chooses a
sequence-based strategy, it needs to
create a sequence. The provider can’t
always rely on the database connection
that it obtains from the server to
have permissions to create a table in
the database. This is normally a
privileged operation that is often
restricted to the DBA. There will need
to be some kind of creation phase or
schema generation to cause the
resource to be created before the AUTO
strategy is able to function.
The AUTO mode is really a generation
strategy for development or
prototyping. It works well as a means
of getting you up and running more
quickly when the database schema is
being generated. In any other
situation, it would be better to use
one of the other generation strategies
discussed in the later sections [...]
At least for Oracle: one reason is to be able to track the number of objects in a table (for which the table-specific sequence is good, if no objects are deleted from the table). Using GenerationType=AUTO uses a global sequence number, which results in gaps in id numbers when having more than one table in the database.
There are different considerations for choosing identity generator, the most important ones are performance and portability but also clustering and data migration might be a consideration.
In practice in the latest Hibernate versions (if not all of them) the SEQUENCE strategy is actually a sequence based HiLo and not a pure sequence as must people assume.
You can read a pretty details post regarding identity generation strategies at my blog: here
Eyal

JPA and unique fields

I have two persistence objects in my app: Things and tags attached to things. The app can generate collections of things with tags attached. Tag objects have a unique name (it doesn't make sense to tag something twice with the same tag).
When inserting a Thing (with tag objects attached) some of these tag objects with the same name maybe already exist in the db. Now here is the part I don't recall about JPA, is there a way to tell JPA that it should not try to add the corresponding objects to the db if it violates the unique constraint? Or is there a way to do this efficiently w/o having to first fetch all objects, then merge the collection in memory and then write everything back?
I'm also wondering if it's possible to persist a whole collection at once or do I have to call persist for every object when using JPA?
I don't know of any way of doing this 'cleanly', neither with JPA nor with Hibernate or any other provider. You can achieve what you want with a custom SQL query though (similar to this question):
#Entity
#Table(name="tag")
#SQLInsert( sql="INSERT INTO tag(name, count) VALUES (?, ?)
ON DUPLICATE KEY UPDATE set count = count + 1")
public class Tag {}
Now you are unfortunately bound to both Hibernate and MySQL. You can vary the sql syntax for other DB:s, and/or use stored procedures, try an update first and insert on failure etc. They all have their drawbacks, so it would be handy if JPA would support this, but alas.
Regarding you second question, JPA support persisting whole object graphs, including collections.

Categories