Java 在Hibernate会话之外执行Mapstruct映射
我使用的是Spring数据和Mapstruct,我不希望hibernate在将实体映射到dto时盲目地加载所有元素 例如:Java 在Hibernate会话之外执行Mapstruct映射,java,hibernate,spring-data-jpa,mapping,mapstruct,Java,Hibernate,Spring Data Jpa,Mapping,Mapstruct,我使用的是Spring数据和Mapstruct,我不希望hibernate在将实体映射到dto时盲目地加载所有元素 例如: public class VacancyEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Integer id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "job_categ
public class VacancyEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "job_category_id", nullable = false)
JobCategoryEntity jobCategory;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "company_id", nullable = false)
CompanyEntity company;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "employer_created_by", nullable = false)
EmployerProfileEntity employerCreatedBy;
@Column(nullable = false)
String title;
.... }
DTO:
因此,在VacancyRepository
中,我有两种方法findByIdWithCompanyAndCity
和findByIdWithJobAndCityAndEmployer
,只执行一个SQL请求
在我的VacancyService
中有两个@Transactional
方法:findWithCompanyAndCity
和findWithCompanyAndCityAndEmployer
最佳实践是从服务层返回Dto,所以我们需要在服务中将实体解析为Dto
我真的不想把整个映射留在@Transactional
(会话)中,因为如果我在实体中添加一些字段,Mapstruct只会触发N+1问题。
我想到的最好方法是将每个内部实体包含到方法中,并手动检查Mapstruct是否不添加一些新方法。(比检查姓名更快)
例:
但这并不能解决真正的问题。映射时仍存在会话,因此可能导致N+1问题
所以我想出了几个解决办法
- 使用服务中的特殊方法触发@Transactional方法,然后映射到会话范围外的DTO。但在服务中使用双重方法似乎真的很难看
- 从服务返回实体(这是错误的做法)并映射到DTO
LazyInitializationException
,但在我看来,它比不可预测的选择更加健壮和可伸缩
如何以优雅的方式在服务层但在Hibernate会话之外执行从实体到DTO的映射?您没有问任何问题,但问题似乎应该是: 如何以优雅的方式在服务层但在Hibernate会话之外执行从实体到DTO的映射 我推荐这一款。 用法如下所示:
@Autowired
VacancyRepository回购;
@自动连线
交易模板tx;
void someMethod(字符串公司、字符串城市){
VacancyEntity vac=tx.execute(_uu->repo.findwithcompanyanddcity(company,city));
返回映射到(vac);
}
也就是说,我认为您使用了错误的方法来解决根本问题。
我建议您考虑进行一次测试,以验证执行的SQL语句的数量。
请参阅,以了解实现此目的的方法。为了避免N+1问题,您仍然需要使用实体图,尽管我认为这是一个非常适合的用例 我创建了这个库,以便在JPA模型和自定义接口或抽象类定义的模型之间进行简单的映射,类似于类固醇上的Spring数据投影。其思想是以您喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(getter)映射到实体模型 在Blaze持久性实体视图中,您的用例的DTO模型可能如下所示:
@EntityView(VacancyEntity.class)
public interface VacancyDto {
@IdMapping
Integer getId();
String getTitle();
CompanyDto getCompany();
EmployerProfileDto getEmployerCreatedBy();
JobCategoryDto getJobCategory();
@EntityView(CompanyEntity.class)
interface CompanyDto {
@IdMapping
Integer getId();
String getName();
}
@EntityView(EmployerProfileEntity.class)
interface EmployerProfileDto {
@IdMapping
Integer getId();
String getName();
}
@EntityView(JobCategoryEntity.class)
interface JobCategoryDto {
@IdMapping
Integer getId();
String getName();
}
}
查询是将实体视图应用于查询的问题,最简单的就是按id进行查询
VacancyDto a=entityViewManager.find(entityManager,VacancyDto.class,id)代码>
Spring数据集成允许您像使用Spring数据投影一样使用它:
Page findAll(可分页);
最好的是,它只会获取实际需要的状态
@Mapping(target = "id", source = "entity.id")
@Mapping(target = "description", source = "entity.description")
@Mapping(target = "jobCategory", source = "jobCategoryDto")
@Mapping(target = "employerCreatedBy", source = "employerProfileDto")
@Mapping(target = "city", source = "cityDto")
@Mapping(target = "company", ignore = true)
VacancyDto toDto(VacancyEntity entity,
JobCategoryDto jobCategoryDto,
EmployerProfileDto employerProfileDto,
CityDto cityDto);
....
@EntityView(VacancyEntity.class)
public interface VacancyDto {
@IdMapping
Integer getId();
String getTitle();
CompanyDto getCompany();
EmployerProfileDto getEmployerCreatedBy();
JobCategoryDto getJobCategory();
@EntityView(CompanyEntity.class)
interface CompanyDto {
@IdMapping
Integer getId();
String getName();
}
@EntityView(EmployerProfileEntity.class)
interface EmployerProfileDto {
@IdMapping
Integer getId();
String getName();
}
@EntityView(JobCategoryEntity.class)
interface JobCategoryDto {
@IdMapping
Integer getId();
String getName();
}
}
Page<VacancyDto> findAll(Pageable pageable);