0

I am developing an API (JAXRS) using Java EE and trying to implement JPA. For that, I'm using EclipseLink as far as I know, and my app' is deployed on a Payara Server.

When I'm trying to use the persist method from EntityManager, there is nothing happen, with no error message or something else.

Here is my DAO class:

package shareloc.model.dao;

import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.List;
import java.util.Optional;

import static shareloc.ServletContextListenerImpl.createEntityManager;

public abstract class DAO<T> {
    @PersistenceContext(unitName = "MariaDB")
    protected EntityManager em;
    private Class<T> entityClass;

    public DAO(Class<T> entityClass) {
        this.entityClass = entityClass;
        this.em = getEntityManager();
    }

    private EntityManager getEntityManager() {
        if (em == null) {
            em = createEntityManager();
        }
        return em;
    }

    public T create(T entity) {
        em.persist(entity);

        return entity;
    }

    public void update(T entity) {
        em.merge(entity);
    }

    public void delete(T entity) {
        em.remove(em.merge(entity));
    }

    public Optional<T> findById(Object id) {
        return Optional.ofNullable(em.find(entityClass, id));
    }

    public List<T> findAll() {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<T> cq = cb.createQuery(entityClass);
        Root<T> root = cq.from(entityClass);

        cq.select(root);
        return em.createQuery(cq).getResultList();
    }
}

EJB User:

package shareloc.ejb;

import javax.persistence.*;

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private int userId;
    @Column(name = "pseudo")
    private String pseudo;
    @Column(name = "email")
    private String email;
    @Column(name = "password")
    private String password;
    @Column(name = "firstname")
    private String firstname;
    @Column(name = "lastname")
    private String lastname;

    public User() {}

    public User(String pseudo, String email, String password, String firstname, String lastname) {
        this.pseudo = pseudo;
        this.email = email;
        this.password = password;
        this.firstname = firstname;
        this.lastname = lastname;
    }

    public User(int userId, String pseudo, String email, String password, String firstname, String lastname) {
        this.userId = userId;
        this.pseudo = pseudo;
        this.email = email;
        this.password = password;
        this.firstname = firstname;
        this.lastname = lastname;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getPseudo() {
        return pseudo;
    }

    public void setPseudo(String pseudo) {
        this.pseudo = pseudo;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getFirstname() {
        return firstname;
    }

    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }
}

The method which calling the DAO Create method:

public static HashMap<String, String> register(String email, String pseudo, String password, String firstname, String lastname) {
        HashMap<String, String> formError = new HashMap<>();

        formError.putAll(checkEmail(email));
        formError.putAll(checkPassword(password));
        formError.putAll(checkPseudo(pseudo));
        formError.putAll(checkFirstname(firstname));
        formError.putAll(checkLastname(lastname));

        if (formError.isEmpty()) {
            System.out.println("form error empty test");
            userDAO.create(new User(pseudo, email, password, firstname, lastname));
        }

        return formError;
    }

Authentication root:

    @POST
    @Path("register")
    @Produces(MediaType.APPLICATION_JSON)
    public Response register(@QueryParam("email") String email, @QueryParam("pseudo") String pseudo,
                             @QueryParam("password") String password, @QueryParam("firstname") String firstname,
                             @QueryParam("lastname") String lastname) {
        HashMap<String, String> errorMsgs = AuthManager.register(email, pseudo, password, firstname, lastname);

        if (errorMsgs.isEmpty()) {
            return Response.ok().build();
        } else {
            GenericEntity<HashMap<String, String>> entity =
                    new GenericEntity<>(errorMsgs) {};

            return Response.status(Response.Status.BAD_REQUEST).entity(entity).build();
        }
    }

The ServletContextListener to create an destroy the em Factory:

@WebListener
public class ServletContextListenerImpl implements ServletContextListener {
    private static EntityManagerFactory emf;

    // Application start-up
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        emf = Persistence.createEntityManagerFactory("MariaDB");
    }

    // Application destroyed
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        emf.close();
    }

    public static EntityManager createEntityManager() {
        if (emf == null)
            throw new NullPointerException("Context is not initialized yet.");

        return emf.createEntityManager();
    }
}

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>fr.unistra.iutrs</groupId>
    <artifactId>ShareLoc-API</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>ShareLoc-API</name>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
        <junit.version>5.6.2</junit.version>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.eclipse.persistence/org.eclipse.persistence.jpa -->
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa</artifactId>
            <version>3.0.0-M1</version>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>8.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.mvc</groupId>
            <artifactId>javax.mvc-api</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
        <dependency>
            <groupId>org.mariadb.jdbc</groupId>
            <artifactId>mariadb-java-client</artifactId>
            <version>2.7.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>9</source>
                    <target>9</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

persistence.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
             version="2.2">
    <persistence-unit name="MariaDB" transaction-type="JTA">
        <jta-data-source>java:global/mariadb</jta-data-source>
        <properties>
            <property name="javax.persistence.transactionType" value="JTA"/>
            <property name="javax.persistence.jdbc.driver" value="org.mariadb.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mariadb://mysql.iutrs.unistra.fr:3306/sharelocda" />
            <property name="javax.persistence.jdbc.user" value="" />
            <property name="javax.persistence.jdbc.password" value="" />

            <property name="eclipselink.target-database" value="MySQL"/>
            <property name="eclipselink.logging.level.sql" value="FINE"/>
            <property name="eclipselink.logging.parameters" value="true"/>
        </properties>
    </persistence-unit>
</persistence>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <data-source>
        <name>java:global/mariadb</name>
        <class-name>org.mariadb.jdbc.MariaDbDataSource</class-name>
        <server-name>mysql.iutrs.unistra.fr</server-name>
        <port-number>3306</port-number>
        <database-name>sharelocda</database-name>
        <user></user>
        <password></password>
    </data-source>


    <listener>
        <listener-class>
            shareloc.ServletContextListenerImpl
        </listener-class>
    </listener>
</web-app>

And, here is the logs from Payara:

[2020-11-14T17:38:44.418+0100] [Payara 5.2020.5] [WARNING] [] [javax.enterprise.resource.resourceadapter.org.glassfish.jdbc.deployer] [tid: _ThreadID=125 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1605371924418] [levelValue: 900] [[
  Value of maxPoolSize Given, -1, was outside the bounds, default value of 32 will be used - PLEASE UPDATE YOUR VALUE]]

[2020-11-14T17:38:44.419+0100] [Payara 5.2020.5] [WARNING] [] [javax.enterprise.resource.resourceadapter.org.glassfish.jdbc.deployer] [tid: _ThreadID=125 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1605371924419] [levelValue: 900] [[
  Value of steadyPoolSize Given, -1, was outside the bounds, default value of 8 will be used - PLEASE UPDATE YOUR VALUE]]

[2020-11-14T17:38:44.499+0100] [Payara 5.2020.5] [INFO] [] [org.eclipse.persistence.session./file:/D:/Utilisateurs/razor/Documents/Etudes/IUT/IUT Robert Schuman/LP CDAD/LP1 - Développement Service Web/ShareLoc-API/target/ShareLoc-API-1.0-SNAPSHOT/WEB-INF/classes/_MariaDB] [tid: _ThreadID=125 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1605371924499] [levelValue: 800] [[
  EclipseLink, version: Eclipse Persistence Services - 2.7.7.payara-p2]]

[2020-11-14T17:38:45.791+0100] [Payara 5.2020.5] [INFO] [] [fish.payara.micro.cdi.extension.ClusteredCDIEventBusImpl] [tid: _ThreadID=125 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1605371925791] [levelValue: 800] [[
  Clustered CDI Event bus initialized]]

[2020-11-14T17:38:45.856+0100] [Payara 5.2020.5] [INFO] [] [org.glassfish.soteria.servlet.SamRegistrationInstaller] [tid: _ThreadID=125 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1605371925856] [levelValue: 800] [[
  Initializing Soteria 1.1-b01.payara-p5 for context '/ShareLoc-API-1.0-SNAPSHOT']]

[2020-11-14T17:38:45.947+0100] [Payara 5.2020.5] [INFO] [jsf.config.listener.version] [javax.enterprise.resource.webcontainer.jsf.config] [tid: _ThreadID=125 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1605371925947] [levelValue: 800] [[
  Initializing Mojarra |version.string| for context '/ShareLoc-API-1.0-SNAPSHOT']]

[2020-11-14T17:38:46.062+0100] [Payara 5.2020.5] [FINE] [] [org.eclipse.persistence.default] [tid: _ThreadID=125 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1605371926062] [levelValue: 500] [[
  SAXParserFactory instance: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl@72289c40]]

[2020-11-14T17:38:46.114+0100] [Payara 5.2020.5] [INFO] [AS-WEB-GLUE-00172] [javax.enterprise.web] [tid: _ThreadID=125 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1605371926114] [levelValue: 800] [[
  Loading application [ShareLoc-API-1.0-SNAPSHOT] at [/ShareLoc-API-1.0-SNAPSHOT]]]

[2020-11-14T17:38:46.147+0100] [Payara 5.2020.5] [INFO] [] [javax.enterprise.system.core] [tid: _ThreadID=125 _ThreadName=admin-thread-pool::admin-listener(1)] [timeMillis: 1605371926147] [levelValue: 800] [[
  ShareLoc-API-1.0-SNAPSHOT was successfully deployed in 2 990 milliseconds.]]

[2020-11-14T17:39:01.341+0100] [Payara 5.2020.5] [FINE] [] [org.eclipse.persistence.session./file:/D:/Utilisateurs/razor/Documents/Etudes/IUT/IUT Robert Schuman/LP CDAD/LP1 - Développement Service Web/ShareLoc-API/target/ShareLoc-API-1.0-SNAPSHOT/WEB-INF/classes/_MariaDB.sql] [tid: _ThreadID=111 _ThreadName=http-thread-pool::http-listener-1(4)] [timeMillis: 1605371941341] [levelValue: 500] [[
  SELECT user_id, email, firstname, lastname, password, pseudo FROM user WHERE (email = ?)
    bind => [test@gmail.fr]]]

[2020-11-14T17:39:01.429+0100] [Payara 5.2020.5] [INFO] [] [] [tid: _ThreadID=111 _ThreadName=http-thread-pool::http-listener-1(4)] [timeMillis: 1605371941429] [levelValue: 800] [[
  form error empty test]]

So, there is no exception, nothing to explain me what am I doing wrong. I took a look on internet before asking, and I found people having the same issue, but no solution solved my problem...

If someone can solve my problem, I'll be really happy ! (Because omg, I got a lot of problems since I'm learning Java EE lol) If you need more info' or code, please tell me! Thanks!

EDIT

public class AuthManager {
    private static UserDAO userDAO = new UserDAO(); // I'm calling my DAO with this.

    public static HashMap<String, String> register(String email, String pseudo, String password, String firstname, String lastname) {
        HashMap<String, String> formError = new HashMap<>();
       
       // Some check about the email, pseudo, etc..

        if (formError.isEmpty()) {
            System.out.println("form error empty test");
            userDAO.create(new User(pseudo, email, password, firstname, lastname)); // That the create method from the DAO wihch only do a em.persist
        }

        return formError;
    }

And here is my UserDAO class:

public class UserDAO extends DAO<User> {
    public UserDAO() {
        super(User.class);
    }

    @Transactional
    public Optional<User> findByEmail(String email) {
        User user;

        Query query = em.createQuery("SELECT u FROM User u WHERE u.email = :email");
        query.setParameter("email", email);

        try {
            user = (User) query.getSingleResult();
            return Optional.of(user);
        } catch (NoResultException e) {
            return Optional.empty();
        }
    }
}

And the DAO:

public abstract class DAO<T> {
    @PersistenceContext(unitName = "MariaDB")
    protected EntityManager em;
    private Class<T> entityClass;

    public DAO(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    @Transactional
    public T create(T entity) {
        em.persist(entity);

        return entity;
    }
    
    // + update, findAll, etc...

Deewens
  • 107
  • 2
  • 11
  • Does this answer your question? [EntityManager persist() not saving anything to database](https://stackoverflow.com/questions/5370736/entitymanager-persist-not-saving-anything-to-database) – crizzis Nov 15 '20 at 13:33
  • No, because I'm not using the Spring Framework... – Deewens Nov 15 '20 at 15:15
  • I was refering to [this answer](https://stackoverflow.com/a/5370855/1092818) – crizzis Nov 15 '20 at 15:26
  • I get this exception : javax.servlet.ServletException: java.lang.IllegalStateException: Exception Description: Cannot use an EntityTransaction while using JTA. – Deewens Nov 15 '20 at 15:41

1 Answers1

1

I'm confused. You've configured the persistence unit to point to a data source managed by your container (java:global/mariadb) and also configured the JDBC properties? You have to make up your mind, either use the datasource provided by the container or configure it yourself, but not both at the same time (in other words, the jta-data-source property is mutually exclusive with the javax.persistence.jdbc.* properties).

You're getting the error because you've set the persistence unit to use JTA, and then you're trying to begin a transaction somewhere using em.getTransaction(), which is not allowed for JTA. Also, you put @PersistenceContext on top of entityManager, which means right after the constructor gets invoked, the entityManager is probably getting overwritten by an injected instance.

The solution is, assuming you do actually want to use JTA, to inject the EntityManager using @PersistenceContext (you shouldn't have to initialize it manually), and then annotating the methods that you want to execute atomically with @Transactional, instead of trying to use entityManager.getTransaction().

crizzis
  • 9,978
  • 2
  • 28
  • 47
  • Ok, so I deleted the jdbc properties in my persistence.xml. And, If my understanding is good. Do I still have to open and close the entityManagerFactory in my "ServletContextListenerImpl" class ? – Deewens Nov 15 '20 at 18:24
  • And I also have the problem that the @PersistenceContext does not injectif the entityManager. I'm getting a null pointer exception if I'm not using entityManagerFactory.createEntityManager(). ` @PersistenceContext(unitName = "MariaDB") protected EntityManager em; private Class entityClass; public DAO(Class entityClass) { this.entityClass = entityClass; } @Transactional public T create(T entity) { em.persist(entity); return entity; } ` – Deewens Nov 15 '20 at 18:25
  • When using JEE, you don't interact with `EntityManagerFactory` directly. Please show the code that uses a concrete `DAO` (you are injecting the DAO as a bean, right?) – crizzis Nov 15 '20 at 18:36
  • `You are injecting the DAO as a bean, right?` I don't know, I edited my first post with my DAO, but I'm not sure I understood what you mean. Btw, thanks for helping me ^^ – Deewens Nov 15 '20 at 18:48
  • 1
    You can't create beans using constructors and expect injection or `@Transactional` to work. You need to use them as beans and make the container create them for you. You should inject `AuthManager` to your endpoint class using `@Inject private AuthManager authManager`, then inject the `UserDAO` into `AuthManager` using `@Inject private UserDAO` (`register` must then be converted into a non-static method) and so on. I would suggest that you read [the docs](https://docs.oracle.com/javaee/7/tutorial/cdi-basic.htm) for an example of how to use DI – crizzis Nov 15 '20 at 19:05
  • 1
    Oooh, ok, I did not understand that, big thanks, now it works !! – Deewens Nov 16 '20 at 18:07