Sequences not recognized when migrating hibernate 3 to hibernate 5 [duplicate] - java

I have id column with generated strategy AUTO, I'm wondering, why MySql generate hibernate_sequence table? I supposed that hibernate will pick IDENTITY id generating strategy
<mapped-superclass class="com.cl.xlp.model.data.Identity">
<attributes>
<id name="id">
<column name="id" />
<generated-value strategy="AUTO" />
</id>
</attributes>
</mapped-superclass>
Hibernate properties
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.hbm2ddl.auto=update
Mysql connector version
version.mysql.connector>5.1.39</version.mysql.connector>
Mysql server version is 5.6.12

The way Hibernate interprets AUTO generation type has changed starting with Hibernate version 5.0.
When using Hibernate v 4.0 and Generation Type as AUTO, specifically for MySql, Hibernate would choose the IDENTITY strategy (and thus use the AUTO_INCREMENT feature) for generating IDs for the table in question.
Starting with version 5.0 when Generation Type is selected as AUTO, Hibernate uses SequenceStyleGenerator regardless of the database. In case of MySql Hibernate emulates a sequence using a table and is why you are seeing the hibernate_sequence table. MySql doesn't support the standard sequence type natively.
References
http://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-auto
https://www.thoughts-on-java.org/5-things-you-need-to-know-when-using-hibernate-with-mysql/

If you use strategy="AUTO", Hibernate will generate a table called hibernate_sequence to provide the next number for the ID sequence. You may have forgotten to add the AutoIncrement feature to your table's PK.
You may use generation strategy strategy="IDENTITY" to enforce using the AutoIncrement feature available in MySql and avoid creating a table.

Related

Spring Data JDBC with Azure SQL: After saving the identifier must not be null

Could you help me please with the following error? I am using spring-data-jdbc with Liquibase and Azure SQL Database. I get this when I want to save an object to database with UserRepository.save
java.lang.IllegalArgumentException: After saving the identifier must not be null!
at org.springframework.util.Assert.notNull(Assert.java:201)
at org.springframework.data.jdbc.core.JdbcAggregateTemplate.store(JdbcAggregateTemplate.java:343)
at org.springframework.data.jdbc.core.JdbcAggregateTemplate.save(JdbcAggregateTemplate.java:149)
at org.springframework.data.jdbc.repository.support.SimpleJdbcRepository.save(SimpleJdbcRepository.java:55)
Entity:
#Table("user_details")
public class UserDetails {
#Id
Long id;
#Column("email_address")
String emailAddress;
#Column("first_name")
String firstName;
#Column("last_name")
String lastName;
}
Repository:
#Repository
public interface UserRepository extends CrudRepository<UserDetails, Long> {
}
Liquibase:
<changeSet id="0001_user_details">
<createSequence sequenceName="user_details_seq" startValue="1"/>
<createTable tableName="user_details">
<column name="id" type="BIGINT" defaultValueSequenceNext="user_details_seq">
<constraints nullable="false"/>
</column>
<column name="email_address" type="VARCHAR(1024)">
<constraints nullable="true"/>
</column>
<column name="first_name" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
<column name="last_name" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
</createTable>
<addPrimaryKey tableName="user_details" columnNames="id"
constraintName="user_details_pk"/>
</changeSet>
Generated Schema:
create sequence user_details_seq
go
create table user_details
(
id bigint
constraint DF_user_details_id default NEXT VALUE FOR [user_details_seq] not null
constraint user_details_pk
primary key,
email_address varchar(1024),
first_name varchar(255),
last_name varchar(255),
)
go
Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>8.4.1.jre14</version>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>3.8.9</version>
</dependency>
If I understand your Liquibase configuration correct, it sets up a sequence and sets the default value of the id to the next value of that sequence.
This in principle should work, since Spring Data JDBC does not include a null id in an insert, assuming it will be generated by the database and returned by the JDBC driver.
That last part is probably where things go wrong.
The exception basically says after inserting the data in the database and using whatever the JDBC driver returned to update the id, the id is still null (or 0 if you use a primitive number type).
So probably the driver doesn't return the id generated by the sequence.
In order to solve the problem you have the following options:
Don't use a sequence, but create the id on the client side, for example by using a UUID. The benefit of this is that it's easy to implement should scale pretty well. If you create the id upon instantiation of the entity, you may also refer to it before persisting the entity.
Use a separate query to select the value from the sequence and use it to set the id. This is probably best done in a BeforeSaveCallback, but could also happen in a factory method that creates your entities. This is a little more involved and requires an additional database roundtrip. Which could get mitigated by implementing an algorithm where you select values from a sequence multiply it with say 50 and then generate 50 ids from it locally. Which is basically the HiLo algorithm of Hibernate.
Teach Spring Data JDBC how to get the id from the database. This could take many forms and depends on Azure knowledge that I don't have. If Azure has a SERIAL or AUTOINCREMENT field, that could probably be used instead of the sequence and it should get returned by the JDBC driver.
Maybe it works when a different IdGeneration is used in the Dialect you are using. Or maybe a new IdGeneration could be implemented. This obviously is rather involved and would take a full new release (or a patched version) of Spring Data JDBC.

JPA: globally changing type of the database column generated in schema

We are generating our database schema using JPA, using this property set in persistence.xml:
<property name="hibernate.hbm2ddl.auto" value="update" />
But it turned out that varchar column type generated by JPA for our String properties is too short. I know we can add:
#Column(columnDefinition = "varchar(2000)")
String someStr;
For every String property of our entities but it would be a lot of work.
It would be much better to globally configure JPA in the way that it generates varchar(2000) for every String in our entities. Is this possible?
PS. We use Hibernate as JPA backend if this fact matters.

how to disable generator when db trigger creates id

Currently when I try to insert new records I am getting an error:
[ERROR] 05/12/11_09:44:20.54 [org.hibernate.event.def.AbstractFlushingEventListener] - Could not synchronize database state with session
Db2 triggers to generate the ID need to remain in place to support legacy applications. How can I configure the hbm.xml to not generate the ID?
I'm not sure what version of Hibernate you are using, but Hibernate currently supports getting an ID that is generated from trigger via a special generator called select.
In short, you can add this generator to your ID column, and then reference a natural key you can use to retrieve the trigger generated ID as follows:
<id name="id" type="long" column="person_id">
<generator class="select">
<param name="key">socialSecurityNumber</param>
</generator>
</id>
If your mapping already has a natural-key entry defined, then you shouldn't even need to specify the key param to the generator.
One problem with this particular generator is that you can only use one entity property as the selection key for it. If you need to select via a composite key, then you'll have to create your own generator for this purpose.
You could extend org.hibernate.id.SelectGenerator or one of it's parents, and then implement the select via multiple columns that way. Then you simply replace the class attribute of the above generator entry with the fully qualified class name of your new generator.

Hibernate session.get() Problem with UUID string ID

I am having a problem with Hibernate.
The primary key of ID in mysql table is UUID style String. e.g.08fe2a75-5d40-4645-896d-aab2a3ac96b8
But I can not use session.get() to fetch out the data for some reason.
Software software=(Software)session.get(Software.class, id);
It seems ok with integer IDs.
Please advise
Activate the logging of the generated SQL to see what is happening exactly. This can be done by setting the following property in your Hibernate configuration:
<property name="hibernate.show_sql" value="true"/>
Or set the following category to debug in the configuration of your logging backend.
org.hibernate.SQL
Compare the generated query with the expected result (that works in your SQL client).
Reference
Hibernate Core reference guide
Section 3.5. "Logging"

JPA and toplink create-table on if they don't already exist?

Looks like jpa is something which makes me ask a lot of questions.
Having added this
<property name="toplink.ddl-generation" value="create-tables"/>
my JPA application always creates tables when running, which results in exceptions in case the tables already exist. I would like JPA to check if the tables already exist and if not create them, however I could not find a value for the property above which does this.
So if I just turn it off, is there a way to tell JPA manually at some point to create all the tables?
Update here's the exception I get
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'tags' already exists
Error Code: 1050
Call: CREATE TABLE tags (ID BIGINT AUTO_INCREMENT NOT NULL, NAME VARCHAR(255), OCCURRENCE INTEGER, PRIMARY KEY (ID))
MySQLSyntaxErrorException?! Now that's wrong for sure
According to http://www.oracle.com/technology/products/ias/toplink/JPA/essentials/toplink-jpa-extensions.html#Java2DBSchemaGen toplink does not have an option to update exiting tables, I'm not sure if I would trust it to do the right thing anyway.
You could configure toplink to generate a sql script that you then would have to execute manually to create all tables. The filenames and location can be configured like this:
<property name="toplink.ddl-generation" value="create-tables"/>
<property name="toplink.ddl-generation.output-mode" value="sql-script"/>
<property name="toplink.create-ddl-jdbc-file-name" value="createDDL.sql"/>
<property name="toplink.drop-ddl-jdbc-file-name" value="dropDDL.sql"/>
<property name="toplink.application-location" value="/tmp"/>
I would like [my] JPA [provider] to check if the tables already exist and if not create them, however I could not find a value for the property above which does this.
Weird, according to the TopLink Essentials documentation about the toplink.ddl-generation extension, create-table should leave existing table unchanged:
TopLink JPA Extensions for Schema Generation
Specify what Data Descriptor Language
(DDL) generation action you want for
your JPA entities. To specify the DDL
generation target, see
toplink.ddl-generation.output-mode.
Valid values: oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
none - do not generate DDL; no
schema is generated.
create-tables - create DDL for
non-existent tables; leave existing
tables unchanged (see also
toplink.create-ddl-jdbc-file-name).
drop-and-create-tables - create DDL for all tables; drop all existing
tables (see also
toplink.create-ddl-jdbc-file-name
and
toplink.drop-ddl-jdbc-file-name).
If you are using persistence outside
the EJB container and would like to
create the DDL files without creating
tables, additionally define a Java
system property INTERACT_WITH_DB and
set its value to false.
Liquibase (http://www.liquibase.org) is good at this. It takes some time to get fully used to it, but I think it's worth the effort.
The Liquibase-way is independent of which JPA persistence provider you use. Actually, it's even database agnostic.

Categories