java - 我怎样才能实现这个 REST API 并保持 DRY?

标签 java spring jersey jax-rs spring-data

我正在构建一个 REST API 来对数据库执行 CRUD 操作。我暂定的堆栈是 Jersey、Spring、Spring Data、JPA 和 Hibernate。我还使用 jersey-spring 来提供资源类的实例,以便 Spring 可以 Autowiring 它们。

该 API 将支持对数十个表进行 CRUD 操作,并伴随有由 Spring Data 存储库支持的 JPA 实体和 DAO。 DAO 接口(interface)系列和相关的 DTO 看起来像这样:

public interface CrudService<T extends PersistedObject> { /* ... */  }
public interface PersonService extends CrudService<Person> { /* ... */  }

public class PersistedObject { /* ... */ }
public class Person extends PersistedObject { /* ... */ }

这是 JAX-RS 资源类的简化版本:

@Component
@Path("/people")
public class PersonResource {

    @Autowired
    private PersonService personService;

    @Path("/{id}")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Person get(@PathParam("id") String id) {
        return personService.findOne(Long.valueOf(id));
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response post(Person person) {
        personService.save(person);
        return Response.created().build();
    }
}

问题是几十个资源类的其余部分看起来几乎相同,唯一的区别是它们在不同的 PersistedObject 子类及其相应的 DAO 上运行。我想通过拥有一个可以支持所有实体类型的 CRUD 操作的资源类来保持 DRY,大概是通过多态和 DAO 的巧妙注入(inject)。它可能看起来像这样:

@Component
@Path("/{resourceType}")
public class CrudResource {

    @Autowired
    private CrudService crudService;

    @Path("/{id}")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public PersistedObject get(@PathParam("id") String id) {
        return crudService.findOne(Long.valueOf(id));
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response post(PersistedObject entity) {
        crudService.save(entity);
        return Response.created().build();
    }
}

我需要解决的问题:

  • 如果资源方法接受并返回 PersistedObject,Jersey/Jackson 如何知道如何序列化/反序列化?路径参数中的 resourceType 变量指示用户请求的具体类型,但我不知道如何利用它来获得任何优势。
  • 当 Spring 提供资源类实例时,它如何知道要注入(inject)哪个 DAO?

总的来说,我不确定自己是否走在正确的道路上。是否有可能以通用方式实现它?

最佳答案

我自己也遇到过几次这个问题。您可以创建一个通用端点和一个 PersistedObjectDao,它应该都可以正常工作。在 Hibernate 中, session 方法如 persist()、merge() 和 delete() 并不关心它得到什么,只要它是一个托管对象或可以成为一个托管对象(在 merge() 的情况下) .由于您只是通过 id 查找,并且应该在 PersistedObject 类而不是 Person 类中进行管理,因此 DAO 的功能将正常工作。

这种方法的唯一问题是它破坏了 Enunciate 等文档工具,并使资源 URL 需要全局唯一 ID。/xxx/1 和/yyy/1 不能共存。 findOne 方法将为两者返回相同的对象。这意味着您将必须使用 @Inheritance(strategy = InheritanceType.JOINED) 来避免 ID 冲突,并在数据库中的所有持久实体中创建一个全局唯一的 ID 列。

因此,我通常创建一个 AbstractPersistedObjectDAO 类并实现 persist()、merge() 和 delete(),并将 findOne() 抽象为一个子类,以避免在我需要做更多事情时强制转换代码比增删改查。但我通常只承担端点上样板的成本,这样我就可以使用 Enunciate 生成 REST 文档,并且它为我提供了一个类,以便在未来需要时采用其他方法。

关于java - 我怎样才能实现这个 REST API 并保持 DRY?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15262425/

相关文章:

java - Spring中Bean未初始化

java - 在MyBatis for IBM DB2中执行多个INSERT语句

java - 有没有办法附加到@Path 的末尾

java - 我应该如何将我的用户名和密码发送到 Java 后端?

java - 在 Jersey 中为 REST API 处理完整和部分 View

java - 术语 bean 初始化和实例化是否可以互换

java - Android - notifyDataSetChanged() 不工作

Spring Cloud Config Server - 使用 2FA 连接到 Github 帐户

Java LocalConnection 客户端

java - Spring Data MongoDB 使用 DBRef 按嵌套属性(非 ID)查找