Search code examples
javajpaeclipselinkpayarajava-ee-8

EntityManager.persist does not have any effect


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 => [[email protected]]]]

[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...


Solution

  • 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().