Java EE 6:如何在应用程序客户端上添加Web模块

时间:2011-06-17 14:39:39

标签: jsf jpa netbeans java-ee ejb

Technology(Java EE 6 with Glassfish 3.1, Netbeans 7.0)

我有一个通过JPA访问数据库的应用程序客户端。不涉及EJB。现在我需要为此应用程序客户端添加Web界面。所以我会选择使用JSF 2.x。我对这里的设计有些担心,我希望社区能帮助我。 So thanks to BalusC, I am able to use JPA in a stand alone client application通过在persistence.xml中指定transaction-type=RESOURCE_LOCAL。以下是演示:

编辑以下代码已根据BalusC建议进行了编辑

以下是我的 App Client main

public static void main(String[] args) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("CoreInPU");
    EntityManager em = emf.createEntityManager();
    EntityDAO entityDAOClient = new EntityDAOClient(em);
    Main pgm = new Main();
    try {
        process(entityDAOClient);
    } catch (Exception e) {
        logger.fatal("", e);
    }finally{
        em.close();
        emf.close();
    }
}

public void process(EntityDAO entityDAO){
    validatePDF(List<pdfFiles>);
    processPDF(List<pdfFiles>, entityDAO);
    createPrintJob(List<pdfFiles>, entityDAO);
}

public void processPDF(List<pdfFiles>, EntityDAO entityDAO){
    for(File file : pdfFiles){
        entityDAO.create(file);
    }
}

以下是 App Client

中的DAO接口类
public interface EntityDAO {
    public <T> T create(T t);
    public <T> T find(Class<T> type, Object id);
    public List findWithNamedQuery(String queryName);
    public List findWithNamedQuery(String queryName, int resultLimit);

}

以下是 App Client DAO

public class EntityDAOClient implements EntityDAO {

    private EntityManager em;

    private static Logger logger = Logger.getLogger(EntityDAOClient.class);

    public EntityDAOClient(EntityManager em) {
        this.em = em;
    }

    @Override
    public <T> T create(T t){
        em.getTransaction().begin();
        em.persist(t);
        em.getTransaction().commit();
        return t;
    }

    @Override
    public <T> T find(Class<T> type, Object id){
        em.getTransaction().begin();
        T t = em.find(type, id);
        em.getTransaction().commit();
        return t;
    }
    ...
}

这是persistence.xml

<persistence-unit name="CoreInPU" transaction-type="RESOURCE_LOCAL">
   <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
   <class>com.wf.docsys.core.entity.Acknowledgement</class>
   <class>com.wf.docsys.core.entity.PackageLog</class>
   <properties>
      <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/core"/>
      <property name="javax.persistence.jdbc.password" value="root"/>
      <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
      <property name="javax.persistence.jdbc.user" value="xxxx"/>
      <property name="eclipselink.ddl-generation" value="create-tables"/>
   </properties>
</persistence-unit>

现在我需要在此基础上添加一个Web模块。我知道我需要JTA事务类型,因此我创建了一个EAR项目调用foo,其中包含foo_ejbfoo_war。所以我的EJB看起来像这样。

@Stateless
@LocalBean
public class CoreEJB implements EntityDAO{

    @PersistenceContext(unitName = "CoreInWeb-ejbPU")
    private EntityManager em;

    //@Override
    public <T> T create(T t) {
        em.persist(t);
        return t;
    }

    //@Override
    public <T> T find(Class<T> type, Object id) {
        return em.find(type, id);
    } 

    ...
}

请注意,CoreInWeb-ejbPU是具有JTA事务类型的新persistence.xml单元名称。我还将我的app客户端jar文件添加到foo_ejb包中。 当我部署时,我收到了此消息Invalid ejb jar [foo-ejb.jar]: it contains zero ejb.这是因为@Stateless public class CoreEJB implements EntityDAO。如果我取出implements EntityDAO然后它是部署,但我需要EJB实现EntityDAO,以便在我的托管bean中我可以做到这一点

@ManagedBean
@RequestScoped
public class Bean {

   @EJB
   private CoreEJB coreEJB;


   public Bean() {

   }

   public void runAppClientMainProcess() { 
       //The web interface can also kick off the same process as the app client
       process(coreEJB);
   }

   // ...
}

如何正确执行此操作?请帮助

我知道我可能会在这里问太多,但如果您可以根据我上面的结构,告诉我如何添加一个Web模块,我将非常感激。有些代码会很棒。 我还在学习,所以如果我的设计有缺陷,请随意撕开它,如果我说服有更好的方法来实现这一点,我会重新设计一切。最重要的是,有一组业务逻辑,我想通过application clientweb interface访问这些逻辑。就像glassfishv3有web界面和管理控制台一样

4 个答案:

答案 0 :(得分:6)

  

你能告诉我在哪里可以使用transaction-type =“JTA”放置/创建另一个persistence.xml吗?在我的Web模块中,或者创建一个单独的EJB?

没有什么不同。它仍然需要进入/META-INF/persistence.xml文件。如果您的项目代表WAR,请将其放在Web项目中。或者,如果它代表EAR,则将其放在EJB项目中。


  

在JSF中,我使用Managed Bean,我的托管bean如何从EntityUtil调用方法?我问这个问题,因为通常我在某处有一个EJB,所以如果我希望我的Managed bean访问它,我会使用@EJB注释注入EJB。由于EntityUtil未注释为EJB,我如何从Managed Bean访问它?

理论中,您可以创建一个新的EJB,它组成/委托EntityUtil,然后将此EJB注入托管bean。

@Stateless
public class SomeEJB {

    @PersistenceUnit(unitName="someWebPU")
    private EntityManagerFactory emf;

    @PostConstruct
    public void init() {
        EntityUtil.newInstance(emf);
    }

    public void create(Some some) {
        EntityUtil.create(some);
    }

    // ...
}

但是......您的EntityUtil 不是线程安全EntityUtil中的所有内容都是static。 Java EE Web应用程序是一个严重多线程的环境。多个并发用户同时使用相同的代码库。所有公开暴露的静态变量都在所有用户之间共享。当用户在close()上调用EntityUtil时,它会影响所有当前webapp用户。因此,忙于交易的人将获得例外。

无论是客户端还是Web应用程序,您都应该在应用程序启动时仅创建EntityManagerFactory一次,在整个应用程序的生命周期内重用相同的实例,并仅在应用程序关闭时关闭它。每个事务或会话只需创建一次EntityManager,并在事务或会话结束时关闭EntityUtil。在具有JTA事务类型的Java EE 6中,事务由容器完全管理。但是在具有资源本地事务类型的客户端应用程序中,您需要自己管理事务。

我建议您将public interface SomeDAO { public void save(Some some); // ... } 重写为类似DAO的界面,该界面旨在为客户端应用和网络应用提供不同的实现。

public class SomeDAOClient implements SomeDAO {

    private EntityManager em;

    public SomeDAO(EntityManager em) { 
        this.em = em;
    }

    public void save(Some some) {
        em.getTransaction().begin();
        em.persist(some);
        em.getTransaction().commit();
    }

    // ...
}

以下是如何为客户端应用程序实现它:

public static void main(String[] args) throws Exception {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("someClientPU");
    EntityManager em = emf.createEntityManager();
    SomeDAO someDAO = new SomeDAOClient(em);

    try {
        Some some = new Some();
        some.setFoo("foo");
        someDAO.save(some);
    } finally {
        em.close();
        emf.close();
    }
}

并按如下方式使用:

@Stateless
public class SomeDAOEJB implements SomeDAO {

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

    public void save(Some some) {
        em.persist(some);
    }

    // ...
}

以下是如何为Web应用程序实现它:

@ManagedBean
@RequestScoped
public class Bean {

    @EJB
    private SomeDAO someDAO;
    private Some some;

    public Bean() {
        some = new Some();
    }

    public void save() {
        someDAO.save(some);
    }

    // ...
}

并按如下方式使用

{{1}}

答案 1 :(得分:2)

@Harry 您正在讨论三个层次1)演示文稿2)业务3)持久性

案例1 - 应用程序客户端 - 您已使用主类进行演示,validatePDF,processPDF,createPrintJob是您的业务层3)EntityDAOClient和JPA是您的持久层

案例2 - Web模块 - 您已经使用(想要使用)jsf进行演示,使用CoreEJB进行持久化 - 我不清楚您打算放置业务逻辑的位置,因为现在生病是另一个ejb类。

这种方法的主要问题是“为业务逻辑层使用两种不同的方法”案例1 - 普通的Java Case 2 Ejb。

当你使用普通的java时,你失去了依赖注入,tx管理,并发管理,拦截器等的奢侈。

当您使用EJB时​​ - 您失去了与应用程序客户端共享代码的灵活性。

解决方案是使用CDI。使用CDI,您可以集成两个用例。它就像三层之间的桥梁。将依赖注入,拦截器,装饰器的所有功能提供给应用程序客户端。

实际上,应用程序客户端和JavaEE中的业务层和持久层代码都不会发生任何变化。

我在CDI编程模型中提到你不需要使用@managed。所有的pojo都是CDI bean。

在CDI中,您无需编写主类。开始编写业务逻辑,如下所示


//This class works as is in both javaEE and client. 
//There is no need of managedbean in CDI. 
//This class can be accessed in jsf using EL #businessProcessor
@Singleton  @Named
public class BusinessProcessor
{
private @Inject EntityDAO entityDAO; // CDI will inject an implementation of EntityDao. 
//In our case there is only one impl for client as well as JavaEE 

// CDI will invoke below method on startup. Not used in JavaEE. // In JavaEE JSF will execute process() directly using Expression Language public void init(@Observes ContainerInitialized event, @Parameters List parameters){ process(entityDAO); }

public void process(@Inject EntityDAO entityDAO){     validatePDF(List);     processPDF(List, entityDAO);     createPrintJob(List, entityDAO); }

public void processPDF(List, EntityDAO entityDAO){     for(File file : pdfFiles){         entityDAO.create(file);     } } }


// this class is same for both JavaEE and Client
public class EntityDAOClient implements EntityDAO {

private @Inject EntityManager em;

    private static Logger logger = Logger.getLogger(EntityDAOClient.class);         @Override     public T create(T t){         em.getTransaction().begin();         em.persist(t);         em.getTransaction().commit();         return t;     }

    @Override     public T find(Class type, Object id){         em.getTransaction().begin();         T t = em.find(type, id);         em.getTransaction().commit();         return t;     }     ... }

运行应用客户端


java org.jboss.weld.environment.se.StartMain
如需更多肉类,请查看www.seamframework.org

答案 2 :(得分:0)

您可以使用netbeans做一些小项目示例,看看它是如何工作的.. NetBeans为您生成所有这些“悲伤”的东西,因此,您可以自己研究它,并自己完成。

答案 3 :(得分:0)

我想为我迟到的回复道歉。我落后于另一个项目的时间表,所以我没有时间对BalusCkiran答案做出正确的回复。

kiran问我这个:Case 2 - Web Module - You have used(want to use) jsf for presentation , CoreEJB for persistence -- i am not clear where you intend to put business logic, for now ill take is as another ejb class.

答案:您好kiran,最重要的是,我希望在应用客户端和Web模块端访问同一组逻辑。喜欢glassfish应用服务器。您有一个访问相同资源的命令行版本和Web模块版本。

如果我只是使用应用程序客户端,

BalusC建议使用RESOURCE_LOCAL工作得很好。但是,一旦我在它上面添加了Web模块,即使BalusC确实尝试与我合作,我也无法让它继续工作。你们可以阅读BalusC的回复以及我对上述想法的实现(原帖中的代码是我对BalusC想法的实现)。如果我将App Client添加到Java EE项目(EAR)类路径中,当我尝试从App Client访问逻辑时它不会收到编译错误,但是当我运行Web模块时,它会生成Exception,表示它没有看到我尝试从App Client访问的类/方法。因此,我决定将所有逻辑移植到EJB上,使我的App Client非常薄。逻辑将通过javax.ejb.Remote接口向App Client公开,并通过javax.ejb.Local接口公开给Web模块。以下是我对这个想法的总体布局。

这是我的thin应用客户端(主要)

public class Main {

    @EJB
    private static CoreMainEJBRemote coreEJBRemote;

    private static Logger logger = Logger.getLogger(Main.class);

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
         coreEJBRemote.process(args[0]);
    }
}

所以在我的EJB端,包含EJB和Web模块的Java EE项目(EAR)中的哪个包,我有

@Stateless
public class CoreMainEJB implements CoreMainEJBRemote, CoreMainEJBLocal {

    @EJB
    private PackageProcessor packageProcessor;

    @Override
    public void process(String configFileName) {
         ...
         //Process config File
         packageProcessor.validatePDF();
         packageProcessor.processPDF();
         ...
    }
}

请注意,由于现在EJB内部的所有逻辑,我可以使用JTA事务类型,这是容器管理的。我喜欢这种方式比自己管理它更好。

有些人建议通过RESTful公开业务逻辑。由于我不太了解RESTful,我现在将继续使用这个实现。感谢您的所有帮助BalusCkiran