4

从一个月前开始,我一直在努力学习宁静的网络服务。现在我确实练习了语法并且理解了这些概念,我决定制作一个非常简单的企业应用程序,其中包括 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 服务的企业应用程序的组织方式通常是什么?
4

3 回答 3

5

下面是一个作为会话 bean 实现的 JAX-RS 服务的示例,它使用 JPA 进行持久性,使用 JAXB 进行消息传递可能看起来像这样。(注意 anEntityManager被注入到会话 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>

了解更多信息

于 2012-04-05T14:53:48.323 回答
0

I think that you miss one additional component here - application service. Additionally, your CRUDFacade is just a CredentialRepository.

With components I mentioned above, there are two possible solutions:

  1. If you had an application service clearly separated, yours SampleService would be just one of many possible methods of exposing such service to the outside world. You can have SampleRestResource, SampleYamlResource ;) or any other of your choice. In such case, I'd recommend to create CredentialDTO and annotate its fields with @XmlRootElement. Yours application service returns this DTO to the outside world and SampleRestResource (formerly SampleService) just forwards it.
  2. If you don't want to introduce additional building blocks like DTO (maybe along with assembler) and the separate SampleRestResource class, you can add annotations on your application service methods - I'm not sure if it's possible with all JAXWS implementations, though.

Following approach I described, usually I put rest resources along with application services in one module. Your EJB module (which currently looks for me as a pure domain module) is just one of its dependencies.

于 2012-04-05T14:39:15.457 回答
0

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

于 2012-04-05T14:42:29.013 回答