Configuration to be able to @Inject EntityManager in Seam 3
Asked Answered
B

1

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.

Balinese answered 6/1, 2012 at 19:11 Comment(4)
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.Atomic
@Atomic please look at my post that I edited and give me some example for the discriminator.Balinese
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.Atomic
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.Balinese
F
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.

Festus answered 7/1, 2012 at 12:18 Comment(6)
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)Balinese
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?Balinese
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 smokeandice.blogspot.com/2009/12/… - this is really cool!).Festus
@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.Tranquillity
@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.Festus
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.Tranquillity

© 2022 - 2024 — McMap. All rights reserved.