spring-boothibernatejpaspring-data-jpah2

Could not generate CGLIB subclass of class org.h2.jdbcx.JdbcConnectionPool


Problem:

I'm working on a dependency migration task trying to migrate the Spring codebase from Spring 2.6.9 to Spring 2.7.0. However, am facing some issues related to the migration H2.

I read the migration guide of the Spring 2.7 which states

Spring Boot 2.7 has upgraded to H2 2.1.120. H2 2.x is backwards incompatible and fixes a number of security vulnerabilities. See the H2 changelog and migration guide for details of the changes and how to handle the upgrade.

application.properties

spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=password

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto = create
DataSource.java

@Configuration
public class DataSourceConfig {

    @Bean(name = "sessionFactory")
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(datasource());
        sessionFactory.setHibernateProperties(properties());

        return sessionFactory;
    }

    @Bean(name = "dataSource")
    JdbcConnectionPool datasource() {
        return JdbcConnectionPool.create("jdbc:h2:mem:testdb", "user", "password");
    }
    @Bean
    public PlatformTransactionManager hibernateTransactionManager() {
        HibernateTransactionManager transactionManager
                = new HibernateTransactionManager();
        transactionManager.setSessionFactory(sessionFactory().getObject());
        return transactionManager;
    }

    @Bean
    Properties properties() {
        Properties properties = new Properties();
        properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
        return properties;
    }

}
POM.xml
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
    </parent>

..

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>9.1.0</version>
        </dependency>
Stacktrace

When running my application I find below useful stack trace logs:

Caused by: org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'dataSource' defined in class path resource 
[DataSourceConfig.class]: Initialization of bean failed; nested exception is 
org.springframework.aop.framework.AopConfigException: 

Could not generate CGLIB subclass of class org.h2.jdbcx.JdbcConnectionPool: 
Common causes of this problem include using a final class or a non-visible class; 
nested exception is java.lang.IllegalArgumentException: Cannot subclass final class org.h2.jdbcx.JdbcConnectionPoo


Caused by: org.springframework.aop.framework.AopConfigException: 
Could not generate CGLIB subclass of class org.h2.jdbcx.JdbcConnectionPool: 
Common causes of this problem include using a final class or a non-visible class; 
nested exception is java.lang.IllegalArgumentException: Cannot subclass final class org.h2.jdbcx.JdbcConnectionPool
...


Caused by: java.lang.IllegalArgumentException: Cannot subclass final class org.h2.jdbcx.JdbcConnectionPool
at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:660) ~[spring-core-5.3.20.jar:5.3.20]
What didn't work!

I've read below questions/answers:

However, didn't find anything useful.

I'd appreciate any help. Thanks!


Solution

  • As commented by @M. Deinum

    Spring will create a DataSource for you based on the properties

    We don't need to create a datasource explicitly, and as far as sessionFactory is concerned. We can create one using LocalSessionFactoryBean as below:

    import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
    import javax.sql.DataSource
    
    @Configuration
    public class DataSourceConfig {
    
        @Bean(name = "sessionFactory")
        LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
            final var sessionFactory = new LocalSessionFactoryBean();
            sessionFactory.setDataSource(dataSource);
    
            return sessionFactory;
        }
    }