这里的问题是,您已经在web范围内初始化了实体(实际上不应该知道持久层实现的细节),然后传递给服务层,服务层可能使用不同的线程。。。
您应该应用长寿的“OpenSessionInView”模式,该模式现在被认为是“不良实践”或“反模式”。此模式将确保您的实体将使用相同的会话实例,从您的web请求的最开始,直到web请求发送所有响应数据。
或者另一个选择:重新设计应用程序,引入纯DTO层,并创建映射。DTO在WEB层进行处理,并传递到服务层,在服务层将其映射到实体。在这种情况下,我建议使用Mapstruct生成映射程序
更新:
你有两个实体。
@Entity
class MyEntity {
// ...properties
@Id
private Long id;
String stringProperty;
LocalDate dateProperty;
}
@Entity
class MyDependentEntity {
@Id
Long id;
@ManyToOne(targetEntity=MyEntity.class)
MyEntity entity;
// ... properties
}
现在您希望您的服务能够更新您的
MyEntity
还有你的
MyDependentEntity
为此,您可以创建DTO,它将是您的实体的纯表示:
class MyEntityDTO {
Long id;
String stringProperty;
LocalDate dateProperty;
}
class MyDependentEntityDTO {
Long id;
// here we will be transferring the ID of the entity. Or we can pass the DTO.
// it depends on usage scenario.
Long entity;
}
对于此更新,您将创建服务层,该层将使用此DTO。或者我称之为“Usermodel”对象,即为我的服务/app/microservice/的客户端打开的数据模型。
@Service
class ManagementService {
MyEntityRepository entityRepository;
MyDependentEntityRepository dependentEntityRepository;
MapStructMapper mapper;
@Transactional
public MyEntityDTO saveOrUpdate(@Valid MyEntityDTO dto) {
MyEntity managedEntity = Optional.ofNullable(dto.id).map(entityRepository::findOne).orElse(new MyEntity());
// now we need to copy values from dto to managed entity. We can write set of setters by own hands, or use the [mapstruct][1]
mapper.map(dto, managedEntity);
managedEntity = entityRepository.saveAndFlush(managedEntity);
return mapper.map(managedEntity);
}
@Transactional
MyDependentEntityDTO saveOrUpdate(MyDependentEntityDTO dto) {
// first of all we assume, that if there is no passed id, the entity should be created
// and if it is present the entity, already exists.
MyDependentEntity managedEntity =
Optional.ofNullable(dto.id).map(dependentEntityRepository::findOne)
.orElse(new MyDependentEntity());
// it is up to you which logic to apply if ID exist, but no entity found.
// Current implementation will throw nullpointer exception in this case. Which for me is quite fine.
// now we need to copy values from dto to managed entity. We can write set of setters by own hands, or use the [mapstruct][1]
mapper.map(dto, managedEntity);
managedEntity = dependentEntityRepository.saveAndFlush(managedEntity);
return mapper.map(managedEntity);
}
}
现在给地图绘制者。你可以自己创造。但这将需要大量的monkeycoding,当您更新实体和dto时,可能会成为出现问题的地方,但忘记更新映射器。Mapstruct来到这里,给予了极大的支持。它是注释处理器,允许生成样板代码。你只需写:
// componentModel="spring" will make the generated MapStructMapperImpl class
// into a spring bean, available for injection in your services.
@Mapper(componentModel="spring")
abstract class MapStructMapper {
@Autowired
MyEntityRepository entityRepository;
abstract MyEntityDTO map(MyEntity entity);
// MappingTarget annotation marks the destination object.
abstract void map(MyEntityDTO dto, @MappingTarget MyEntity entity);
abstract MyDependentEntityDTO map(MyDependentEntity entity);
abstract void map(MyDependentEntityDTO dto, @MappingTarget MyDependentEntity entity);
// This method will be picked up by mapstruct automatically to
// map your Long property to MyEntity, during mapping of MyDependentEntity.
// NOTE: here you have to chose how to deal with NULL values.
// Mapstruct does not imply any rule: you write custom mapping on your own.
MyEntity mapMyEntityFromId(Long id) {
return Optional.ofNullable(id).map(entityRepository::findOne).orElse(null);
}
}
使用所描述的方法,您可以轻松地创建Web层,并分离存储实现和Web逻辑。
例如:如果将SQL替换为JPA到MongoDB。您根本不需要重写web层。只是为了重做你的存储库,制作那些MongoDB存储库。在这种情况下,不应重新创建服务层和映射服务。。。。不错:)