【发布时间】:2019-09-09 16:32:52
【问题描述】:
我有一堆表,我需要为其提供标准的 CRUD 接口。每次我必须公开一个新表时,我都会遵循以下模式。
public interface EntityWithId<TDbEntity> extends Serializable {
public TDbEntity entityId();
}
@Entity
public class DbEntityName implements EntityWithId<Long> {
@Id private Long id;
@Override public Long entityId() {return id;}
// other fields follow
}
public class EntityName {
private Long id;
// other fields follow
// create Entity from DbEntity
public EntityName(DbEntityName dbItem) { ... }
// get DbEntity from Entity
public DbEntityName toDb() { ... }
}
@Repository
public interface DbEntityNameRepository extends CrudRepository<DbEntityName, Long> { }
public interface CrudService<TDbEntity extends EntityWithId<ID>, ID> {
CrudRepository<TDbEntity, ID> getCrudRepository();
// provide default implementation of all CRUD operations here like the below one
default TDbEntity save(TDbEntity entity) { return getCrudRepository().save(entity); }
}
public interface DbEntityNameService extends CrudService<DbEntityName, Long> {
}
@Service
public class DbEntityNameServiceImpl implements DbEntityNameService {
@lombok.Getter @Autowired DbEntityNameRepository crudRepository;
}
@RestController
@RequestMapping("/api/v1/dbservice")
public class EntityNameController {
@Autowired DbEntityNameService dbService;
@PostMapping("/{EntityName}") // this should be replaced by the actual name of the entity
public Long save(@RequestBody EntityName msg) {
return dbService.save(msg.toDb()).entityId();
}
// implement other CRUD end points
}
EntityWithId<T> 接口和CrudService<TDbEntity extends EntityWithId<ID>, ID> 只为系统定义一次。它们提供了在访问 repo 时摆脱重复代码的机制。
您会注意到,唯一需要完成的真正代码是添加实体和数据库实体中的字段,以及它们的转换。另外,我需要为每个新表推出一个新的控制器。
问题:我如何构造控制器代码,以便我可以从基本 CRUD 控制器继承功能。
请注意,在我的真实代码中,并非所有实体都用于简单的 CRUD,并且当前结构提供了扩展服务的简便方法
简而言之,我正在寻找一些模式来帮助我提供如下所示的内容,其中我有一个通用的 Base 类,我可以用最少的代码创建一个子类来公开控制器的端点。不用说,以下代码无法按原样提供我正在寻找的功能。
class BaseController<TEntity, TDbEntity, TId> {
CrudService<TDbEntity, TId> dbService;
@GetMapping("/{TEntity}/{id}")
public TEntity getById(@PathVariable TId id) {
return new TEntity(dbService.getById(id));
}
@PostMapping("/{TEntity}")
public Long save(@RequestBody TEntity msg) {
return dbService.save(msg.toDb()).entityId();
}
}
class EntityNameController : BaseController<EntityName, DbEntityName, Long> {
}
也欢迎提供其他建议。我的目的是减少控制器中的重复代码 - 这主要是创建 CRUD 函数,将其与 CRUD 端点相关联并调用底层服务来完成实际工作。
编辑:我知道我可以编写一个自定义注释处理器来生成标准的 CRUD 函数(几乎就像CrudRepository 的工作方式一样),但这不是我想要的方向。
澄清一下,这里的目的是标准功能(如 CRUD)可以在将公开它的基本控制器中一劳永逸地编码,从而释放子控制器来处理其他非标准工作。
【问题讨论】:
-
@PaulPearson 这就是我想要弄清楚的。这样的代码会是什么样子?
dbService将如何被初始化。GetMapping和PostMapping'继承'等将如何? -
Tbh 这听起来你应该使用 Spring Data Rest。基本上,您将让您的存储库公开所有标准端点,如果需要,您可以为非标准端点添加指定的 RepositoryControllers。
-
@MirkoBrandt 感谢您的建议。我正在阅读它,它看起来像我可能正在寻找的东西。尝试查找是否可以 1. 在不破坏已经公开的 API(控制输出 JSON 和 URL 端点)的情况下合并它。2。在控制器中添加新的自定义处理程序/端点(不在 repo 中)。
-
@MirkoBrandt 不幸的是,我不知道如何在当前情况下使用 Spring Data Rest。尽管 Spring Data Rest 提供了一种方法来做我所要求的,但它并不能让我很好地控制 JSON 返回。此外,它不完全支持 api 模型和 db 模型的概念(我们可以使用 Jackson 序列化器/反序列化器破解它)。该机制绕过服务层,并在控制器中公开 repo(这很棒),但在我的情况下,我在服务层中进行了一堆验证、解析、查找等