3

In my project I use Seam 3 and I am having issues injecting the EntityManager with the @Inject annotation. I am pretty sure there is some kind of configuration to make sure the EnityManager knows which PersistenceUnit to use. For example with EJB you can type:

@PersistenceContext(unitName="MY_PERSISTENCE_UNIT_NAME")
private EntityManager eManager;

which persistence unit is configured in the persistence.xml file. Here is my pseudo configuration:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
    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">

    <persistence-unit name="MY_PERSISTENCE_UNIT_NAME" transaction-type="JTA">

        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:jboss/TimeReportDS</jta-data-source>
        <mapping-file>META-INF/orm.xml</mapping-file> 

        <class>....</class>
        <class>....</class>
        <class>....</class>

        <properties>

            <property name="jboss.entity.manager.factory.jndi.name"
                value="java:/modelEntityManagerFactory" />

            <!-- PostgreSQL Configuration File -->
            <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
            <property name="hibernate.connection.password" value="password" />
            <property name="hibernate.connection.url" value="jdbc:postgresql://192.168.2.125:5432/t_report" />
            <property name="hibernate.connection.username" value="username" />

            <!-- Specifying DB Driver, providing hibernate cfg lookup
                 and providing transaction manager configuration -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
            <property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"/>
            <property name="hibernate.transaction.manager_lookup_class"
                value="org.hibernate.transaction.JBossTransactionManagerLookup" />
            <property name="hibernate.archive.autodetection" value="class" />

            <!-- Useful configuration during development - developer can see structured SQL queries -->
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="false" />

        </properties>
    </persistence-unit>
</persistence>  

I have read some articles about Seam 2 but there the configuration is made in components.xml file by adding the:

<persistence:managed-persistence-context
        name="entityManager" auto-create="true" persistence-unit-jndi-name="java:/modelEntityManagerFactory" />

inside the <components> tag. The next step in Seam 2 is to add the:

<property name="jboss.entity.manager.factory.jndi.name"
                value="java:/modelEntityManagerFactory" />

in the persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence ...>
    <persistence-unit name="MY_PERSISTENCE_UNIT_NAME" ...>
        ...
        <properties>
            ...

            <property name="jboss.entity.manager.factory.jndi.name"
                value="java:/modelEntityManagerFactory" />

        </properties>
    </persistence-unit>
</persistence>

but it seam that in Seam 3 there is no file components.xml. Also there is no attribute unitName in @Inject annotation to specify the persistence unit.

So please help me to configure my project so I can use the @Inject with EntityManager as shown in many examples on the net.

I use Postgres database and JBoss AS 7.

EDIT: Adding an example. I don't use the EntityManager in an Entity class.

@Named("validateReportAction")
@SessionScoped
public class ValidateReportAction extends ReportAction implements Serializable {

    private static final long serialVersionUID = -2456544897212149335L;

    @Inject
    private EntityManager em;
...
}  

Here in this @Inject I get warning from Eclipse "No bean is eligible for injection to the injection point [JSR-299 §5.2.1]"

If I use the @Inject on some beans that are marked as Entity the @Inject works fine.

nyxz
  • 6,918
  • 9
  • 54
  • 67
  • What problem are you having? You only have to use a discriminator in your annotation if you have multiple persistence units within the same persistence.xml. As it is you only have one which will be the default. Make sure your persistence.xml is in the same module (JAR) as your DAO and Entity classes. – Perception Jan 06 '12 at 19:24
  • @Perception please look at my post that I edited and give me some example for the discriminator. – nyxz Jan 06 '12 at 20:35
  • Maistora - you do not need a discriminator annotation, you only have one PU in your persistence.xml. But based off the error message you added I am pretty sure Eclipse is not finding your JBoss JEE implementation files. If you are using Maven then you need to add the jboss-javaee-web-6.0 dependency to your POM. If you are using some other build tool then you need to find and add the JAR to your project classpath manually. – Perception Jan 06 '12 at 20:49
  • If the message is that what bothers you I must say that I successfully use [et]Inject on some beans. For example @Inject private UserBean userBean; where the UserBean is marked as Entity. – nyxz Jan 07 '12 at 10:48

1 Answers1

5

You can use @PersistenceContext on a CDI bean. It doesn't have to be an EJB.

If for some reason you want to use @Inject, you have to do more work. @Inject doesn't know about the EntityManager; it can only inject other managed beans. Happily, there is a simple workaround - use a producer method that acts as a simple trampoline.

@ApplicationScoped
public class EntityManagerProducer {

    @PersistenceContext
    private EntityManager entityManager;

    @Produces
    @RequestScoped
    public EntityManager getEntityManager {
        return entityManager;
    }

    public void closeEntityManager(@Disposes EntityManager em) {
        if (em != null && em.getTransaction().isActive()) {
            em.getTransaction().rollback();
        }
        if (em != null && em.isOpen()) {
            em.close();
        }
    }

}

You can now use @Inject to inject the EntityManager. The injected EntityManager will be RequestScoped, while the EntityManagerProducer is ApplicationScoped. Furthermore, the entityManager must be closed.

clark
  • 388
  • 3
  • 20
Tom Anderson
  • 46,189
  • 17
  • 92
  • 133
  • That's very nice :) Thanks, but when I want to use entityManager.persist(MyObject) and the this error appear: javax.persistence.TransactionRequiredException: Transaction is required to perform this operation (either use a transaction or extended persistence context) – nyxz Jan 07 '12 at 13:43
  • So I added the PersistenceContext(type=PersistenceContextType.EXTENDED) but nothing happened. I read that this means that I should care about the transaction myself. Is there a way to make the container to manage the transaction? – nyxz Jan 07 '12 at 13:45
  • 1
    Afraid not. EJBs get transactions; CDI beans don't. Seam probably has something to help with this, but i don't know much about Seam. In plain EE, you would have to manage transactions yourself. Your options there are (a) manage transactions explicitly via UserTransaction in your bean, (b) add a filter and wrap the whole request cycle in a transaction, and (c) write a CDI interceptor that manages transactions (see http://smokeandice.blogspot.com/2009/12/cdi-and-declarative-transactions.html - this is really cool!). – Tom Anderson Jan 07 '12 at 22:20
  • 1
    @TomAnderson Since the `EntityManager` instance is not thread safe, shouldn't `EntityManagerProducer` be `@RequestScoped` instead of `@ApplicationScoped`? Even session or conversation seems dangerous since the same session could have multiple parallel requests. – Brian Jan 12 '12 at 19:05
  • 1
    @Brian: Great question. I hadn't thought of that. I know that CDI uses scope-aware proxies to safely handle injection of beans into other beans of wider scope (request into session and so on). Does that apply here? Or rather, does this apply at the point at which `@PersistenceContext` injects the EntityManager? If not, then certainly, what i've written is wrong, and the EntityManagerProducer should be request scoped. – Tom Anderson Jan 12 '12 at 21:42
  • From what I understand, since the `EntityManager` is defaulted to dependent scope, the producer method will always return the same instance. You can put `@RequestScoped` on either the field or producer method which, in my experimenting, has some effect. However, I'm not sure how to be confident that it's the desired effect. – Brian Jan 13 '12 at 18:22