Mapstruct - 在响应中发送具有(一对多关系)的嵌套实体

时间:2016-12-29 09:25:58

标签: java hibernate one-to-many dto mapstruct

我有2个实体 CallRecords CallRecordOperators ,具有一对多关系,如下所示

 public class CallRecords {

    @Id
    @Column(name = "id", unique = true)
    private String id;

    @Column(columnDefinition = "varchar(255) default ''")
    private String callerNumber = "";

    @OneToMany(mappedBy="callrecord")
    private List<CallRecordOperators> callRecordOperators = new ArrayList<CallRecordOperators>();


   //getter setters
}

public class CallRecordOperators {

    @Id
    @Column(name = "id", length = 50, unique = true, nullable = false, insertable = false, updatable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @JsonIgnore
    @ManyToOne
    @JoinColumn(name = "callRecordId")
    private CallRecords callrecord;

    @ManyToOne
    @JoinColumn(name = "operatorId")
    private Operator operator;

    @Formats.DateTime(pattern = "yyyy-MM-dd HH:mm:yy")
    @Column(columnDefinition = "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP")
    private Date startTime = new Date();

    @Column(columnDefinition = "varchar(100) default ''")
    private String dialStatus;

   //getter setter
}

因此,如果用户要求所有&#34; CallRecords&#34;我还必须提供数据&#34; CallRecordOperators&#34;因为它们是相关的。

Mapper和DTO的当前代码

@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface CallRecordsMapper {

    CallRecordsMapper INSTANCE = Mappers.getMapper(CallRecordsMapper.class);

    @Mapping(source="callRecordOperators",target = "operators")
    CallRecordsDto callRecordsToCallRecordsDto(CallRecords callRecords);

    public abstract CallRecordOperatorsDto toTarget(CallRecordOperators source);

    List<CallRecordsDto> callRecordsToCallRecordsDtos(List<CallRecords> callRecords);

}

public class CallRecordsDto {

    private String callerNumber;

    private List<CallRecordOperatorsDto> operators;

    //getter setters
}

public class CallRecordOperatorsDto {

    private String callRecordsId;

    private String operatorId;
    private String operatorName;

    private String currentTime;

   // getter setter

}

但对于上面的代码我正在

{
    "callerNumber": "9898989898",
    "operators": [{
        "callRecordsId": null,
        "operatorId": null,
        "operatorName": null,
        "currentTime": null
    }, {
        "callRecordsId": null,
        "operatorId": null,
        "operatorName": null,
        "currentTime": null
    }]
}

operator数组的值为null。他可能会发出什么问题?

3 个答案:

答案 0 :(得分:3)

您似乎缺少从CallRecordOperatorsCallRecordOperatorsDto的映射:

@Mapper
public interface CallRecordsMapper {

    CallRecordsMapper INSTANCE = Mappers.getMapper(CallRecordsMapper.class);

    @Mapping(source="callRecordOperators",target = "operators")
    CallRecordsDto callRecordsToCallRecordsDto(CallRecords callRecords);

    @Mapping(target = "callRecordsId", source = "callrecord.id")
    @Mapping(target = "operatorId", source = "operator.id")
    @Mapping(target = "operatorName", source = "operator.name")
    @Mapping(target = "currentTime", source = "startTime")
    CallRecordOperatorsDto callRecordOperatorsToDto(CallRecordOperators source);
}

答案 1 :(得分:0)

当您对A元素执行Hibernate查询时,您可以使用不同的策略获取B集合的相关bs元素。其中一些是:

  1. 如果您使用HQL构建查询,则可以JOIN FETCHLEFT JOIN FETCH填充bs集合:

    String hql = "SELECT DISTINCT a FROM " + A.class.getName() 
        + " a LEFT JOIN FETCH a.bs WHERE ...";
    

    此查询将使用单个SQL查询加载所有数据。

  2. 使用热切提取bs集合,更改@OneToMany注释:

    @OneToMany(fetch=FetchType.EAGER)
    private List<B> bs;
    

    在这种情况下,当您运行A元素的查询时,将启动SQL查询以检索A数据,并且对于结果中的每个A对象,将执行SQL查询以加载相应的bs集合。

  3. 如果您使用Criteria构建查询,则可以采用与HQL bs类似的方式更改JOIN FETCH集合的获取模式:

    Criteria c = session.createCriteria(A.class);
    c.setFetchMode("bs", FetchMode.JOIN);
    c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
    

答案 2 :(得分:-1)

如何切换到稍微不同的方法,同样表现更好?通过使用Blaze-Persistence Entity Views,您可以直接在DTO类上定义映射,并将其应用于查询构建器,以生成完全符合DTO结构的高效查询。

@EntityView(CallRecords.class)
public interface CallRecordsDto {
    // The id of the CallRecords entity
    @JsonIgnore
    @IdMapping("id") String getId();

    String getCallerNumber();

    @Mapping("callRecordOperators")
    List<CallRecordOperatorsDto> getOperators();
}

@EntityView(CallRecordOperators.class)
public interface CallRecordOperatorsDto {

    // The id of the CallRecordOperators entity
    @JsonIgnore
    @IdMapping("id") Long getId();

    @Mapping("callrecord.id")
    String getCallRecordId();

    @Mapping("operator.id")
    String getOperatorId();

    @Mapping("operator.name")
    String getOperatorName();

    @Mapping("startTime")
    String getCurrentTime();

    // Whatever properties you want
}

了解如何在DTO中映射实体属性?这里有查询代码

EntityManager entityManager = // jpa entity manager
CriteriaBuilderFactory cbf = // query builder factory from Blaze-Persistence
EntityViewManager evm = // manager that can apply entity views to query builders

CriteriaBuilder<User> builder = cbf.create(entityManager, CallRecords.class)
    .where("callerNumber").eq("123456789");
List<CallRecordsDto> result = evm.applySetting(
    builder, 
    EntityViewSetting.create(CallRecordsDto.class)
).getResultList();

请注意,这将粗略地生成以下优化查询

SELECT 
    c.id, 
    c.callerNumber, 
    o.callrecord.id, 
    o.id,
    o.startTime,
    op.id,
    op.name
FROM CallRecords c
LEFT JOIN c.callRecordOperators o
LEFT JOIN o.operator op
WHERE c.callerNumber = :param_1