2012-04-05 73 views
4

自从一个月前我真的很难学习宁静的web服务。 现在我已经练习了语法,并且理解了这些概念,我决定创建一个包含EJB,JPA和REST的非常简单的企业应用程序。 我正在努力尝试了解组织这种系统的最佳方式。如果有人在该领域有经验可以给我一些关于什么是最佳实践的提示,那么我很感激,我该如何解决我目前的问题。应该如何组织提供REST Web服务的JEE6企业应用程序?

让我告诉你这张图片。对不起,我不能得到更好的分辨率(使用Ctrl +鼠标向上滚动放大):

enter image description here

正如你可以看到这是一个非常简单的应用程序的企业,有2个模块。

此应用程序不使用CDI(我要实现我的目标没有CDI的帮助和)

当某些客户端(任何可互操作的客户端)发送一个@GET一些参数的REST服务应该通过这些参数到EJB模块,它将在数据库中搜索并发回相应的数据。最后,服务将在JAXB的帮助下自动编组,并将.XML发送回客户端。

我的问题有以下几点:

  • 我得到一个ClassCastException,因为在该实体EJB模块中不与JAXB类兼容的WebModule(即使它们的变量都相同)
  • 我不知道应该怎么组织,所以前端可以编组和解组这些实体。
  • 也许实体类可能在前端与JAXB映射相结合?如果那样的话,EJB模块将不再需要。但事情是,我想要EJB模块,因为我经常在那里做我的CRUD操作。
  • 如何将EJB公开为REST Web服务(制作混合)?你认为这是一个好主意吗?它如何帮助我?
  • 同样,如果我在Web模块中创建JAXRS + EJB的混合,我将必须在前端创建我的JPA实体,这是我以前从未做过的事情。你认为这是一个好习惯吗?
  • 你有什么建议?通常使用REST Web服务的企业应用程序的组织方式是什么?

回答

5

下面是作为使用JPA的持久性和JAXB用于消息可能看起来像一个会话bean实现的JAX-RS服务的一个例子。 (注意:一个EntityManager注入到会话bean,你为什么要避免这种行为的?):

package org.example; 

import java.util.List; 

import javax.ejb.*; 
import javax.persistence.*; 
import javax.ws.rs.*; 
import javax.ws.rs.core.MediaType; 

@Stateless 
@LocalBean 
@Path("/customers") 
public class CustomerService { 

    @PersistenceContext(unitName="CustomerService", 
         type=PersistenceContextType.TRANSACTION) 
    EntityManager entityManager; 

    @POST 
    @Consumes(MediaType.APPLICATION_XML) 
    public void create(Customer customer) { 
     entityManager.persist(customer); 
    } 

    @GET 
    @Produces(MediaType.APPLICATION_XML) 
    @Path("{id}") 
    public Customer read(@PathParam("id") long id) { 
     return entityManager.find(Customer.class, id); 
    } 

    @PUT 
    @Consumes(MediaType.APPLICATION_XML) 
    public void update(Customer customer) { 
     entityManager.merge(customer); 
    } 

    @DELETE 
    @Path("{id}") 
    public void delete(@PathParam("id") long id) { 
     Customer customer = read(id); 
     if(null != customer) { 
      entityManager.remove(customer); 
     } 
    } 

    @GET 
    @Produces(MediaType.APPLICATION_XML) 
    @Path("findCustomersByCity/{city}") 
    public List<Customer> findCustomersByCity(@PathParam("city") String city) { 
     Query query = entityManager.createNamedQuery("findCustomersByCity"); 
     query.setParameter("city", city); 
     return query.getResultList(); 
    } 

} 

如果你想使用的服务器和客户端相同的域对象。然后,我会通过XML提供JPA映射而不是注释,以避免客户端上的类路径依赖。

更多信息


UPDATE

META-INF/persistence.xml中

persistence.xml文件是在其中指定链接到包含JPA映射的XML文件:

<persistence-unit name="CustomerService" transaction-type="JTA"> 
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 
    <jta-data-source>CustomerService</jta-data-source> 
    <mapping-file>META-INF/orm.xml</mapping-file> 
</persistence-unit> 

META-INF/orm.xml

在此文件中,您将添加JPA元数据的XML表示。

<?xml version="1.0" encoding="UTF-8"?> 
<entity-mappings 
    version="2.0" 
    xmlns="http://java.sun.com/xml/ns/persistence/orm" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"> 
    <entity class="org.example.Customer"> 
     <named-query name="findCustomersByCity"> 
      <query>SELECT c FROM Customer c WHERE c.address.city = :city</query> 
     </named-query> 
     <attributes> 
      <id name="id"/> 
      <basic name="firstName"> 
       <column name="FIRST_NAME"/> 
      </basic> 
      <basic name="lastName"> 
       <column name="LAST_NAME"/> 
      </basic> 
      <one-to-many name="phoneNumbers" mapped-by="customer"> 
       <cascade> 
        <cascade-all/> 
       </cascade> 
      </one-to-many> 
      <one-to-one name="address" mapped-by="customer"> 
       <cascade> 
        <cascade-all/> 
       </cascade> 
      </one-to-one> 
     </attributes> 
    </entity> 
    <entity class="org.example.Address"> 
     <attributes> 
      <id name="id"/> 
      <one-to-one name="customer"> 
       <primary-key-join-column/> 
      </one-to-one> 
     </attributes> 
    </entity> 
    <entity class="org.example.PhoneNumber"> 
     <table name="PHONE_NUMBER"/> 
     <attributes> 
      <id name="id"/> 
      <many-to-one name="customer"> 
       <join-column name="ID_CUSTOMER"/> 
      </many-to-one> 
     </attributes> 
    </entity> 
</entity-mappings> 

更多信息

+0

我喜欢这个主意,因为你使用的是混合方法听起来很有趣。如果我理解的很好,你说我应该将实体(当前在EJB模块中)的映射添加到web.xml中,而不是使用注释,对吧? 但是EJB模块呢?你能用一个如何做这些映射和在哪个文件中的例子来更新你的问题吗?我有点困惑。 – sfrj 2012-04-05 18:58:49

+0

@sfrj - JPA元数据将放入从'persistence.xml'文件引用的文件中。 – 2012-04-05 19:13:35

+1

Tnx这个答案是非常有用的。 – sfrj 2012-04-09 09:54:41

0

我想你错过了一个额外的组件 - 应用程序服务。 此外,您的CRUDFacade只是一个CredentialRepository

用我上面提到的成分,有两种可能的解决方案:

  1. 如果你有一个应用服务明显地分开,你SampleService将只是暴露这样的服务对外部世界的许多可能的方法之一。您可以有SampleRestResourceSampleYamlResource;)或您选择的任何其他选项。在这种情况下,我建议创建CredentialDTO并用@XmlRootElement注释其字段。您的应用服务将此DTO返回给外部世界,并且SampleRestResource(原名称为SampleService)只是将其转发给您。
  2. 如果你不想引入额外的积木一样DTO(也许连同汇编)和单独SampleRestResource类,你可以在你的应用服务方法添加注释 - 我不知道是否有可能与所有JAXWS的实现,但是。

以下描述的方法通常会将其余资源和应用程序服务一起放在一个模块中。您的EJB模块(目前将我看作纯粹的域模块)只是它的一个依赖项。

0

这是分离,你是从你的模型域暴露域是一个好主意,所以我会保持这个样子,与实体和生成的类分开。解决这个ClassCastException的直接方法是将web模块中的jaxb类映射到实体作为输入,反之作为输出。您可以手动完成,或者有不同的库来解决这个映射问题。即推土机(http://dozer.sourceforge.net/)

相关问题