Problems with integration tests after upgrading H2 database from 1.4.200 to 2.0.204 - spring-boot

Recently I have upgraded H2 database in our SpringBoot 2.5.8 project from version 1.4.200 to 2.0.204. It is used for testing purposes only. For production we use PostgreSQL 12.9.
It seems that after upgrading some words become keywords in H2 database for example: day, value. After invoking integration test Hibernate fails on DDL part.
Postgres 12 - Keywords
H2 - Keywords
What is the best solution for that case?
Review all entities and apply back-ticks around reserved column names:
#NotNull
#Column(name = "day", nullable = false)
private LocalDate day;
#NotNull
#Column(name = "`day`", nullable = false)
private LocalDate day;
Provide dedicated SpringPhysicalNamingStrategy and override toPhysicalColumnName method only for integration test purpose. Check list of reserved keywords in H2 database and quote them.
# Datasource related properties
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
spring.sql.init.mode=always
spring.sql.init.continue-on-error=true
spring.sql.init.platform=h2
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
spring.jpa.hibernate.naming.physical-strategy=[project-related-package-name-here].strategy.CustomH2NamingStrategy
spring.jpa.defer-datasource-initialization=true
I think first solution should work both with PostgreSQL and H2 databases. Although day identifier is non-reserved in PostgreSQL 12.9 it might be in the future releases.
Second one should sort out the problem with H2 database.
What do you think about that? Maybe there are better solutions for that case? Or maybe take a list of reserved keywords from SQL:​2016 standard and apply them to both databases via custom SpringPhysicalNamingStrategy?

In H2 2.0 you can use SET NON_KEYWORDS setting by appending ;NON_KEYWORDS=DAY,VALUE to JDBC URL, but the normal solution is to quote all identifiers in generated SQL unconditionally with spring.jpa.properties.hibernate.globally_quoted_identifiers=true, for example.
You also should normally have ;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH in JDBC URL of H2 when you try to use it instead of PostgreSQL. But it's a bad idea to use different DBMS for production and testing and Hibernate ORM doesn't fully support H2 2.0 yet. Edited: Hibernate ORM 5.6.5.Final has basic support of H2 2.*.* and some additional issues were fixed in newer versions.

Related

Spring boot native query and JPA Repository

I would like to share all my project Repository interfaces with different database implementation (PostgreSQL, MySQL, ...), but having also some specific Repository with native query, for every db platform.
How can I achieve it? Is it possible to annotate a Repository to be used only with a specific database?
if you can not do it with annotations you could find out the dialect of the database and act accordingly.
an example
private boolean isOracle() {
Session session = (Session) entityManager.getDelegate();
Dialect dialect = ((SessionFactoryImpl) session.getSessionFactory()).getJdbcServices().getDialect();
return dialect.getClass().getName().contains("Oracle");
}
so if (isOracle()) you would use your oracleJpaNativeRepository

How does springboot JPA knows which database will be used?

I got to know Java spring JPA a couple days ago and there is one question which really makes me confused.
As I create a repository and use 'save()' method to save some objects into it. How does it know what type of database I am using and which local location to save.
I know I can config database (h2) like:
spring.datasource.url=jdbc:h2:mem/mydb
Then JPA will know: ok you are using h2 database and url is "jdbc:h2:mem/mydb"
However, some people said this config is not mandatory. If without this config, how does JPA knows which database I gonna use?
From the spring-boot documentation:
You should at least specify the URL by setting the spring.datasource.url property. Otherwise, Spring Boot tries to auto-configure an embedded database.
The following class is responsible for providing default settings for embedded DB: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
public String determineDatabaseName() {
...
if (this.embeddedDatabaseConnection != EmbeddedDatabaseConnection.NONE) {
return "testdb";
}
...
}
This answer can also be helpful: Where does the default datasource url for h2 come from on Spring Boot?

Hibernate ColumnTransformer and DataJpaTest

I do have a mysql database. That database reads and writes by hibernate and uses field encryption. The application is running on spring boot.
#ColumnTransformer(
read = "AES_DECRYPT(message, 'secret')",
write = "AES_ENCRYPT(?, 'secret')"
)
#Column(
columnDefinition = "varbinary(5120)"
)
private String field;
When writing unit tests I get an exception because these test is running on embedded h2 and the encryption methods are mysql based.
#RunWith(SpringRunner.class)
#DataJpaTest
I found this solution, but it does not work for me: How to Ignore Certain Fields in unit tests, Hibernate
Is there any way to test this behaviour and ignoring encryption and decryption in test configuration?
regards,
Moritz

Different UserType for different database in Hibernate

I'm developing an application in Spring Boot and Hibernate. I've created a class implementing UserType, which maps java.time.Duration to INTERVAL in PostgreSQL. Using it in following manner:
#TypeDefs({
#TypeDef(typeClass = DurationUserType.class, defaultForType = Duration.class)
}
works fine.
I'm running unit tests with H2. Since it doesn't support INTERVAL type, Hibernate fails at initialization with H2. I'm looking for the way to somehow associate DurationUserType with PostgreSQL dialect, so this it's not used in unit tests.

update of hibernate and spring security failed

I would like to update hibernate 3 to 4 and spring 3 to 3.1 and spring security 3 to 3.1 in my application, but when I do this the users with authorities generated using previous versions within database are not usable and the exception
java.io.InvalidClassException GrantedAuthorityImpl local class incompatible
occurred when the application want to fetch users' authorities from database. This is the config for entity user:
#ElementCollection(targetClass = GrantedAuthority.class, fetch=FetchType.EAGER)
#CollectionTable(name = "user_authorities", schema = "mydb", joinColumns = #javax.persistence.JoinColumn(name = "user_id"))
private Collection<GrantedAuthority> authorities;
Just checked differences between master and 3.0.x branches. Your problem is in the serialization which is done by Hibernate. Judging on the JPA configuration you are storing serializable POJOs (GrantedAuthorityImpl) objects directly in to the database. That is not optimal approach. Better would be to change this to store just collection of strings.
Workaround: You can put the old GrantedAuthorityImpl source in you sources so that the classloader will prefer your version. Then you should be OK with Spring Security 3.1 and still use your approach. However that is from the hack solutions category.

Resources