Search code examples
javahibernatemavenjpajakarta-ee

glassfish 7 + jakarta ee 10 + hibernate entity manager java.lang.NoClassDefFoundError: org/hibernate/annotations/common/reflection/ReflectionManager


I am trying to get hibernate working with glassfish 7 jdbc. I got the configuration of the pool right and ping is a success. I have put mysql-connector-j-8.0.33.jar and hibernate-core-6.2.6.Final.jar in glassfish/domain/domain1/lib. My persistence.xml looks like this:

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
             version="3.0">

    <persistence-unit name="persistence-unit">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>jpa1</jta-data-source>
        <class>jpa1.User</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
            <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
    <!--
    <persistence-unit name="persistence-unit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="jakarta.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:8889/learn_it_jpa?autoReconnect=true&amp;useSSL=false"/>
            <property name="jakarta.persistence.jdbc.user" value="root"/>
            <property name="jakarta.persistence.jdbc.password" value="root"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="jakarta.persistence.schema-generation.database.action" value="none"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
        </properties>
    </persistence-unit>
    -->
</persistence>

When I am trying to use hibernate with resource_local there is no error. But when I try to use the jdbc persistence-unit im getting following error:

java.lang.NoClassDefFoundError: org/hibernate/annotations/common/reflection/ReflectionManager

I have added hibernate-core-6.2.6.Final.jar in the lib folder of glassfishs domain but i still getting noclassfounderror. With the resource_local persistence-unit i dont get that error. I understand that its possible to add further jar files in the lib folder to solve this problem but i might doing something wrong because the hibernate-core was working with the resource_local persistence-unit.

My pom file

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.example</groupId>
  <artifactId>jpa1</artifactId>
  <packaging>war</packaging>
  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
  </properties>
  <version>1.0-SNAPSHOT</version>
  <name>jpa1 Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
      <version>8.0.33</version>
    </dependency>
    <dependency>
      <groupId>jakarta.platform</groupId>
      <artifactId>jakarta.jakartaee-api</artifactId>
      <version>10.0.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>jakarta.persistence</groupId>
      <artifactId>jakarta.persistence-api</artifactId>
      <version>3.1.0</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>6.2.6.Final</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>jpa1</finalName>
  </build>
</project>

The code where I am using jpa

package jpa1;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.List;

@WebServlet(name = "homeServlet", value = "/start")
public class HomeServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("persistence-unit");
        EntityManager entityManager = entityManagerFactory.createEntityManager();

        entityManager.getTransaction().begin();

        User user1 = new User("Max","Mustermann", User.Gender.MALE, 10, 50000, 1000);

        entityManager.persist(user1);
        entityManager.getTransaction().commit();

        List<User> results = entityManager
                .createQuery("Select a from User a", User.class)
                .getResultList();


        entityManager.close();
        entityManagerFactory.close();

        req.setAttribute("test", results);
        req.getRequestDispatcher("start.jsp").forward(req, resp);
    }
}

The entity:

package jpa1;

import jakarta.persistence.*;

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column(name = "gender")
    @Enumerated(EnumType.ORDINAL)
    private Gender gender;

    @Column(name = "age")
    private int age;

    @Column(name = "salary")
    private int salary;

    @Column(name = "test")
    private int test;

    public User() {

    }

    public enum Gender {
        MALE,
        FEMALE
    }

    public User(String firstName, String lastName, Gender gender, int age, int salary, int test) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.gender = gender;
        this.age = age;
        this.salary = salary;
        this.test = test;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public Gender getGender() {
        return gender;
    }

    public String getGenderStr() {
        switch (gender) {
            case MALE -> {
                return "Männlich";
            }
            case FEMALE -> {
                return "Weiblich";
            }
            default -> {
                return "";
            }
        }
    }

    public int getAge() {
        return age;
    }
}

What I am using:

  • glassfish 7.06
  • hibernate 6.2.6

Solution

  • I have added now the following jars in the glassfish/domain/domain/lib folder to add the missing classes needed for hibernate:

    • hibernate-commons-annotations-6.0.6.Final.jar
    • jandex-3.1.2.jar
    • byte-buddy-1.12.9.jar
    • antlr4-runtime-4.10.1.jar

    With that all missing classes are added but after the first successful using the entitymanager the next refresh of the page theres an endless loop loading.

    When i stop the server the following error message appears:

      Error allocating connection: [org.glassfish.resourcebase.resources.api.PoolInfo@f6def2b2[jndiName=test/jpa1, applicationName=null, moduleName=null]: No Pool Meta Data object associated with the pool : org.glassfish.resourcebase.resources.api.PoolInfo@f6def2b2[jndiName=test/jpa1, applicationName=null, moduleName=null]. Try redeploying the application.]]]
    
      SQL Error: 0, SQLState: null]]
    

    I found for sql error 0, sqlstate null a post SQL Error: 0, SQLState: null

    The problem was that the EntityManagerFactory was closed and the connection was lost. When i not close the factory the connection will be established as long as the app runs. Thats how i solved it:

    package jpa1;
    
    import jakarta.persistence.EntityManager;
    import jakarta.persistence.EntityManagerFactory;
    import jakarta.persistence.Persistence;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    import java.util.List;
    
    @WebServlet(name = "homeServlet", value = "/start")
    public class HomeServlet extends HttpServlet {
    
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("persistence-unit");
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            EntityManager entityManager = entityManagerFactory.createEntityManager();
            entityManager.getTransaction().begin();
    
            User user1 = new User("Max","Mustermann", User.Gender.MALE, 10, 50000, 1000);
    
            entityManager.persist(user1);
            entityManager.getTransaction().commit();
    
            List<User> results = entityManager
                    .createQuery("Select a from User a", User.class)
                    .getResultList();
    
    
            entityManager.close();
    
            req.setAttribute("test", results);
            req.getRequestDispatcher("start.jsp").forward(req, resp);
        }
    }
    

    EDIT: This only works when you always restart the server. If you redeploy it the endless loading loop continues.

    EDIT 2: After putting the EntityManagerFactory out of the servlet class into a seperate class and make it static theres no loop.

    Globals class:

    public class Globals {
    
        public static EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("persistence-unit");
    
    }