-->

Java EntityManager null with @PersistenceContext

2019-08-23 03:48发布

问题:

I'm using Wildfly 10, Jersey, and injecting dependencies with @Inject. I have DAO and Service interfaces with their implementations declared in a CustomBinder. The injections work well, but the EntityManager is injected null with the @PersistenceContext annotation. I'm using MySQL and the datasource test-connection is working.

API Rest class

@Path("/account")
public class CuentaServiceRS {

    @Inject
    private ICuentaService cuentaService;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Cuenta getCuenta() {
        return cuentaService.getCuentas().get(0);       
    }

}

The implementation of ICuentaService

@Stateless
public class CuentaServiceImpl implements ICuentaService {

    @Inject
    private ICuentaDAO cuentaDAO;

    @Override
    public List<Cuenta> getCuentas() {
        List<Cuenta> cuentas = cuentaDAO.getAllCuentas();
        cuentas;
    }

}

The implementation of CuentaDAO

@Stateless
public class CuentaDAOImpl implements ICuentaDAO {

    @PersistenceContext(unitName = "altitudePU")
    protected EntityManager em;

    @Override
    public List<Cuenta> getAllCuentas() {
        CriteriaQuery<Cuenta> cq = em.getCriteriaBuilder().createQuery(Cuenta.class);

        /...

        return resultlist;
    }

}

My persistence-unit in persistence.xml

<persistence-unit name="altitudePU">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:jboss/datasources/AltitudePU</jta-data-source>
    <class>ar.com.olx.domain.Cuenta</class>
    <properties>
        <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
    </properties>    
</persistence-unit>

The servlet configured on web.xml

<servlet>
        <servlet-name>altitudeservlet</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>ar.com.villat.bind.ApplicationJaxRS</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
</servlet>

My custom AplicationJaxRS which extendes from ResourceConfig

public class ApplicationJaxRS extends ResourceConfig {

    public ApplicationJaxRS(){
        register(new CustomBinder());
        packages(true, "ar.com.olx");
    }

}

The CustomBinder

public class CustomBinder extends AbstractBinder {

    @Override
    protected void configure() {
        bind(CuentaServiceImpl.class).to(ICuentaService.class);
        bind(CuentaDAOImpl.class).to(ICuentaDAO.class);
    }

}

And finally, my pom.xml dependencies related to this post

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.22.2</version>
</dependency>

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>javax.enterprise</groupId>
    <artifactId>cdi-api</artifactId>
    <version>1.2</version>
</dependency>

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>7.0</version>
</dependency>

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-annotations</artifactId>
    <version>3.5.6-Final</version>
</dependency>

<dependency>
    <groupId>org.hibernate.javax.persistence</groupId>
    <artifactId>hibernate-jpa-2.1-api</artifactId>
    <version>1.0.0.Final</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>org.jboss.spec.javax.ejb</groupId>
    <artifactId>jboss-ejb-api_3.2_spec</artifactId>
    <version>1.0.0.Final</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.39</version>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.5</version>
</dependency>

<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
    <scope>provided</scope>
</dependency>

If you need more information, please tell me.

回答1:

As you are using a full stack JavaEE server you can simplify this a lot.

If you have not added a MySQL datasource to WildFly then you can do it like this:

WildFly Datasource Setup

  1. Create a file called deploy-mysql-ds.cli with the following content:

    # Execute offline
    embed-server --server-config=standalone.xml
    
    deploy $HOME/.m2/repository/mysql/mysql-connector-java-5.1.39.jar
    
    # Add the application datasource
    data-source add \
        --name=AltitudeDS \
        --driver-name=mysql-connector-java-5.1.39.jar \
        --connection-url=jdbc:mysql://localhost:3306/altitudeDB \
        --jndi-name=java:jboss/datasources/AltitudePU \
        --user-name=$USER_NAME \
        --password=$PASSWORD
    
  2. Replace $HOME, $USER_NAME and $PASSWORD with real values

  3. Configure the data source in Wildfly by executing it like:

    $JBOSS_HOME/bin/jboss-cli.sh --file="deploy-mysql-ds.cli"
    

You only need to do this once.

Note that the --jndi-name matches the <jta-data-source>...</jta-data-source> in your persistence.xml file.

You do not need the mysql-connector-java-5.1.39.jar in your maven dependencies because it has been added to the server directly. There are other solutions that involve adding the jar as a "module" but I find this way to be much simpler.

Amend Application

Amend your application as follows:

  1. Remove all the redundant dependencies:

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>
    </dependencies>
    
  2. Remove the AplicationJaxRS and CustomBinder classes and add a JAXRSConfiguration class:

    /**
     * Configures a JAX-RS endpoint
     */
    @ApplicationPath("resources")
    public class JAXRSConfiguration extends Application {
    }
    
  3. Remove the altitudeservlet from the web.xml. If that is the only thing in it then completely remove the whole file.

Test it

You should be able to deploy this web application and access it from:

http://localhost:8080/altitude-webapp/resources/account

where altitude-webapp is the name of your WAR file.