我读了很多关于这个东西的文章,现在我正在开发一个更大的web应用程序及其相应的后端。
但是,我从一个设计开始,在这个设计中,我要求存储库从数据库中获取数据并将其映射到DTO中。为什么是DTO?因为到目前为止,基本上所有的事情都是简单的,不需要更多的复杂性。如果它变得更加复杂,那么我就开始在服务层直接映射1到n之间的关系。类似于:
// This is Service-Layer
public List<CarDTO> getCarsFromOwner(Long carOwnerId) {
// Entering Repository-Layer
List<CarDTO> cars = this.carRepository = this.carRepository.getCars(carOwnerId);
Map<Long, List<WheelDTO>> wheelMap = this.wheelRepository.getWheels(carId);
for(CarDTO car : cars) {
List<WheelDTO> wheels = wheelMap.get(car.getId());
car.setWheels(wheels);
}
return cars;
}当然,这是可行的,但事实证明,有时候事情比这更复杂,我开始意识到,如果我不对此做任何事情,代码看起来可能会很难看。
当然,我可以在wheelMap中加载CarRepository,在那里执行轮询映射,并且只返回完整的对象,但是由于SQL查询有时看起来非常复杂,所以我不想获取所有的cars及其wheels,还要处理getCars(Long ownerId)中的映射。
我显然错过了一个商业层,对吧?但我根本无法理解它的最佳实践。
假设我有Car和Owner业务对象。我的代码会像这样:
// This is Service-Layer
public List<CarDTO> getCarsFromOwner(Long carOwnerId) {
// The new Business-Layer
CarOwner carOwner = new CarOwner(carOwnerId);
List<Car> cars = carOwner.getAllCars();
return cars;
}这看起来很简单,但是内部会发生什么呢?这个问题特别针对CarOwner#getAllCars()。
我设想这个函数将使用Mappers和存储库来加载数据,特别是处理关系映射部分:
List<CarDTO> cars = this.carRepository = this.carRepository.getCars(carOwnerId);
Map<Long, List<WheelDTO>> wheelMap = this.wheelRepository.getWheels(carId);
for(CarDTO car : cars) {
List<WheelDTO> wheels = wheelMap.get(car.getId());
car.setWheels(wheels);
}但是怎么做呢?CarMapper是否提供getAllCarsWithWheels()和getAllCarsWithoutWheels()功能?这也会将CarRepository和WheelRepository移动到CarMapper中,但这是存储库的合适位置吗?
如果有人能为上面的代码展示一个很好的实用示例,我会很高兴的。
更多信息
我不使用ORM,而是使用jOOQ。它实际上只是编写SQL的一种类型安全的方法(而且它使使用SQL变得非常有趣)。
下面是一个这样的例子:
public List<CompanyDTO> getCompanies(Long adminId) {
LOGGER.debug("Loading companies for user ..");
Table<?> companyEmployee = this.ctx.select(COMPANY_EMPLOYEE.COMPANY_ID)
.from(COMPANY_EMPLOYEE)
.where(COMPANY_EMPLOYEE.ADMIN_ID.eq(adminId))
.asTable("companyEmployee");
List<CompanyDTO> fetchInto = this.ctx.select(COMPANY.ID, COMPANY.NAME)
.from(COMPANY)
.join(companyEmployee)
.on(companyEmployee.field(COMPANY_EMPLOYEE.COMPANY_ID).eq(COMPANY.ID))
.fetchInto(CompanyDTO.class);
return fetchInto;
}发布于 2016-09-17 17:38:44
模式库属于数据访问对象的模式组,通常意味着--对相同类型的对象的存储的抽象。想想您可以用来存储对象的Java集合--它有哪些方法?它是如何运作的?
根据这一定义,存储库不能使用DTOs -它是--一个域实体的存储。如果您只有DTO,那么您需要更通用的DAO,或者,可能是CQRS模式。有一个存储库的独立接口和实现是很常见的,比如在Spring中(它会自动生成实现,所以您只需要指定接口,很可能是从公共超级接口CrudRepository继承基本CRUD操作)。示例:
class Car {
private long ownerId;
private List<Wheel> wheels;
}
@Repository
interface CarRepository extends CrudRepository<Car,Long> {
List<Car> findByOwnerId(long id);
}当您的域模型是一棵对象树并将它们存储在关系数据库中时,事情就变得复杂了。根据这个问题的定义,您需要一个ORM。将关系内容加载到对象模型中的每一段代码都是ORM,因此您的存储库将有一个ORM作为实现。通常,JPA会在场景后面连接对象,更简单的解决方案,比如基于JOOQ或普通JDBC的定制映射器,必须手动完成。没有任何解决所有ORM问题的灵丹妙药能够有效和正确地解决:如果您选择编写自定义映射,最好还是将布线保留在存储库中,这样业务层(服务)就可以使用真正的对象模型操作。在您的示例中,CarRepository了解Cars。Car知道Wheels,因此CarRepository已经对Wheel具有传递依赖关系。在CarRepository#findByOwnerId()方法中,您可以通过添加一个join为同一查询中的汽车直接获取Wheels,或者将此任务委托给WheelRepository,然后只进行连接。此方法的用户将收到完全初始化的对象树。示例:
class CarRepositoryImpl implements CarRepository {
public List<Car> findByOwnerId(long id) {
// pseudocode for some database query interface
String sql = select(CARS).join(WHEELS);
final Map<Long, Car> carIndex = new HashMap<>();
execute(sql, record -> {
long carId = record.get(CAR_ID);
Car car = carIndex.putIfAbsent(carId, Car::new);
... // map the car if necessary
Wheel wheel = ...; // map the wheel
car.addWheel(wheel);
});
return carIndex.values().stream().collect(toList());
}
}业务层(有时也称为服务层)的作用是什么?业务层对对象执行业务特定的操作,如果这些操作必须是原子的,则管理事务。基本上,它知道何时发出事务启动、事务提交和回滚的信号,但不知道这些消息实际上将在事务管理器实现中触发什么。从业务层的角度来看,只有对对象、边界和事务隔离的操作,而没有其他操作。它不必知道映射器或任何位于存储库接口后面的东西。
发布于 2016-09-18 03:26:36
在我看来,没有正确的答案。这实际上取决于所选择的设计决策,它本身取决于您的堆栈和您/团队的舒适度。
案例1:
我不同意你在发言中强调的以下各节:
“当然,我可以在wheelMap中加载CarRepository,在那里执行轮询映射,并且只返回完整的对象,但是由于查询有时看起来非常复杂,所以我不想获取所有的汽车及其车轮,还要处理getCars(Long OwnerId)中的映射。”
上面的sql将很简单。此外,由于数据库是为连接和获取数据而优化的,所以它可能会更快。现在,我将这种方法称为Case1,因为如果您决定使用sql通过存储库提取数据,就可以遵循这种方法。而不是仅仅将sql用于简单的CRUD,然后在java中操作对象。(下文)
Case2:存储库仅用于获取与“一个”表对应的“每个”域对象的数据。
在这种情况下,您所做的已经是正确的。如果您永远不会单独使用WheelDTO,那么就没有必要为它提供单独的服务。你可以准备所有的汽车服务。但是,如果您单独需要WheelDTO,则为每个服务提供不同的服务。在这种情况下,在服务层的顶部可以有一个助手层来执行对象创建。我不建议从零开始实现orm,方法是为每个回购(直接使用hibernate或)创建存储库并加载所有联接。
同样,IMHO,无论您从上面的任何方法,服务或业务层只是补充它。所以,没有硬和快速的规则,试着灵活的根据你的要求。如果您决定使用ORM,上面的一些内容将再次更改。
https://stackoverflow.com/questions/39128750
复制相似问题