How to get Container Managed Transactions (CMT) working with EJB 3.1, Hibernate 3.6, JPA 2.0 , JBoss and MySQL

狂风中的少年 提交于 2020-01-05 07:37:12

问题


I was trying to get CMT working with JPA EntityManagers and EJBs, but came up with the error below. (stack trance truncated):

Caused by: java.lang.RuntimeException: **Could not resolve @EJB reference: [EJB Reference: beanInterface 'com.mydomain.beans.TestBean2', beanName 'testBean2', mappedName 'null', lookupName 'null',** owning unit 'AbstractVFSDeploymentContext@2008455195{vfs:///Users/willtardy/Documents/workspace/.metadata/.plugins/org.jboss.ide.eclipse.as.core/JBoss_6.0_Runtime_Server1300532851414/deploy/mydomainWeb.war}']
for environment entry: env/com.mydomain.action.SearchAction/testBean in unit AbstractVFSDeploymentContext@2008455195{vfs:///Users/willtardy/Documents/workspace/.metadata/.plugins/org.jboss.ide.eclipse.as.core/JBoss_6.0_Runtime_Server1300532851414/deploy/mydomainWeb.war}

My classes:

Servlet that access the Session Bean:

public class SearchActionExample extends Action {
    @EJB
    private static TestBeanServiceInterface testBean;

    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        testBean.addSource("TEST SOURCE NAME", 88, 99);
        Service service = testBean.findService("HBA", "MEL");

        return mapping.findForward("success");
    }
}

Remote interface:

@Remote
public interface TestBeanServiceInterface {
    // Source is my own custom entity
    void addSource(String sourceName, int newthreadsleeptime, int maxactivehttpclients);

    // Service is my own Custom entity    
    Service findService(String departureAirportCode, String arrivalAirportCode);
}

Stateless Session Bean definition:

@Stateless
public class TestBeanService implements TestBeanServiceInterface {

    @PersistenceContext(unitName="mydomainJPA")
    private EntityManager em;

    public void addSource(String sourceName, int newthreadsleeptime, int maxactivehttpclients) {
        Source source = new Source();
        source.setName(sourceName);
        source.setNewThreadSleepTime(newthreadsleeptime);
        source.setMaxActiveHttpClients(maxactivehttpclients);
        em.persist(source);
    }
    public Service findService(String departureAirportCode, String arrivalAirportCode) {
        String queryString = "from Service where departureairportcode = '" + departureAirportCode + "' and arrivalairportcode = '" + arrivalAirportCode + "'";
        Service service = (Service)em.createQuery(queryString).getSingleResult();
        return service;
    }
}

file persistnce.xml:

<?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="mydomainJPA" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:/MySqlDS</jta-data-source>
    <class>com.mydomain.entities.Service</class>
<class>com.mydomain.entities.Source</class>
    <properties>
        <property name="hibernate.query.factory_class" value="org.hibernate.hql.classic.ClassicQueryTranslatorFactory"/>
        <property name="hibernate.archive.autodetection" value="class"/>
        <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
        <property name="hibernate.current_session_context_class" value="jta"/>   
    </properties>   
</persistence-unit>

When it says "cannot resolve reference", where else can I define the beans? ejb-jar.xml isn't needed with EJB3. Is there some other config file that I'm missing?


UPDATE:

  • I have updated the code segments above so that the bean is created as the interface type instead, as per the answer below.

  • Do the EJBs need to be defined or mapped in web.xml?

  • Assuming that a reference is required in web.xml, I have added an EJB ref to web.xml (see below), but now I'm receiving a new error (see below)

lines added to web.xml:

<ejb-ref>
    <ejb-ref-name>ejb/TestBeanEJBname</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <home>com.mydomain.action.TestBeanService</home>
    <remote>com.mydomain.action.TestBeanServiceInterface</remote>
 </ejb-ref>

new error message now being received:

12:11:00,980 ERROR [org.jboss.kernel.plugins.dependency.AbstractKernelController] Error installing to PostClassLoader: name=vfs:///Users/willtardy/Documents/workspace/.metadata/.plugins/org.jboss.ide.eclipse.as.core/JBoss_6.0_Runtime_Server1300532851414/deploy/purejetWeb.war state=ClassLoader mode=Manual requiredState=PostClassLoader: org.jboss.deployers.spi.DeploymentException: java.lang.IllegalStateException: Failed to find ContainerDependencyMetaData for interface: au.com.purejet.action.TestBeanServiceInterface

Caused by: java.lang.IllegalStateException: Failed to find ContainerDependencyMetaData for interface: com.mydomain.action.TestBeanServiceInterface
at org.jboss.deployment.MappedReferenceMetaDataResolverDeployer.resolveEjbInterface(MappedReferenceMetaDataResolverDeployer.java:1255) [:6.0.0.Final]
at org.jboss.deployment.MappedReferenceMetaDataResolverDeployer.resolveEjbRefs(MappedReferenceMetaDataResolverDeployer.java:1099) [:6.0.0.Final]
at org.jboss.deployment.MappedReferenceMetaDataResolverDeployer.resolve(MappedReferenceMetaDataResolverDeployer.java:807) [:6.0.0.Final]
at org.jboss.deployment.MappedReferenceMetaDataResolverDeployer.internalDeploy(MappedReferenceMetaDataResolverDeployer.java:181) [:6.0.0.Final]
... 39 more

Update:

"Local" interface works just fine (i.e. doesn't have to be Remote)

I got it to work by deploying within an Enterprise Application Project within Eclipse. No references to beans are required within web.xml, ejb-jar.xml, or application.xml.

Contents of application.xml within EAR being deployed to Jboss:

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd" id="Application_ID" version="6">
<display-name>myprojects</display-name>
<module>
<web>
<web-uri>myproject.war</web-uri>
<context-root>myproject</context-root>
</web>
</module>
<module>
<ejb>myprojectsEJB.jar</ejb>
</module>
</application>

SessionBean class:

@Stateless
@Local(SessionBeanLocal.class)

public class SessionBean implements SessionBeanLocal {

@PersistenceContext(unitName="JPAtestProjectPersistenceUnit")
private EntityManager em;

Interface class:

@Local
public interface SessionBeanLocal {

TestTiger addTestTiger(String testTigerName);

MOST IMPORTANT change that got things working: inside the class that holds the session been local variable, a setting was required for the container (JBoss AS) to create the bean:

@EJB()
private TestBean3Local beanVariable;

public void setBeanVariable(TestBean3Local beanVariable) {
    System.out.println("=====\n\nSET BEAN VARIABE SETTER WAS CALLED. (BY CONTAINER?)  \n\n=======");
    this.beanVariable = beanVariable;
}

回答1:


You need to inject the remote interface and not the Bean

public class SearchActionExample extends Action {
    @EJB
    private static TestBean2Remote testBean;



回答2:


public class SearchActionExample extends Action {
    @EJB
    private static TestBeanServiceInterface testBean;

Don't do injections into static field, injections are instance members and happen when object is created, whereas static field is a class member. This is most probably the cause for exception.




回答3:


I have obtained a working solution:

@Local interface works just fine (i.e. doesn't have to be Remote)

No references to beans are required within web.xml, ejb-jar.xml, application.xml, or any jboss config file.

I got it to work by deploying within an "Enterprise Application Project" (EAP) within Eclipse. This project contains "Deployment Assembly" that contains the .jar containing JPA Entity Classes, and another .jar that contains other business-logic classes. The EAP has those two projects PLUS the EJB project and the "Dynamic Web Project" (creates a .war) for a total of 4 projects on it's build path. Jboss AS tool within Eclipse publishes/deploys the EAP to the Jboss server. Contents of application.xml within EAP being deployed to Jboss:

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd" id="Application_ID" version="6">
    <display-name>myprojects</display-name>
    <module>
        <web>
            <web-uri>myproject.war</web-uri>
            <context-root>myproject</context-root>
        </web>
    </module>
    <module>
        <ejb>myprojectsEJB.jar</ejb>
    </module>
</application>

Local Interface class:

package com.myproject.beans;
import javax.ejb.Local;

import com.myproject.entities.Lion;

@Local
public interface SessionBeanLocal {
Lion addLion(String lionName);
}

SessionBean class:

package com.myproject.beans;

import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.myproject.Lion;

@Stateless
@Local(SessionBeanLocal.class)
public class SessionBean implements SessionBeanLocal {

@PersistenceContext(unitName="PersistenceUnitNameInPersistenceXML")
private EntityManager em;

public Lion addLion(String lionName) {
    Lion lion = new Lion(lionName);
    em.persist(lion);
}

MOST IMPORTANT change that got things working: inside the class that holds the session been variable (e.g. inside a Struts action servlet, but could be any servlet), a setter method was required for the container (JBoss AS) to create the bean:

@EJB()
private SessionBeanLocal bean;

public void setBean(SessionBeanLocal bean) {
    System.out.println("setBean setter was called by container (e.g. Jboss)");
    this.bean = bean;
}

public exampleStrutsServletMethod(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
    PrintWriter out = response.getWriter();

    Lion lion = bean.addLion("Simba");  // this will persist the Lion within the persistence-context (and auto-generate an Id), and the container will manage when it's flushed to the database

    out.print("<html>LION ID = " + lion.getLionId() + "<html>");
}

file persistnce.xml:

<?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="PersistenceUnitNameInPersistenceXML" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:/MySqlDS</jta-data-source>
    <properties>
    </properties>   
</persistence-unit>

mysql-dx.xml (in directory jboss-server-dir/server/default/deploy):

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
    <local-tx-datasource>
        <jndi-name>MySqlDS</jndi-name>
        <connection-url>jdbc:mysql://localhost:3306/myProjectDatabase</connection-url>
        <driver-class>com.mysql.jdbc.Driver</driver-class>
        <user-name>username</user-name>
        <password>mypassword</password>
        <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
        <metadata>
            <type-mapping>mySQL</type-mapping>
        </metadata>
    </local-tx-datasource>
</datasources>

NOTE: Classes do not need to be defined in persistence.xml (via "< class >") if "Persistence Class Management" is set to "Discover annotated classes automatically" in "Java Persistence" project property panel for the Eclipse JPA project (i.e. the project that containers your JPA 2.0 Entity classes and persistence.xml)

NOTE: This solution is based on: EJB3.1, Eclipse Helios SR2, Hibernate 3.6, JPA 2.0, JBoss 6, MySQL 5.5.10

NOTE: Regarding "Container Managed Transactions" (CMT). The Hibernate manual references them, and indicates that you need to set persistence.xml properties such as "hibernate.transaction.factory_class" to value of: "org.hibernate.transaction.CMTTransactionFactory". This is not the case if you are using JPA EntityManager instead of native hibernate. I didn't required any such custom CMT properties in persistence.xml. This is where Hibernate gets confusing, between the two ways to implement it (i.e. SessionFactory vs EntityManager). Please feel free to comment more on this part of my solution as I'm still just wrapping my head around it! Will



来源:https://stackoverflow.com/questions/5387022/how-to-get-container-managed-transactions-cmt-working-with-ejb-3-1-hibernate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!