3

I'm trying to develop a (Maven-based) jar library which can act as a DAL for my Java application.

This is my persistence.xml file

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

   <persistence-unit name="DALPersistenceUnit" transaction-type="JTA">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <jta-data-source>java:app/env/MyDataSource</jta-data-source>          
      <shared-cache-mode>NONE</shared-cache-mode>
      <properties>
         <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
      </properties>
   </persistence-unit>
</persistence>

This is the DataSource definition, done using the @DataSourceDefinition annotation

import javax.annotation.sql.DataSourceDefinition;
import javax.ejb.Startup;


@DataSourceDefinition(name = "java:app/env/MyDataSource",
                      className = "com.mysql.jdbc.Driver",
                      serverName="<serverIP>",
                      portNumber=3306,
                      user = "<username>",
                      password = "<pwd>",
                      databaseName = "<dbname>",
                      minPoolSize = 0,
                      initialPoolSize = 0
                      )
@Startup
public class MyDataSource {

}

This is the DAO which retrieves the EntityManager from the persistence.xml definition (I'm using QueryDSL to simplify query definitions)

package my.dal.dao;
import my.domain.dal.QUser;
import my.domain.dal.User;

import javax.enterprise.inject.Default;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;

import com.mysema.query.jpa.JPQLQuery;
import com.mysema.query.jpa.impl.JPAQuery;

@Default
public class UserDAO {

    @PersistenceContext(unitName = "DALPersistenceUnit", type = PersistenceContextType.EXTENDED)
    private EntityManager entityManager;

    public User getMark()
    {
        QUser qUser = QUser.user;
        JPQLQuery query = new JPAQuery(entityManager);          
        User mark = query.from(qUser).where(qUser.username.eq("mark")).uniqueResult(qUser);
        return mark;
    }       
}

Also, I added the beans.xml file in the META-INF folder

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
   http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">

</beans>

This is the test class (I'm using CDI-Unit for the CDI testing)

import my.dal.dao.UserDAO;
import my.domain.dal.User;
import static org.junit.Assert.assertTrue;
import javax.inject.Inject;
import org.jglue.cdiunit.CdiRunner;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(CdiRunner.class)
public class DALUserTest {

    @Inject UserDAO userDAO;

    @Test
    public void testGetMark()
    {
        User user = userDAO.getMark(); // Here I get a NullPointerException
        assertTrue(user.getUsername()=="mark");
    }
}

When I run the test I get a NullPointerException at the marked line in the test class. Moreover, if I debug the test I can see that the EntityManager entityManager field of the UserDAO class is null, so it is not injected from the persistence.xml file.

Am I missing something in the Java persistence/CDI mechanism?

Thank you

EDIT 1: I have added Maven dependencies to

querydsl-core:3.3.2
querydsl-apt:3.3.2
querydsl-jpa:3.3.2
log4j:1.2.16
mysql-connector-java:5.1.29
hibernate-entitymanager:4.3.1.Final
hibernate-validator:5.0.3.Final
cdi-unit:3.0.1
junit:4.11
javaee-api:7.0
hibernate-jpa-2.0-api:1.0.1.Final
hibernate-core:4.3.1.Final
hibernate-commons-annotations:4.0.4.Final

EDIT 2: Following @earthling advice, I added the following bean definition in the beans.xml file

<bean id="entityManagerFactory" class="org.hibernate.jpa.internal.EntityManagerFactoryImpl">
    <property name="persistenceUnitName" value="DALPersistenceUnit" />
    <property name="persistenceXmlLocation" value="classpath*:/src/main/resources/META-INF/persistence.xml" />
</bean> 

But I get the following error in the beans.xml file

cvc-complex-type.2.4.a: Invalid content was found starting with element 'bean'. One of '{"http://java.sun.com/xml/ns/javaee":interceptors, "http:// java.sun.com/xml/ns/javaee":decorators, "http://java.sun.com/xml/ns/javaee":alternatives}' is expected.

Jens Piegsa
  • 7,399
  • 5
  • 58
  • 106
gvdm
  • 3,006
  • 5
  • 35
  • 73

2 Answers2

2

You are configuring your datasource twice. In the persistence.xml and through @DataSourceDefinition.

Since you are not running your tests inside an application server you need to produce an EntityManager yourself. A good Idea would be to use an EntityManagerProducer which can be switch with an alternative to run tests.

@RequestScoped
public class EntityManagerProvider {

    @PersistenceContext
    private EntityManager entityManager;

    @Produces
    public EntityManager getEntityManager() {
        return entityManager;
    }

}

Then you need an Test-EntityManager Producer

@Alternative
@RequestScoped
public class EntityManagerProvider {


    private EntityManager entityManager;

    @Produces
    public EntityManager getEntityManager() {
        if(entityManager == null) {
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("...");
            entityManager = emf.createEntityManager();
        }
        return entityManager;
    }

}

You can access your EntityManager this way @Inject private EntityManager em

Lukas Eichler
  • 5,689
  • 1
  • 24
  • 43
  • The jar library I'm developing will be integrated in a web application which will run on Wildfly (JBoss AS 8), so in the end the library will run in a application server. Do I need to write the EntityManagerProvider class even in this case? – gvdm Jun 03 '14 at 12:39
  • This class is just to provide an EntityManager for your tests because they do not run on an application server. – Lukas Eichler Jun 03 '14 at 13:36
  • Ok. I've added the code you provided but at the injection point I get this warning message "No bean is eligible for injection to the injection point [JSR-299 §5.2.1]". If I try to run the test it stops throwing this exception http://pastebin.com/CnQmxj7Y – gvdm Jun 03 '14 at 13:46
  • Did you active the alternative by adding it to the beans.xml? More info to alternatives --> http://docs.oracle.com/javaee/6/tutorial/doc/gjsdf.html – Lukas Eichler Jun 03 '14 at 13:48
  • Thank you, I added the alternative tag to the beans.xml. I'm sorry, but the problem persists :( – gvdm Jun 03 '14 at 14:01
  • 1
    I added an return to the alternative EntityManagerProvider. Can you verfify that is class is working properly by calling getEntityManager and persisting something into the database? – Lukas Eichler Jun 03 '14 at 14:04
  • Might I ask what the purpose is of tagging the producer as @RequestScoped ? – Gimby Jun 03 '14 at 14:27
  • The Producer should work the same way as `@PersistenceContext`. Therefor it is requestscoped and gets an EntityManager injection on every request. – Lukas Eichler Jun 03 '14 at 14:33
  • This is the code I wrote to test the EntityManager (without injection). http://pastebin.com/j0jErMAc And it works: it adds the provided User to the DB. So the problem seems to be that the project cannot find a bean to inject as stated in the third comment (http://stackoverflow.com/questions/24011055/cannot-inject-entitymanager-declared-in-persistence-xml/24013394?noredirect=1#comment37017949_24013394) – gvdm Jun 03 '14 at 15:56
  • 1
    Try adding the alternative EntityManagerProvider like in the docs of cdi-uni:` @RunWith(CdiRunner.class) @AdditionalClasses(WarpDrive.class) // WarpDrive is available to use. class TestStarship { ` – Lukas Eichler Jun 03 '14 at 16:04
  • I tried adding @AdditionalClasses({EntityManagerProvider.class}) but the problem still remains (http://pastebin.com/CnQmxj7Y). Moreover, I tried adding @ActivatedAlternatives(EntityManagerProvider.class) and got this exception http://pastebin.com/51cYKVk3 – gvdm Jun 03 '14 at 16:27
  • Now the test runs. I added `@AdditionalClasses(EntityManagerProvider.class)` and `@ActivatedAlternatives(EntityManagerProvider.class)` to the test class. Moreover I deleted the `@RequestScoped` annotation to the `EntityManagerProvider` class (as the exception was saying). My doubt now is: will an application (which includes the library I've written) inject the the `EntityManager` from the `persistence.xml` or will it use the `EntityManagerProvider` to build the `EntityManager`? – gvdm Jun 05 '14 at 08:41
0

Try creating entityManagerFactory in your bean xml and inject in to your DAO class via constructor injection

say

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
         <property name="persistenceUnitName" value="DALPersistenceUnit" />
        <property name="persistenceXmlLocation" value="classpath*:/<path-to>/persistence.xml" />
</bean> 
earthling
  • 69
  • 3