Affects: spring-orm 6.0.7 and earlier
In application running spring 6.0.7 and hibernate 6.2.0.final, when spring is creating hibernate session factory in Spring application context exception occurs:
java.lang.NoSuchMethodError: 'void org.springframework.orm.hibernate5.LocalSessionFactoryBuilder.setImplicitNamingStrategy(org.hibernate.boot.model.naming.ImplicitNamingStrategy)' at org.springframework.orm.hibernate5.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:547) at com.epox.service.system.EpoxLocalSessionFactoryBean.afterPropertiesSet(EpoxLocalSessionFactoryBean.java:30) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1816) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1766)
This is caused by the fact that in Hibernate 6.2 has changed return type for the method Configuration.setImplicitNamingStrategy
(instead of void Configuration
is returned).
Comment From: goetzseb
Same issue here for us. Hibernate's org.hibernate.cfg.Configuration class has changed the return types of most methods to Configuration to support method chaining, I guess, which breaks the compatibility with the version used as dependency in spring-core 6.0.8 (org.hibernate hibernate-core-jakarta 5.6.15.Final). The problem arises if using spring-core 6.x with hibernate-orm 6.x on the classpath. As in previous versions org.springframework.orm.hibernate5.LocalSessionFactoryBuilder bean is used to configure Hibernate. We do it with XML like this:
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean" depends-on="dataSource">
<property name="dataSource" ref="dataSource" />
<!-- entity search path -->
<property name="packagesToScan" value="com.acme.model" />
<property name="physicalNamingStrategy">
<bean class="${hibernate.physical_naming_strategy}"/>
</property>
<!-- package-level annotation search path -->
<property name="annotatedPackages">
<list>
<value>com.acme.model</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<!-- Qualify unqualified table names with the given schema/tablespace -->
<prop key="hibernate.default_schema">${hibernate.default_schema}</prop>
<!-- Use a customer schema name resolver since hibernate's default resolver is a little naive -->
<prop key="hibernate.schema_name_resolver">${hibernate.schema_name_resolver}</prop>
<!-- SQL dialect -->
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<!-- The second-level cache -->
<prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache}</prop>
<prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class}</prop>
<!-- Enable query cache -->
<prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>
<!-- Echo all executed SQL to stdout -->
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<!-- Enable statistics -->
<prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
<!-- Pretty print the SQL in the log and console. -->
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<!-- Prevents hibernate default save or update listener from throwing PropertyValueException prior to our entity listener being called -->
<prop key="hibernate.check_nullability">false</prop>
<!-- Maintain backwards compatibility for id generators to support existing databases -->
<prop key="hibernate.id.new_generator_mappings">${hibernate.id.new_generator_mappings}</prop>
<!-- Allow alternative naming strategy for physical table names -->
<prop key="hibernate.physical_naming_strategy">${hibernate.physical_naming_strategy}</prop>
<!-- Allow alternative naming strategy for physical table names -->
<prop key="hibernate.globally_quoted_identifiers">${hibernate.globally_quoted_identifiers}</prop>
<!-- Allow lazy initialization w/o detached objects -->
<prop key="hibernate.enable_lazy_load_no_trans">${hibernate.enable_lazy_load_no_trans}</prop>
<!-- ORACLE SPECIFICS -->
<!-- Stick to LONG RAW data type for backward compatibility -->
<prop key="hibernate.dialect.oracle.prefer_long_raw">${hibernate.dialect.oracle.prefer_long_raw}</prop>
</props>
</property>
</bean>
When the org.springframework.orm.hibernate5.LocalSessionFactoryBean.afterPropertiesSet() method is called after the bean is initialized, a LocalSessionFactoryBuilder instance is used to setup the SessionFactory.
Workaround: We have overridden this method with hibernate.orm 6.2.1 on the classpath to compile against the most current org.hibernate.cfg.Configuration class version. But it is a very nasty solution which requires Reflection, because most of the properties are not accessible otherwise.
Comment From: BladeWise
The problem can be seen even looking at the current LocalSessionFactoryBuilder
source code for setCurrentTenantIdentifierResolver
(spring-orm 6.0.9).
As stated above, on Hibernate 6.0+ this override should return a Configuration
, so the current LocalSessionFactoryBuilder
is not compatible with 6.0+.
Given that LocalSessionFactoryBuilder
is delcared in the hibernate5 package, is it expected that it is not compliant with 6.0, and an alternative approach should be used?
As far as I can see, Hibernate 6.2 should be supported since Spring 6.0.4 release.
Comment From: snicoll
The compatibility you're talking about is via the JPA persistence provider API, not the Hibernate API. We don't have a commitment for the latter and we expect most users to configure their apps via JPA.
Comment From: BladeWise
If this is the case, can we assume LocalSessionFactoryBuilder
is a deprecated functionality, and the only available support for Hibernate 6.0+ is through the JPA persistence provider API?
In my case, I was supporting both APIs even if the JPA is the one being actually used by the application, and removing the LocalSessionFactoryBuilder
bean fixes the issue.
Comment From: jezovuk
The problem is indeed caused by changes in Hibernate's Configuration
class, but it seems to me that it only affects Spring integration with Hibernate 6.2+, not 6.0.x and 6.1.x. See the original Hibernate commit here: https://github.com/hibernate/hibernate-orm/commit/7b493f30fbaba8c50aae4959dbab87136b6c8226#diff-ae5690e4fc2a7db82608af8ed7fd93334ae9b5b97cc2b86b63f62a4a0870e8c2.
If LocalSessionFactoryBuilder
is to remain available and supported, I guess there should be a way to support both Hibernate 5.x - 6.1.x and Hibernate 6.2.x. Perhaps separate spring-orm artefact built against Hibernate 6.2+?
Comment From: snicoll
The purpose of this very issue is to investigate a solution…
Comment From: BladeWise
In my opinion, the solution could be the same that has been used previously in spring-orm
when switching Hibernate support from 3 to 4, and from 4 to 5.
Checking the history of this repository, it seems that spring-orm
was initially supporting the previous version of Hibernate, providing an additional module for the new version (see tag 3.0.x
vs tag 4.0.x
), but later on it started providing support only to the latest version (see tag 5.0.x
).
Following this pattern, 6.0.x should either provide a spring-orm-hibernate6
module (or modules, given @jezovuk comment on the minor-version breaking change), or eventually drop support for Hibernate 5 in favor of Hibernate 6.2.
Comment From: snicoll
Thanks @BladeWise, but we know what our options are.
Comment From: jhoeller
If this is the case, can we assume LocalSessionFactoryBuilder is a deprecated functionality, and the only available support for Hibernate 6.0+ is through the JPA persistence provider API?
That's the case indeed for such purposes. While the orm.hibernate5
package is not technically deprecated, it is exclusively meant to be used with Hibernate ORM 5.x as per the package name. The javadoc explicitly states compatibility with Hibernate ORM 5.5/5.6.
FWIW, LocalSessionFactoryBuilder
is only part of the problem there. HibernateTemplate
and co have even stronger compatibility issues against Hibernate ORM 6. The parallel maintenance of an orm.hibernate5
or potential orm.hibernate6
package is not justifiable against Hibernate ORM 6 anymore, given how omnipresent JPA-style setup is in recent years.
All things considered, Hibernate ORM 6.x is only supported via JPA, by design. We have dedicated Hibernate support there in the form of HibernateJpaVendorAdapter
, supporting Hibernate provider specifics. It currently claims compatibility with 5.5/5.6 as well as 6.0/6.1; I'll make sure to document it for Hibernate ORM 6.2 as well.
Comment From: jhoeller
It turns out that there is actually a bit of alignment to do: Hibernate 6.2 removed deprecated dialect classes for Derby and PostgreSQL, so we have to use replacements there now for our purposes in HibernateJpaVendorAdapter
. This only applies for explicit use of the Database
enum which is not common since Hibernate's database autodetection works quite well anyway, but nevertheless, we need to consistently select a working dialect with Hibernate 6.2 there as well.
Comment From: andersb
I found this issue during my migration from Spring Boot 2.7 to 3.2 which have proven to be quite challenging :)
I decided several years ago to remove JPA (v2.x at the time) configuration in favor of using Spring/Hibernate with LocalSessionFactoryBean because I kept running into the fact that I needed to unwrap the entity manager to Session in order to use hibernate specific features. Now, JPA have of course evolved but even in Jakarta JPA 3.1, there are still features I use in Hibernate which requires unwrapping.
And configuration wise, LocalSessionFactoryBean feels a lot more straight forward and easy to use, in my opinion. (But I must admit, that comes from the fact that I'm a lot more used to this type of configuration).
So, I just wanted to mention that there would be at least 1 vote for having a orm.hibernate6.LocalSessionFactoryBean :)
I got everything working with Spring Boot 3.2 and Hibernate 5.6 so I will stick with that for now. Been using Spring Framework since v1.2.8, keep up the good work :)
Comment From: gregh3269
I read orm.hibernate5.LocalSessionFactoryBean for hibernate 6 should be migrated to use JPA style setup, is this migration documented some where? ie from
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="packagesToScan">
<list>
<value>my.pojos</value>
</list>
</property>
.....
</bean>
to persistence.xml?