如何实现此REST API并保持DRY?

时间:2013-03-07 03:13:32

标签: java spring jersey jax-rs spring-data

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

API将支持数十个表上的CRUD操作,伴随着由Spring Data存储库支持的JPA实体和DAO。 DAO接口和相关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。它可能看起来像这样:

@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提供资源类实例时,它将如何知道要注入哪个DAO?

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

2 个答案:

答案 0 :(得分:2)

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

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

因此我通常创建一个AbstractPersistedObjectDAO类并实现persist(),merge()和delete()并将findOne()抽象为子类,以避免在我需要执行更多操作时转换代码的需要比CRUD。但我通常只是在端点上吃掉样板的成本,这样我就可以使用Enunciate生成REST文档,如果需要的话,它会给我一个类来在将来采用其他方法。

答案 1 :(得分:2)

不确定您是否以任何方式绑定到JAX-RS,但Spring Data系列项目附带Spring Data REST模块,该模块以超媒体驱动的方式自动公开由Spring Data存储库管理的实体。它基于Spring MVC。

因此,您基本上可以免费获取实体上的CRUD操作,透明地公开查询方法,以及根据您的需要调整和调整所有内容的能力。

以下是一些您可能想要浏览的有用链接,以获取更多信息: