【问题标题】:How a JEE6 enterprise application that offers REST web services should be organized?应该如何组织提供 REST Web 服务的 JEE6 企业应用程序?
【发布时间】:2012-04-05 13:55:19
【问题描述】:

从一个月前开始,我一直在努力学习 Restful Web 服务。 现在我确实练习了语法并且理解了这些概念,我决定制作一个非常简单的企业应用程序,其中包括 EJB、JPA 和 REST。 我正在努力了解组织这种系统的最佳方式是什么。如果有该领域经验的人能给我一些关于什么是最佳实践以及如何解决我当前问题的提示,我将不胜感激。

请让我给你看这张图片。抱歉,我无法获得更好的分辨率(使用 Ctrl+鼠标向上滚动进行缩放):

如您所见,这是一个非常简单的企业应用程序,有 2 个模块。

此应用程序不使用 CDI(我想在没有 CDI 帮助的情况下实现我的目标并且)

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

我的问题如下:

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

【问题讨论】:

    标签: java design-patterns jakarta-ee rest jax-rs


    【解决方案1】:

    下面是一个 JAX-RS 服务示例,它使用 JPA 进行持久性和 JAXB 进行消息传递,实现为会话 bean,可能看起来像这样。 (注意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 映射,以避免客户端上的类路径依赖。

    更多信息


    更新

    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>
    

    更多信息

    【讨论】:

    • 我喜欢这个想法,听起来很有趣,因为您使用的是混合方法。如果我理解得很好,您说我应该将实体的映射(当前在 EJB 模块中)添加到 web.xml 中,而不是使用注释,对吗?但是 EJB 模块呢?您能否用一个如何进行这些映射以及在哪个文件中的示例来更新您的问题?我有点困惑。
    • @sfrj - JPA 元数据将放入从persistence.xml 文件引用的文件中。
    • Tnx 这个答案非常有用。
    • @sfrj - 我已更新我的答案以包含一个示例 orm.xml 文件。
    【解决方案2】:

    将您要公开的域与模型域分开是个好主意,所以我会保持这样,实体和生成的类是解耦的。解决此 ClassCastException 的一种直接方法是在 Web 模块中将 jaxb 类映射到作为输入的实体,反之亦然作为输出。您可以手动完成,也可以使用不同的库来解决此映射问题。即推土机(http://dozer.sourceforge.net/)

    【讨论】:

      【解决方案3】:

      我认为您在这里错过了一个额外的组件 - 应用程序服务。 此外,您的CRUDFacade 只是一个CredentialRepository

      对于我上面提到的组件,有两种可能的解决方案:

      1. 如果您有明确分离的应用程序服务,那么您的SampleService 只是将此类服务暴露给外界的众多可能方法之一。您可以拥有SampleRestResourceSampleYamlResource ;) 或任何其他您选择的。在这种情况下,我建议创建CredentialDTO 并使用@XmlRootElement 注释其字段。你的应用服务将这个 DTO 返还给外界,SampleRestResource(以前的SampleService)只是转发它。
      2. 如果您不想引入额外的构建块,如 DTO(可能与汇编程序一起)和单独的 SampleRestResource 类,您可以在您的应用程序服务方法上添加注释 - 我不确定这是否适用于所有不过是 JAXWS 实现。

      按照我描述的方法,通常我将剩余资源与应用程序服务放在一个模块中。您的 EJB 模块(目前将我视为纯域模块)只是它的依赖项之一。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-12
        相关资源
        最近更新 更多