javascalahibernate

Complete steps for enabling second-level caching with Hibernate?


I have seen lots of different guides for enabling second-level caching with Hibernate, all either out of date or with errors that are not described with a fix.

Currently, this is my setup:

My build.sbt libraries for Hibernate:

  "org.hibernate.orm"               % "hibernate-core"            % "6.6.3.Final",
  "org.hibernate.validator"         % "hibernate-validator"       % "8.0.0.Final",
  "org.glassfish"                   % "jakarta.el"                % "5.0.0-M1"                              % Test,
  "io.agroal"                       % "agroal-pool"               % "2.5",
  "org.hibernate.orm"               % "hibernate-agroal"          % "6.4.4.Final",
  "com.mysql"                       % "mysql-connector-j"         % "9.1.0",
  "jakarta.el"                      % "jakarta.el-api"            % "6.0.1",
  "com.sun.el"                      % "el-ri"                     % "3.0.4",
  "jakarta.el"                      % "jakarta.el-api"            % "6.0.1",
  "org.hibernate.orm"               % "hibernate-jcache"          % "6.6.9.Final",
  "org.ehcache"                     % "ehcache"                   % "3.10.8",
  "org.hibernate.orm"               % "hibernate-ehcache"         % "6.0.0.Alpha7",
  "jakarta.xml.bind"                % "jakarta.xml.bind-api"      % "4.0.2",
  "org.glassfish.jaxb"              % "jaxb-runtime"              % "4.0.5",
  "jakarta.transaction"             % "jakarta.transaction-api"   % "2.0.1",

Database creation:

    val config = new Configuration()
      .addAnnotatedClass(classOf[Server])
      .addAnnotatedClass(classOf[Chat])
      .addAnnotatedClass(classOf[User])
      .setProperty(JAKARTA_JDBC_URL, "jdbc:mysql://localhost:3306/hydra_data")
      .setProperty(JAKARTA_JDBC_USER, sqlUser)
      .setProperty(JAKARTA_JDBC_PASSWORD, sqlPass)
      .setProperty("hibernate.hbm2ddl.auto", "create")
      // use Agroal connection pool
      .setProperty("hibernate.agroal.maxSize", "20")
      .setProperty("hibernate.hbm2ddl.auto", "update")
      .setProperty("hibernate.cache.use_query_cache", true)
      .setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory")
      .setProperty("hibernate.javax.cache.provider", "org.ehcache.jsr107.EhcacheCachingProvider")

    sessionFactory = Option(config.buildSessionFactory())

Currently, I have this error:

Exception in thread "main" java.lang.NoClassDefFoundError: javax/transaction/SystemException
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:578)
    at java.base/java.lang.Class.forName(Class.java:557)
    at org.jboss.logging.Logger.doGetMessageLogger(Logger.java:2562)
    at org.jboss.logging.Logger.getMessageLogger(Logger.java:2530)
    at org.jboss.logging.Logger.getMessageLogger(Logger.java:2516)
    at org.hibernate.cache.ehcache.internal.EhCacheMessageLogger.<clinit>(EhCacheMessageLogger.java:31)
    at org.hibernate.cache.ehcache.internal.EhcacheRegionFactory.<clinit>(EhcacheRegionFactory.java:46)
    at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
    at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1161)
    at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.ensureClassInitialized(MethodHandleAccessorFactory.java:340)
    at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newConstructorAccessor(MethodHandleAccessorFactory.java:103)
    at java.base/jdk.internal.reflect.ReflectionFactory.newConstructorAccessor(ReflectionFactory.java:173)
    at java.base/java.lang.reflect.Constructor.acquireConstructorAccessor(Constructor.java:548)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:498)
    at java.base/java.lang.reflect.ReflectAccess.newInstance(ReflectAccess.java:132)
    at java.base/jdk.internal.reflect.ReflectionFactory.newInstance(ReflectionFactory.java:259)
    at java.base/java.lang.Class.newInstance(Class.java:804)
    at org.hibernate.cache.internal.StrategyCreatorRegionFactoryImpl.create(StrategyCreatorRegionFactoryImpl.java:62)
    at org.hibernate.cache.internal.StrategyCreatorRegionFactoryImpl.create(StrategyCreatorRegionFactoryImpl.java:23)
    at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.resolveStrategy(StrategySelectorImpl.java:240)
    at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.resolveStrategy(StrategySelectorImpl.java:189)
    at org.hibernate.cache.internal.RegionFactoryInitiator.resolveRegionFactory(RegionFactoryInitiator.java:96)
    at org.hibernate.cache.internal.RegionFactoryInitiator.initiateService(RegionFactoryInitiator.java:47)
    at org.hibernate.cache.internal.RegionFactoryInitiator.initiateService(RegionFactoryInitiator.java:32)
    at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:130)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:238)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:215)
    at org.hibernate.boot.internal.MetadataBuilderImpl$MetadataBuildingOptionsImpl.<init>(MetadataBuilderImpl.java:705)
    at org.hibernate.boot.internal.MetadataBuilderImpl.<init>(MetadataBuilderImpl.java:139)
    at org.hibernate.boot.MetadataSources.getMetadataBuilder(MetadataSources.java:164)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:899)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:999)
    at database.Database.init(Database.scala:79)
    at database.DatabaseUtil$.getInstance(Database.scala:29)
    at core.Main$.main(Main.scala:34)
    at core.Main.main(Main.scala)
Caused by: java.lang.ClassNotFoundException: javax.transaction.SystemException
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528)
    ... 38 more

Which, from what I've read, should be fixed with the import of jakarta.transaction-api, but it is not.

Can someone provide complete steps for enabling caching properly please?


Solution

  • So it turns out it was a combination of mismatched packages and some errors in my config.

    Working imports:

      // Hibernate stuff
      "org.hibernate.orm"               % "hibernate-core"            % "6.6.3.Final",
      "org.hibernate.orm"               % "hibernate-jcache"          % "6.6.3.Final",
      "org.hibernate.validator"         % "hibernate-validator"       % "8.0.0.Final",
      "io.agroal"                       % "agroal-pool"               % "2.5",
      "org.hibernate.orm"               % "hibernate-agroal"          % "6.6.3.Final",
      "com.mysql"                       % "mysql-connector-j"         % "9.2.0",
      "jakarta.el"                      % "jakarta.el-api"            % "6.0.1",
      "com.sun.el"                      % "el-ri"                     % "3.0.4",
      "jakarta.el"                      % "jakarta.el-api"            % "6.0.1",
      "org.ehcache"                     % "ehcache"                   % "3.10.8",
      "jakarta.xml.bind"                % "jakarta.xml.bind-api"      % "4.0.2",
      "org.glassfish.jaxb"              % "jaxb-runtime"              % "4.0.5",
      "jakarta.transaction"             % "jakarta.transaction-api"   % "2.0.1",
    

    I ensured the versions of hibernate packages and ehcache are compatible.

    And the hibernate config:

        val config = new Configuration()
          .addAnnotatedClass(classOf[Server])
          .addAnnotatedClass(classOf[Chat])
          .addAnnotatedClass(classOf[User])
          .setProperty(JAKARTA_JDBC_URL, "jdbc:mysql://localhost:3306/hydra_data")
          .setProperty(JAKARTA_JDBC_USER, sqlUser)
          .setProperty(JAKARTA_JDBC_PASSWORD, sqlPass)
          .setProperty("hibernate.hbm2ddl.auto", "create")
          // use Agroal connection pool
          .setProperty("hibernate.agroal.maxSize", "20")
          .setProperty("hibernate.hbm2ddl.auto", "update")
          .setProperty("hibernate.cache.use_second_level_cache", true)
          .setProperty("hibernate.cache.use_query_cache", true)
          .setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.jcache.JCacheRegionFactory")
          .setProperty("hibernate.javax.cache.provider", "org.ehcache.jsr107.EhcacheCachingProvider")