I have my id generation defined as follows:
#Id
#GeneratedValue(generator = "pooled")
#GenericGenerator(name = "pooled", strategy = "org.hibernate.id.enhanced.TableGenerator", parameters = {
#Parameter(name = "value_column_name", value = "sequence_next_hi_value"),
#Parameter(name = "prefer_entity_table_as_segment_value", value = "true"),
#Parameter(name = "optimizer", value = "pooled-lo"),
#Parameter(name = "increment_size", value = "100")})
private Long id;
I'm using liquibase for table generation and let hibernate validate the schema, so that forgetting to edit the changelog will be detected. The liquibase definition for the hibernate_sequences looks like:
<changeSet author="david (generated)" id="1477051164467-23">
<createTable tableName="hibernate_sequences">
<column name="sequence_name" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="sequence_next_hi_value" type="bigint"/>
</createTable>
</changeSet>
This works for my local SQLServer Express as well as the h2 database that is used in unit tests. When I deploy the application on our dev server with postgres hibernate expects a database sequence instead of using the sequence table: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing sequence [hibernate_sequence]
I thought that using the TableGenerator strategy will not require sequences on any database.
Using Spring Boot with hibernate 5.0.11.Final
At this point we do not know what databases will be used in production so I need a portable solution. (at least supporting Oracle, MsSQL, Postgres, MySql... so in fact almost everything)
Update:
I have installed a local postgres and added the sequence by hand to check if it used. It is indeed used (the counter is 3) but I have no clue by which entity.
I have also hibernate envers enabled for entity auditing. The revinfo table uses a separate sequence, but this one is created by liquibase.
Turned out, the hibernate envers default entity uses #GeneratedValue hence the need for the sequence
Thanks in advance. If you need more information feel free to ask.
Please review your entities and check if all of them are using org.hibernate.id.enhanced.TableGenerator. That's the only possible cause - if you take a look at https://github.com/hibernate/hibernate-orm/blob/5.0.11/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java#L662 you will realize it doesn't require any sequences.
Related
I am quite new to development with databases, so maybe this question is not entirely spot on, but I'd appreciate if someone can make it a bit clearer to me... I've read all about sequences, and how they are preferred over identities. I have a hypothetical question. If I were to use a sequence to generate my PK along with Hibernate (data insertion) and Liquibase (schema creation), what would be the right spot to define sequence?
For example: Sequence generation on class level.
User.java
#Entity
#Table(name = "USER")
public class User {
#Id
#SequenceGenerator(name = "USER_SEQ", sequenceName = "USER_SEQ")
#GeneratedValue(strategy = SEQUENCE, generator = "USER_SEQ")
#Column(name = "ID")
private Long id;
// other fields
}
Or other example: Sequence generation on Schema level.
changelog.xml
<changeSet author="wesleyy">
<createSequence catalogName="cat"
cycle="true"
incrementBy="1"
maxValue="1000"
minValue="10"
ordered="true"
schemaName="public"
sequenceName="user_seq"
startValue="1"/>
</changeSet>
Is it required to define a sequence in both Liquibase and Hibernate? What exactly is the difference between the two?
By lines in class User
#SequenceGenerator(name = "USER_SEQ", sequenceName = "USER_SEQ")
#GeneratedValue(strategy = SEQUENCE, generator = "USER_SEQ")
you say to hibernate: for each object User inserted into database give new value for primary key from sequence named USER_SEQ.
By adding described changeSet to liquibase xml script you say to liquibase: next time create in database sequence named user_seq if this changeSet was not already applied to database.
In other words in liquibase script you create sequence and in code of class User you use it.
If what you wish for is the id primary key to be auto generated then this can be simply done as follows using a JPA implementation such as Hibernate or others:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
The Id primary key will be auto generated and managed for you.
Using liquibase, in your base changelog xml file, you can do the following as long as your database supports auto generated id's (many do this but check if unsure):
<changeSet author="name" id="auto increment example">
<createTable tableName="users">
<column autoIncrement="true" name="id" type="SERIAL">
<constraints primaryKey="true" primaryKeyName="users_pkey"/>
</column>
<column name="some_other_column" type="VARCHAR(255)"/>
<column name="another_column" type="VARCHAR(255)"/>
</createTable>
</changeSet>
Your question is a bit misleading because it is related to two different things:
generation of entity identifiers (behavior of your application) and
definition of the underlying database schema.
"If I were to use a sequence to generate my PK along with Hibernate (data insertion) and Liquibase (schema creation), what would be the right spot to define sequence?"
You could define a sequence in many different ways. It always ends with SQL statement, but sometimes you may prefer to use Liquibase, Flyway or Hibernate to DDL auto export (Hibernate hbm2ddl.auto possible values and what they do?).
My personal preference is to rely on Hibernate automatic schema export during initial development and later on use some of the mentioned version-based DB migration tools.
I've wasted too much time on this ...
I'm using oracle and I have a sequence (MY_TABLE_SEQ) defined which increments by 1.
In my Pojo I have:
#SequenceGenerator(name = "MY_SEQ", sequenceName="MY_TABLE_SEQ", allocationSize=50)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="MY_SEQ")
This gives me a unique constraint issue. From my understanding I need to set the following property:
hibernate.id.new_generator_mappings=true
I've tried setting in my hibernate.cfg.xml file but it does not seem to make any difference. I've come across server post to place in persistance.xml but this is a standalone app, no webcontainer.
Setting allocationSize=1 works but of course it hits the db on each insert to get the next sequence. Setting the above property is suppose to resolve it.
I haven't tried Oracle, but I had similar issues to yours inserting into an AS400 DB2 table.
I had to remove the identity flag on the id column on DB2 table - and instead used a custom jpa/hibernate sequence generator. This is set up on the pojo/entity annotation of the #ID entity field as you've done.
DB2 had been giving me errors about missing SYSIBM.SYSSEQUENCES table, so evidently hibernate (version 5.2), doesn't recognize the native DB2 identity designation. A custom sequence was and effective workaround.
On the #ID entity field:
#GeneratedValue(generator = "table", strategy=GenerationType.TABLE)
#TableGenerator(name = "table", allocationSize = 20)
This example allocates a pool of 20 sequence numbers each time it queries the table.
Next, create the required table Hibernate needs with columns that match the hibernate5 API - must be in lower case ... so put quotes around the names to work around the auto-upper casing that DB2 defaults to. The API will error out if these names are in caps.
Table:
"hibernate_sequences"
example of 2 Columns used:
"sequence_next_hi_value" (integer, not nullable, 0 default)
"sequence_name" (character, sample length 20, not nullable, natural default)
In the configuration code for the dialect used - ex: Spring Boot programmatically, add these properties:
properties.put("hibernate.supportsSequences","false");
properties.put("hibernate.id.new_generator_mappings","false");
and in the *.properties file:
spring.jpa.properties.hibernate.dialect.supportsSequences=false
spring.jpa.properties.hibernate.id.new_generator_mappings=false
Database systems are case-sensitive for schema/table/field names. Also watch for typos everywhere, incl. property names.
Be sure your pojo/entity only contains private fields that will be mapped to the table. Static finals such as serialVersionUID are ok.
I will be doing someething similar for SQL Server soon.
For MySQL, I had no issues using an identity column as defined in a table ID field to insert records, so didn't have to make all these changes. A simpler setup since hibernate recognizes the identity designation in MySQL.
#GeneratedValue(strategy=GenerationType.IDENTITY)
was all that was needed in the pojo.
I'm a newbie at all this, so always looking for better ways ... but this worked for now.
I set the property like this in the hibernate.cfg.xml file and it works !
<property name="hibernate.jpa.compliance.global_id_generators" value="true"/>
I have a table with a simple int id column with Identity auto increment in SQL Server.
The entity's Id is annotated with #Id and #GeneratedValue
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", length = 4, precision = 10, nullable = false)
private Integer id;
In SQL Server the column is properly set as Identity with Seed and Increment equals to 1.
When I try to persist an instance of this entity, Hibernate tries to query the hibernate_sequence table to obtain the ID value. Since I haven't created that table in my schema I'm getting an error:
could not read a hi value: com.microsoft.sqlserver.jdbc.SQLServerException: Invalid object name 'MySchema.hibernate_sequence'
If I change the generation type to IDENTITY everything works as expected
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", length = 4, precision = 10, nullable = false)
private Integer id;
I cannot change it this way, since my App will run both on MS SQL and ORACLE, and the latter does not support auto incremented columns.
As far as I know the AUTO type should use the auto increment behaviour if the underlying database has support to it, so I don't know why is not working.
UPDATE:
It took me some time but I was able to understand exactly what is going on.
I am working with legacy databases with the following behaviours:
MSSQL: id generation uses table IDENTITY
ORACLE: id generation uses a trigger. The trigger queries and updates a custom table where all the "next ids" are stored. This table is called SEQ.
Here is the outcome of using some id generation strategies:
AUTO: does not work in MSSQL, as explained above
IDENTITY: works in MSSQL but is not supported by Oracle
"native": works in MSSQL but fails in ORACLE. It fails because Hibernate activates its default sequence strategy, which uses hibernate_sequences.nextval. Since this is a legacy application the values from the SEQ table (mentioned above) and the hibernate_sequences are not synchronized (SEQ's value for that particular table is at 6120, and hibernate_sequences' is at 1, which is expected since it was not used until now).
So what I need to figure out is a way to configure that entity to:
Use MSSQL Identity feature
OR
When using Oracle, do not automatically set any value to the ID variable and leave everything up to the pre-existing trigger
This can cause me serious issues on Oracle when I need to insert entities that depend on the main entity (via foreign key), because Hibernate won't know which ID value was generated by the "external" trigger.
I had a similar problem and found this information (deeper explained in here).
Adding this property into my persistence.xml file fixed the issue:
<property name="hibernate.id.new_generator_mappings" value="false" />
Orcale 12c supports IDENTITY and SQL SERVER 2012 supports SEQUENCES. I believe a SEQUENCE is always a better choice than an IDENTITY. IDENTITY disables batching and SEQUENCES allow you to provide optimizers, such as the pooled-lo optimization strategy.
This is how the actual identifier generator is chosen for the configured GenerationType value:
switch ( generatorEnum ) {
case IDENTITY:
return "identity";
case AUTO:
return useNewGeneratorMappings
? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
: "native";
case TABLE:
return useNewGeneratorMappings
? org.hibernate.id.enhanced.TableGenerator.class.getName()
: MultipleHiLoPerTableGenerator.class.getName();
case SEQUENCE:
return useNewGeneratorMappings
? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
: "seqhilo";
}
If you use the new identifier generators:
properties.put("hibernate.id.new_generator_mappings", "true");
The AUTO will actually use a SequenceStyleGenerator and where the database doesn't support sequences, you end up using a TABLE generator instead (which is a portable solution but it's less efficient than IDENTITY or SEQUENCE).
If you use the legacy identifier generators, you then end up with the "native" generation strategy, meaning:
public Class getNativeIdentifierGeneratorClass() {
if ( supportsIdentityColumns() ) {
return IdentityGenerator.class;
}
else if ( supportsSequences() ) {
return SequenceGenerator.class;
}
else {
return TableHiLoGenerator.class;
}
}
If a new Oracle12gDialect is going to be added and it will support IDENTITY, then AUTO might switch to IDENTITY rather than SEQUENCE, possibly breaking your current expectations. Currently there is no such dialect available so on Oracle you have SEQUENCE and in MSSQL you have IDENTITY.
Conclusion:
Try it like this:
#Id
#GenericGenerator(name = "native_generator", strategy = "native")
#GeneratedValue(generator = "native_generator")
private Long id;
make the id a Long instead of Integer, and you can let the HBMDDL handle the primary key column type.
force Hibernate to use the "native" generator
If your legacy system uses a table for generating sequence values and there was no hilo optimization ever used you can use a table identifier generator:
#Id
#GeneratedValue(generator = "table", strategy=GenerationType.TABLE)
#TableGenerator(name = "table", allocationSize = 1
)
private Long id;
You can also use the JPA table generator, just make sure you configure the right optimizer. For more info check my Hibernate tutorial
because
#GeneratedValue(strategy = GenerationType.AUTO)
use SequenceStyleGenerator by default in earlier versions
you have to look at this https://hibernate.atlassian.net/browse/HHH-11014
For testing I'm using the H2 database.
For production it's MySQL.
I understrand that both support AUTO_INCREMENT (mysql / h2), but it seems like Hibernate doesn't work this way.
identity is supported for MySQL. Fine.
What about H2? Should I write my own generator or...? (using the org.hibernate.id.IdentifierGenerator interface as the doc says).
I must have a nice clean & quick way to get an ID (of type long by the way) from the database itself because the application is in a cluster (i.e several servers INSERT into the database at once)... that's why increment is definitely not for me.
Thanks!
You should just annotate your id property which needs the generated value with #GeneratedValue. This will automatically select the appropriate generation strategy for the database you're using. See GenerationType.AUTO for more details.
Your property will look like this:
#Id
#GeneratedValue
private long id;
Use the native generator, for example
<id name="id" type="int">
<column name="id_column" />
<generator class="native" >
<param name="sequence">id_column_sequence</param>
</generator>
</id>
The generator with the class native uses the best generation strategy for the database. In the case of MySql this is auto_increment, in the case of Oracle this is a sequence (and for H2 it also should be a sequence, but I've never tried, because I don't use H2). The generator parameter sequence only is used if it is useful, i. e. for MySql databases the parameter is ignored, and for Oracle it is used.
In that way you can use the same mapping file for different database types (at least as long as the table and column names are the same).
I have a hibernate mapping:
<properties name="lrt_bps_bpr_acs_uk" unique="true">
<many-to-one name="activitySummary" column="bps_acs_id" class="xyz.ActivitySummary"
unique-key="lrt_bps_bpr_acs_uk"/>
<many-to-one name="bestPractice" column="bps_bpr_id" class="xyz.BestPractice"
unique-key="lrt_bps_bpr_acs_uk"/>
</properties>
I expected that hibernate would create unique constraint on database for this mapping. Nothing like that happened however.
What am I doing wrong?
Seems like you do nothing wrong. We do it the same way in our project and it works fine.
I thought to have the same problem once because I couldn't find the constaints in our oracle DB because I thought the specified unique-key attribute value (in your case 'lrt_bps_bpr_acs_uk') is used as constraint_name in the DB. But that isn't the case (at least with oracle DB).
What's the actual DDL that Hibernate is generating? What would be the DDL if you remove the unique-key parameters? What's the Hibernate version?
From the documentation, it seems you are doing it correctly:
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html#mapping-declaration-properties
Dont specify name="lrt_bps_bpr_acs_uk" unique="true" in the properties element. It automatically takes it and applies the constraint with unique-key