Hibernate doesn't add auto_increment - spring

I'm running into an issue when running my SpringBootTests.
The tests are using an H2 database, so they recreate the schema every time they run. For one of my entities, Hibernate doesn't add auto_increment to the id column.
I can't find any relevant differences between the failing entity (Payment) and others that work correctly (e.g. Invoice). They all have the following annotations on the id field:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
I set spring.jpa.show-sql=true, and this is what I see:
The Payment entity shows create table payment (id bigint not null,
The Invoice entity shows create table invoice (id bigint not null
auto_increment,
In fact, I tried copying the Payment class to Payment2, and Payment2 did not have the issue:
The Payment entity shows create table payment (id bigint not null,
The Payment2 entity shows create table invoice (id bigint not null
auto_increment,
Given this, I believe I should be looking for an overriding configuration that's not in the Payment class, but that somehow removes auto_increment from just that one class.
Does anyone know what could be causing this?
These are the versions of the libraries involved:
Spring Boot version 2.1.8
Hibernate 5.3.11
H2 Database 1.4.200

I finally figured it out. This assumption was correct:
I should be looking for an overriding configuration that's not in the Payment class, but that somehow removes auto_increment from just that one class.
Turned out that a colleague had added a "light" read-only version of the Payment entity called PaymentLight. It read from the same payment table but without any of the joins. The id field in the PaymentLight class did not have the #GeneratedValue annotation.
I haven't checked this, but I assume Hibernate merged the two entities (the light class was a subset anyway) and used the #GeneratedValue configuration from one of them (likely alphabetical order).

Related

Tests failing when upgrading to Spring Boot 2.7 - "CommandAcceptanceException: Error executing DDL"

After upgrading to Boot 2.7 the integration tests that were using an embedded H2 database started failing.
I see this WARN message in the logs, but it's not very clear the cause or the solution for this:
WARN 8053 ---[ main] o.h.t.s.i.ExceptionHandlerLoggedImpl :GenerationTarget encountered exception accepting command : Error executing DDL "create table user (id bigint generated by default as identity, email varchar(255) not null, name varchar(255), primary key (id))" via JDBC Statement
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create table user (id bigint generated by default as identity, email varchar(255) not null, name varchar(255), primary key (id))" via JDBC Statement
...
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "create table [*]user (id bigint generated by default as identity, email varchar(255) not null, name varchar(255), primary key (id))"; expected "identifier"; SQL statement:
create table user (id bigint generated by default as identity, email varchar(255) not null, name varchar(255), primary key (id)) [42001-212]
...
It seems my User table is not created after the upgrade, thus making my tests fail.
It seems Boot 2.7 upgraded to its H2 dependency to 2.x, which is not backward compatible and introduces several changes:
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.7-Release-Notes#h2-21
The issue was that User is now a Keyword / reserved word (The H2 "migration-to-v2" guide was not very helpful in my case; it mentioned new keywords were added, but it didn't provide a link to ):
https://www.h2database.com/html/advanced.html#keywords
So, what I had to do is use "quoted names" to define the Table name of my Entity (it seems I can use backticks in the table annotation too instead of escaping double quotes):
#Table(name="\"user\"")
#Entity
public class User {
...
I also had to use double quotes on my data.sql files for this table:
INSERT INTO "user"(id, email, name) VALUES(1, 'test#user.com', 'Test User');
Note: the migration guide also mentions the possibility of using the SET NON_KEYWORDS command as a workaround, but it also discourages it.
Add the following to your src/test/resources/application-test.properties file (assuming your tests run with the test profile):
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring.jpa.properties.hibernate.globally_quoted_identifiers_skip_column_definitions = true
If any of your JPA entities have UUID fields, ensure those fields are annotated with #Column and the annotation's columnDefinition defines the column as type UDID. (In its simplest form: #Column(columnDefinition="UDID").) This works around a Hibernate bug.
It could be because of Identity columns in schema.sql.Identity columns should be normally declared with GENERATED BY DEFAULT AS IDENTITY.
Ex: JOB_EXECUTION_ID BIGINT IDENTITY -- should be changed as JOB_EXECUTION_ID BIGINT GENERATED BY DEFAULT AS IDENTITY.
Refer http://www.h2database.com/html/migration-to-v2.html for more such changes occurred due to upgrade in H2 version.

How to integration test database view in a spring boot project

I have a table and view and created a corresponding JPA for these. I want to both table and view.
-- account table
CREATE TABLE account(
user_id serial PRIMARY KEY,
username VARCHAR (50) UNIQUE NOT NULL,
password VARCHAR (50) NOT NULL,
email VARCHAR (355) UNIQUE NOT NULL,
created_on TIMESTAMP NOT NULL,
last_login TIMESTAMP
);
-- account view
create view account_view as
select user_id, email, last_login from account;
I added an integration test. It works fine for the table, however, the view does not have any data.
How can I make sure that when a row is inserted in the base table, the view also gets updated in integration test? I maybe missing some configuration. How do we add the view definition during integration test?
#DataJpaTest
public class AccountRepositoryIntegTest {
#Autowired
AccountRepository accountRepository;
#Autowired
AccountViewRepository accountViewRepository;
#Test
public void testFindByUserid(){
Account account = Account.builder()
.email("abc#gmail.com")
.username("abc")
.password("abc")
.createdOn(new Date())
.lastLogin(new Date())
.build();
Account persistedAccount = accountRepository.save(account);
Account fetchedAccount = accountRepository.getOne(persistedAccount.getUserId());
List<AccountView> accountViews = accountViewRepository.findAll();
assertThat("view has data", accountViews.size(), is(Matchers.greaterThan(0)));
assertThat(fetchedAccount.getUserId(), is(equalTo(persistedAccount.getUserId())));
}
}
As #marek.kapowicki said, the view is treated as table in JPA. This point helped me solving the problem.
When the application is running, the view data is populated by the database server. Therefore, the repository of the view is trying a select query, it gets the data.
However, during integration test, the tables are created as per the definition of the entity. The view will also be created as a table without the view definition. So, whenever you save data in the parent table, the view will not be updated.
Two ways to solve this:
create all the tables using sql scripts before running the integration tests. That way you can define the view and this script will be used to setup the database.
Create a custom entity and repository for the view that will be used only for testing. Create a custom class to process repository actions on the parent table and whenever there is an DML changes in the parent table, make sure to update the view entity according to view definition. That way, parent and view table will be in sync.
I went ahead with the second approach and it worked for me. I did not try the first approach, but it should work.

Hibernate #Id and oracle Unique constraint

Can I use an hibernate entity with #Id on a unique constraint instead of a primary key on oracle databases? Also will it be JPA compliant?
Sounds a bit strange, but seems possible:
http://www.objectdb.com/api/java/jpa/Id:
The field or property to which the Id annotation is applied should be one of the following types: any Java primitive type; any primitive wrapper type; String; java.util.Date; java.sql.Date; java.math.BigDecimal; java.math.BigInteger.
But be aware:
(1) E.g. in Oracle UNIQUE does not imply NOT NULL, you have to ensure this by your own.
(2) You can't use a FOREIGN KEY constraint in your DB.
(3) You have to be careful with Id generation (http://www.objectdb.com/api/java/jpa/GeneratedValue) if you want to insert new entities (not only read existing ones).
(4) If you insert a new entity, you have to generate a value for the DB id.
And that are only a few disadvantages that came quickly to my mind ...

Spring boot not creating schema correctly?

I have very simple spring boot app which is using h2 database on local. I have schema-h2.sql which has below script -
CREATE TABLE USERS(
userid NUMBER(10,0) AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(256) UNIQUE NOT NULL,
fname VARCHAR(256),
lname VARCHAR(256)
);
And in application-default.properties -
spring.h2.console.enabled=true
spring.datasource.platform=h2
spring.datasource.url=jdbc:h2:mem:testdb;MODE=MSSQLServer;DB_CLOSE_DELAY=-1;DB_CLO SE_ON_EXIT=false
When I look at /h2-console, I can see table being created but it is missing unique constraint on email column.
I even tried adding alter statement in schema-h2.sql script to create constraint but that doesn't work either -
ALTER TABLE USERS ADD CONSTRAINT UQ_EMAIL UNIQUE(email);
Also, when run those script in h2-console it creates unique constraint correctly so it does seem like something to do with how spring executes them.
When debugging I can see that Alter statement gets executed without any exception in ScriptUtils class.
Not sure how to take this forward so any help will be greatly appreciated.
For anyone else coming across this, I was able to fix it by altering my JPA annotation. Adding the unique parameter in addition to creating the constraint in h2-schema.sql allowed the constraint to actually be created.
#Column(name = "email", unique = true, nullable = false)
private String email;
If schema is beign created by spring boot with schema.sql file, make sure to disable hibernate schema auto create:
spring.jpa.hibernate.ddl-auto=none
Otherwise, Hibernate will overwritte it everytime the app starts.

Strange self-referencing constraint violation after upgrading Hibernate

I'm using hibernate-entitymanager in a Spring application. I'm in the process of upgrading my Hibernate version. Narrowed it down to the exact version: when I upgrade from 4.2.1.Final to 4.2.2.Final (or anything higher than that), I'm getting the following error when my unit tests starts up and try to create the database schema:
2014-03-02 18:02:51,559 ERROR [SchemaExport] [main] HHH000389: Unsuccessful: alter table Incident add constraint FK_d91dua6gkdp1jn826adqss3aq foreign key (uuid) references Incident
2014-03-02 18:02:51,559 ERROR [SchemaExport] [main] Constraint "FK_D91DUA6GKDP1JN826ADQSS3AQ" already exists; SQL statement:
alter table Incident
add constraint FK_d91dua6gkdp1jn826adqss3aq
foreign key (uuid)
references Incident [90045-170]
The error does not prevent the system from working just fine, but clearly I can't go to production with such a nasty error in my system and no explanation of it.
This looks a lot like the Incident table has a foreign key relationship to itself, which is absolutely not the case.
I'll try to copy the essence here of the Incident entity:
#Entity
#Audited
#EntityListeners(value = {IncidentIdentifierPrePersistListener.class })
#FilterDefs( ... )
#Filters( ... )
public class Incident extends SomeBaseClass {
#Id
private String uuid = UUID.randomUUID().toString();
#Column(nullable = false, unique = true)
private long identifier;
... a bunch more fields ...
}
Please let me know if I can provide anything else to help ya'all to shed a light on this. I've played around with it for hours, fruitless, and your help would be much appreciated.
Here is what happened:
Incident defined a #ManyToOne to an entity Project
Project incorrectly defined a #ManyToMany back to the entity Incident (should have been a #OneToMany)
As a result Hibernate generated the awkward following constraint:
CONSTRAINT fk909a8f241708a1e FOREIGN KEY (uuid)
REFERENCES incident (uuid) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
Incident inherits SomeBaseClass which has #Inheritance(strategy = InheritanceType.JOINED)
As a result Hibernate generated the following constraint:
CONSTRAINT fk909a8f2ddd08e84 FOREIGN KEY (uuid)
REFERENCES somebaseclass (uuid) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
As a result of HHH-8217 (Make generated constraint names short and non-random) in Hibernate 4.2.2 the above two constraint definitions suddenly got the same name (Hibernate did only consider the entity and the columns of the foreign key constraint, not the target entity of the foreign key; note how generateName only takes one Table as a parameter)
And there was the corresponding clash: Constraint ... already exists
Changing the #ManyToMany to #OneToMany fixes this entirely.
By the way, the offending field definition (the one with the #ManyToMany) also previously caused me problems with Hibernate Envers' #Audited, the reason of which I never understood, and that got resolved now as well. It's a good day. (Not sure if #Audited means much for a mapped field, but at least I can have #Audited on the class without needing to deal with this field.)

Resources