How to enable the Hibernate HiLo entity identifier optimizer strategy - java

I'm initializing Hibernate without any XML by something like
org.hibernate.SessionFactory sessionFactory =
new org.hibernate.cfg.Configuration().
.setProperty(...)
.setProperty(...)
...
.buildSessionFactory();
My classes use an ID like
#Id #Generated(GenerationTime.INSERT) #GeneratedValue private Integer id;
The generator used is SequenceStyleGenerator, which seems to be the replacement for the deprecated SequenceGenerator and SequenceHiLoGenerator and whatever. It uses
public static final int DEFAULT_INCREMENT_SIZE = 1;
and seems to allow configuration via
public static final String INCREMENT_PARAM = "increment_size";
but that's all I could find out. I guess I have to set some property "xxx.yyy.increment_size" or pass it in another way to Hibernate, but I can't see how.
I'm aware of #SequenceGenerator, but it seems to be completely ignored

I guess you are looking for how to set increment_size property for your SequenceSytleGenerator.
Sample snippet below setting increment_size using #GenericGenerator annotation with hilo optimizer and SEQUENCE strategy.
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "hilo_generator")
#GenericGenerator(
name = "hilo_generator",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
// Or leave it out to get "hibernate_sequence".
#Parameter(name = "sequence_name", value = "hilo_sequence"),
// Or leave it out as this is the default.
#Parameter(name = "initial_value", value = "1"),
#Parameter(name = "increment_size", value = "5"),
#Parameter(name = "optimizer", value = "hilo")
})
There's no way you can globally set the DEFAULT_INCREMENT_SIZE with a Hibernate configuration property. You need to use the #Id configuration properties instead.

#Generated vs #GeneratedValue
You don't need to use #Generated along with #GeneratedValue. The #Generated annotation is for the non-id entity attributes that are generated by the database during INSERT or UPDATE. For more details about the #Generated annotation.
On the other hand, the #GeneratedValue is only for the entity identifier attributes, and it's what you need to use when the entity identifier is generated automatically upon persisting an entity.
Sequence generator
The sequence generator requires an extra database roundtrip to call the sequence object when you persist the entity. For this reason, Hibernate offers sequence-based optimizers to reduce the number of roundtrips needed to fetch entity identifier values.
Now, if you want to use hilo, the identifier mapping will look as follows:
#Id
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "post_sequence"
)
#GenericGenerator(
name = "post_sequence",
strategy = "sequence",
parameters = {
#Parameter(name = "sequence_name", value = "post_sequence"),
#Parameter(name = "initial_value", value = "1"),
#Parameter(name = "increment_size", value = "3"),
#Parameter(name = "optimizer", value = "hilo")
}
)
private Long id;
Apart from having to use the Hibernate-specific #GenericGenerator, the problem with hilo is that the generated identifiers don't include the database sequence value, so an 3rd-party client using the database will not know how to generate the next identifier value unless they know the hilo algorithm and the allocationSize.
For this reason, it's better to use pooled or pooled-lo.
Pooled optimizer
The pooled optimizer is very easy to set up. All you need to do is set the allocationSize of the JPA #SequenceGenerator annotation, and Hibernate is going to switch to using the pooled optimizer:
#Id
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "post_sequence"
)
#SequenceGenerator(
name = "post_sequence",
sequenceName = "post_sequence",
allocationSize = 3
)
private Long id;
Pooled-lo optimizer
To use the pooled-lo optimizer, just add the following configuration property:
<property name="hibernate.id.optimizer.pooled.preferred" value="pooled-lo" />
Now, the entity identifier mapping is identical to the one I showed you before for the pooled optimizer.
To understand how the pooled-lo works, check out this diagram:
If you have been using the legacy hilo optimizer, you might want to switch to using pooled or pooled-lo, as hilo is not interoperable with other clients that might not be aware of the hilo identifier allocation strategy.

Related

Hibernate cannot handle sequences with same name in differen schemas

After upgrading from 5.4.7 to 5.4.10 it looks like hibernate cannot handle two sequences with the same name in different db schemas anymore.
I have this entity
#Entity
#Table(name = "VM_LAUF_RICHTUNG", schema = "INFOP_FAHRPLAN")
public class VmLaufRichtung {
public static final String VM_LAUF_RICHTUNG_TABLE = "INFOP_FAHRPLAN.VM_LAUF_RICHTUNG";
#Id
#Digits(integer = 15, fraction = 0)
#SequenceGenerator(name = "InfopFahrplan.seqVmLaufRichtung", schema = "INFOP_FAHRPLAN", sequenceName = "SEQ_VM_LAUF_RICHTUNG")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "InfopFahrplan.seqVmLaufRichtung")
#Column(name = ID)
private Long id;
}
In an other schema, there's a sequence with a the same name SEQ_VM_LAUF_RICHTUNG.
When my spring boot application starts I do get
Caused by: org.hibernate.MappingException: The increment size of the [SEQ_VM_LAUF_RICHTUNG] sequence is set to [50] in the entity mapping while the associated database sequence increment size is [1].
This happens because it's picking up the wrong sequence, which has an other imcrement size.
I tried to fix the problem by setting
spring.jpa.hibernate.use-new-id-generator-mappings=true
but that did not change anything.
With hibernate 5.4.7 everyhting is working fine.
Did I miss something or is this a hibernate bug after all?
It looks that this is indeed a hibernate bug: https://hibernate.atlassian.net/browse/HHH-13322

hibernate #Id #GeneratedValue with non auto-increment and i dont have sequence

i have situation on which i try to persist entity with id that depends on the max id value, for example the new entity id will be MAX(id)+1.
now i try to use JPA to persist this entity
#Entity
#Table(name = "product")
public class ProductDetails {
#Id
#GeneratedValue
private String id;
i used strategy = GenerationType.AUTO, strategy = GenerationType.IDENTITY,strategy = GenerationType.SEQUENCE,strategy = GenerationType.TABLE none of them work, so i think i can solve it through selecting the max id then +1 and use that value (i did not try it) what i am asking for, is there is any way to handle this situation through JPA or Hibernate.Note:the id columns is not auto-increment and the db doesn't have sequence.
Don't use String as Primary key. if you need id like "ABC123" then take 2 id columns. One as id(int) PK, second as display_id(String). You can auto-generate display_id in database level using trigger.
If you use String as a type of your Id you shouldn't use auto-increment cause String is something that can't be incremented since it's not a number type. Just leave #Idand add #GeneratedValue(generator = "uuid") - that should work.
Additionally you can add #GenericGenerator(name = "uuid", strategy = "uuid2")

Is it OK to have a duplicate generator named defined in the JPA persistence unit?

My situation is described here:
eclipse Duplicate generator named "ID_GENERATOR" defined in this persistence unit
However my question is different and the selected answer does not solve it :
"Is it valid to have multiple #SequenceGenerator with the same name even though it is used for this purpose Hibernate : How override an attribute from mapped super class ?"
If not valid, is there an alternative ?
Thank you very much for your answer.
According to section 11.1.48 SequenceGenerator Annotation of the JPA 2.1 specification:
The scope of the generator
name is global to the persistence unit (across all generator types).
So you can't have duplicate generators.
If you try to add the following two entities:
#Entity(name = "Post")
public static class Post {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pooled")
#GenericGenerator(
name = "pooled",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
#Parameter(name = "sequence_name", value = "sequence"),
#Parameter(name = "initial_value", value = "1"),
#Parameter(name = "increment_size", value = "5"),
}
)
private Long id;
}
#Entity(name = "Announcement")
public static class Announcement {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pooled")
#GenericGenerator(
name = "pooled",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
#Parameter(name = "sequence_name", value = "sequence"),
#Parameter(name = "initial_value", value = "1"),
#Parameter(name = "increment_size", value = "10"),
}
)
private Long id;
}
Hibernate will generate the following error message:
Multiple references to database sequence [sequence] were encountered
attempting to set conflicting values for 'increment size'. Found [10]
and [5]
That's because the identifier generator is global and those two sequence configurations will be conflicting.
I think it's valid, because in the end of the day, this is just how hibernate will map the entity to the sequence that will generate the ID when it persist it in the DB. Oracle for example does not care which tables use which sequence, since the sequence itself is an independent entity. IMO this warning (or error) makes more sense depending on the DBMS you're using. IMO, I'd just disable the error warning in eclipse.

Hibernate, id, oracle, sequence

My database is Oracle, and my id column value is an Oracle sequence, this sequence is executed by a trigger, so, before each row is inserted this trigger use this sequence to get the id value. So I am confused in which id strategy generation should I define in my entity class.
#GenericGenerator(name = "generator", strategy = "increment")
#Id
#GeneratedValue(generator = "generator")
or
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "idGenerator")
#SequenceGenerator(name="idGenerator", sequenceName="ID_SEQ")
or
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Really confused, Could someone shed some light on this subject? Please explain clearly..
I had also a projet where an Oracle DB that provides the data to my #Entity classes. As you said, a sequence generates the id for the PK of the table via a trigger. This was the annotations that I used in one of these classes:
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "G1")
#SequenceGenerator(name = "G1", sequenceName = "LOG_SEQ")
#Column(name = "ID", unique = true, nullable = false, precision = 22, scale = 0)
public int getId() {
return this.id;
}
This is the second syntax that you have showed in your post.
There's no call to the trigger in the Java code because the trigger is managed by the DB. I remember that I had to have the sequence and the trigger at the same time in the DB if I didn't wanted to have problems. The trigger asked if the id of the row to insert is null or = 0. In this case the sequence LOG_SEQ is called.
So if you provide a value to the #Id of your entity it could be inserted in the DB (if that Id doesn't exist) and the sequence would not be called. Try to see the code of the trigger to see exactly what it happens.

How is my id being generated with JPA using Hibernate with the Oracle 10g dialect?

I have some code:
#Id
#SequenceGenerator(name = "SOMETHING_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SOMETHING_SEQ")
#Column(name = "SOMETHING", nullable = false)
private Long id;
How is hibernate providing my id?
I see in my database there a single sequence named 'hibernate_sequence' and no other hibernate 'special tables'.
Actually, here your SOMETHING_SEQ is the name of sequence you configured somewhere in your hibernate config. And hibernate_sequence is the sequence name in the database. In configuration it would be looking something like below,
<sequence-generator name="SOMETHING_SEQ"
sequence-name="hibernate_sequence"
allocation-size="<any_number_value>"/>
You can completely skip this configuration by using annotation instead. Then your #SequenceGenerator annotation would need to provide few more paramters. Below is the example.
#SequenceGenerator(name="SOMETHING_SEQ", sequenceName="hibernate_sequence", allocationSize=10)
For example multiple entity classes would do something like below,
#Entity
public class Entity1 {
#Id
#SequenceGenerator(name = "entity1Seq", sequenceName="ENTITY1_SEQ", allocationSize=1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "entity1Seq")
#Column(name = "ID", nullable = false)
private Long id;
...
...
}
#Entity
public class Entity2 {
#Id
#SequenceGenerator(name = "entity2Seq", sequenceName="ENTITY2_SEQ", allocationSize=10)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "entity2Seq")
#Column(name = "ID", nullable = false)
private Long id;
...
...
}
How is hibernate providing my id?
Well, you explicitly told the JPA engine to generate identifier automatically (with the #GeneratedValue annotation) using a strategy of type SEQUENCE indicating that a database sequence should be used to generate the identifier. In case you wonder, sequences are database specific objects (e.g. Oracle) that can be used to generate unique integers.
I see in my database there a single sequence named 'hibernate_sequence'
You didn't use the sequenceName annotation element in your #SequenceGenerator to specify the name of the database sequence object to use so Hibernate created a default sequence object during schema generation (which defaults to hibernate_sequence). To specify a sequence, do it like this:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_entity_seq_gen")
#SequenceGenerator(name = "my_entity_seq_gen", sequenceName="MY_ENTITY_SEQ")
private Long id;
In order to name the sequence you have to set the sequenceName in your #SequenceGenerator annotation:
#GeneratedValue(name="gen", strategy = GeneratorType.SEQUENCE)
#SequenceGenerator(name="gen", sequenceName="Sequence_Name", allocationSize = 1)
#Id
public Long getId()
{
// ...
}
Of note, if you are using a pre-existing generator, your allocationSize must match the allocation size of that generator.
In Oracle you don't have the auto_increment type as in MySQL. So, to generate an auto_increment column you need to use a sequence.
This is an example of how you can achieve this.
create table test (id number, testdata varchar2(255));
create sequence test_seq
start with 1
increment by 1
nomaxvalue;
create trigger test_trigger
before insert on test
for each row
begin
select test_seq.nextval into :new.id from dual;
end;
So you create a sequence and use a trigger before each row is inserted to add its id.
So hibernate must be doing something like this, or instead of using the trigger doing
insert into test values(test_seq.nextval, 'no trigger needed!');
Note: Example taken from here

Categories