Mapstruct - 在响应中发送具有(一对多关系)的嵌套实体
Mapstruct - Send nested entity having (one-to-many relation) in the response
我有 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
}
因此,如果用户要求所有 "CallRecords" 数据,我也必须提供 "CallRecordOperators",因为它们是相关的。
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
}]
}
运算符数组的值为空。他可能有什么问题?
当您执行 A
元素的 Hibernate 查询时,您可以使用不同的策略获取 bs
集合的相关 B
元素。其中一些是:
如果您使用 HQL 构建查询,您可以执行 JOIN FETCH
或 LEFT JOIN FETCH
来填充 bs
集合:
String hql = "SELECT DISTINCT a FROM " + A.class.getName()
+ " a LEFT JOIN FETCH a.bs WHERE ...";
此查询将使用单个 SQL 查询加载所有数据。
使用预取 bs
集合,更改 @OneToMany
注释:
@OneToMany(fetch=FetchType.EAGER)
private List<B> bs;
在这种情况下,当您 运行 查询 A
元素时,将启动 SQL 查询以检索 A
数据,并且对于每个 A
对象,将执行 SQL 查询以加载相应的 bs
集合。
如果你使用Criteria
构建查询,你可以通过类似于HQLJOIN FETCH
的方式改变bs
集合的获取模式:
Criteria c = session.createCriteria(A.class);
c.setFetchMode("bs", FetchMode.JOIN);
c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
您似乎缺少从 CallRecordOperators
到 CallRecordOperatorsDto
的映射:
@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);
}
换一种稍微不同但效果更好的方法怎么样?通过使用 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
我有 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
}
因此,如果用户要求所有 "CallRecords" 数据,我也必须提供 "CallRecordOperators",因为它们是相关的。
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
}]
}
运算符数组的值为空。他可能有什么问题?
当您执行 A
元素的 Hibernate 查询时,您可以使用不同的策略获取 bs
集合的相关 B
元素。其中一些是:
如果您使用 HQL 构建查询,您可以执行
JOIN FETCH
或LEFT JOIN FETCH
来填充bs
集合:String hql = "SELECT DISTINCT a FROM " + A.class.getName() + " a LEFT JOIN FETCH a.bs WHERE ...";
此查询将使用单个 SQL 查询加载所有数据。
使用预取
bs
集合,更改@OneToMany
注释:@OneToMany(fetch=FetchType.EAGER) private List<B> bs;
在这种情况下,当您 运行 查询
A
元素时,将启动 SQL 查询以检索A
数据,并且对于每个A
对象,将执行 SQL 查询以加载相应的bs
集合。如果你使用
Criteria
构建查询,你可以通过类似于HQLJOIN FETCH
的方式改变bs
集合的获取模式:Criteria c = session.createCriteria(A.class); c.setFetchMode("bs", FetchMode.JOIN); c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
您似乎缺少从 CallRecordOperators
到 CallRecordOperatorsDto
的映射:
@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);
}
换一种稍微不同但效果更好的方法怎么样?通过使用 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