Java 在Hibernate会话之外执行Mapstruct映射

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

我使用的是Spring数据和Mapstruct,我不希望hibernate在将实体映射到dto时盲目地加载所有元素

例如:

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);