jimmayhem
jimmayhem

Reputation: 405

How to set reference to parent element in the list of child elements when using MapStruct?

I have two classes

@Entity
class Author {
   
   @Id
   private Long id;
   
   private String authorName;
   
   @OneToMany(mappedBy = "author")
   private List<Book> authorBooks;
}

@Entity
class Book {
 
   @Id
   private Long id;
   
   private String bookTitle;
   
   @ManyToOne
   @JoinColumn(name = "id")
   private Author bookAuthor;
}

and I need to map incoming requests to the following dto class:

class AuthorRequest {

  private String authorRequestName;

  private List<String> authorRequestBooks;

}

I've implemented a simple dto interface with MapStruct

@Mapper
public interface AuthorMapper {

   @Mapping(target = "authorName", source = "request.authorRequestName")
   Author map(AuthorRequest request);

However, I also need to be able to create Book object from each book title coming as a String, as well as to set reference to the Author object in each book while I'm still in the process of creating Author. Something like this

public class AuthorMapperImpl implements AuthorMapper {
   @Override
   public Author map(AuthorRequest request) {

      Author author = new Author();
      author.setAuthorName(request.getAuthorRequestName());
      author.mapBooks(request.getAuthorRequestBooks(), author);

      return author;
   }

   private List<Book> mapBooks(List<String> authorRequestBooks, Author author) {
      List<Book> books = new ArrayList<>();
         for (String title : authorRequestBooks) {
            Book book = new Book();
            book.setBookAuthor(author);
            book.setBookTitle(title);
            books.add(book);
         }
      }
      return books;
   } 

}

Is it possible to do using MapStruct?

Upvotes: 1

Views: 1720

Answers (2)

Filip
Filip

Reputation: 21461

You can achieve this using a combination of lifecycle methods, @Context and custom mapping.

@Context will be used to keep track of the currently mapped author.

A solution can look like:

public class AuthorMappingContext {


    protected Author currentAuthor;

    @BeforeMapping
    public void startMapping(@MappingTarget Author author) {
        this.currentAuthor = author;
    }

    @AfterMapping
    public void afterMapping(@MappingTarget Author author) {
        this.currentAuthor = null;
    }

}

and your mapper will look like:

@Mapper
public interface AuthorMapper {

   @Mapping(target = "authorName", source = "request.authorRequestName")
   @Mapping(target = "authorBooks", source = "authorRequestBooks")
   Author map(AuthorRequest request, @Context AuthorMappingContext context);

    default Book mapBook(String bookTitle, @Context AuthorMappingContext context) {
        Book book = new Book();
        book.setBookTitle(bookTitle);
        book.setBookAuthor(context.getCurrentAuthor());

        return book;
    }
}

The mapBook method is needed to tell MapStruct how to map from a String to a Book.

Upvotes: 1

Related Questions