从数据传输对象(DTO)到具有双向一对多关联的Hibernate实体执行MapStruct映射的最佳方式是什么?
假设我们有一个BookDto
,其中有多个ReviewDto
类 comments :
public class BookDto {
private List<ReviewDto> reviews;
// getter and setters...
}
对应的Hibernate 实体Book
与Review
有一对多的关联:
@Entity
public class Book {
@OneToMany(mappedBy = "book", orphanRemoval = true, cascade = CascadeType.ALL)
private List<Review> reviews = new ArrayList<>();
public void addReview(Review review) {
this.reviews.add(review);
review.setBook(this);
}
//...
}
@Entity
public class Review {
@ManyToOne(fetch = FetchType.LAZY)
private Book book;
public void setBook(Book book) {
this.book = book;
}
//...
}
请注意,本书的addReview
方法通过调用Hibernate专家推荐的review.setBook(this)
(例如'Hibernate Tips: How to map a bi-directional many-to-one association' by Thorben Janssen或'How to synchronize bidirectional entity associations with JPA and Hibernate' by Vlad Mihalcea)双向设置关联,以确保域模型关系的一致性.
现在,我们需要一个MapStruct映射器,它可以自动将 comments 链接回该书.
- 自定义映射方法:
@Mapper
public interface BookMapper {
default Book mapBookDtoToBook(BookDto bookDto) {
//...
for (ReviewDto reviewDto : bookDto.getReviews()) {
book.addReview(mapReviewDtoToReview(reviewDto));
}
//...
}
//...
}
如果这本书还有许多其他字段需要映射,那么这会变得很麻烦.[Update: This can be simplified as suggested by 100.]
- 用
@AfterMapping
的方法使关系具有双向性:
@Mapper
public interface BookMapper {
Book mapBookDtoToBook(Book book); // Implementation generated by MapStruct
@AfterMapping
void linkReviewsToBook(@MappingTarget Book book) {
for (Review review : book.getReviews()) {
review.setBook(book);
}
}
//...
}
这种方法允许MapStruct生成所有其他字段映射;但是,通过在after映射中将自动生成的setReviews
与setBook
操作分离,我们失go 了内聚性.
- 在
Book
中添加方法setBiDirectionalReviews
,并指示MapStruct将其用作target:
@Entity
public class Book {
//...
public void setBiDirectionalReviews(List<Review> reviews) {
this.reviews = reviews;
for (Review review : this.reviews) {
review.setBook(this);
}
}
}
@Mapper
public class BookMapper {
@Mapping(source = "reviews", target = "biDirectionalReviews")
Book mapBookDtoToBook(Book book);
}
Now we have re-established cohesion, but (1) we might still need the additional method addReview
if we wanted to modify the existing reviews somewhere else, and (2) it feels somewhat hacky to abuse MapStruct's accessor naming strategy by pretending there were a field named "biDirectionalReviews".
Anyway, this is the best approach that I could find so far.
在MapStruct中映射双向关联有更好的解决方案吗?