Когда используется @OneToMany и @ManyToOne и с MapStruct - я получаю бесконечность рекурсии - PullRequest
0 голосов
/ 24 марта 2020
  • сущность
Entity
@Table(name ="posts")
public class Post {

    @Id
    @SequenceGenerator(name = "jpaSequence.Post",
            sequenceName = "SEQUENCE_POST",
            allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jpaSequence.Post")
    private Long id;
    private String subject;

    @OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
    private List<Comment> comments = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn
    private User user;


    public Post() {
    }
@Entity
@Table(name = "comments")
public class Comment {

    @Id
    @SequenceGenerator(name = "jpaSequence.Comment",
            sequenceName = "SEQUENCE_COMMENT",
            allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jpaSequence.Comment")
    private Long id;

    private String reply;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn
    private Post post;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn
    private User user;


    public Comment() {
    }
@Entity
@Table(name = "users")
public class User {

    @Id
    @SequenceGenerator(name = "jpaSequence.User",
            sequenceName = "SEQUENCE_USER",
            allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jpaSequence.User")
    private Long id;

    private String name;
    private String email;


    public User() {
    }
  • картограф
@Mapper(componentModel = "spring" )
public interface PostMapper {

    Post postDtoToPostEntity(PostDto dto);

    PostDto postEntityToPostDto(Post entity);

    Iterable<Post> postListDtoToPostListEntity(Iterable<PostDto> list);

    Iterable<PostDto> postListEntityToPostListDto(Iterable<Post> list);
}

@Mapper(componentModel = "spring")
public interface CommentMapper {

    Comment commentDtoToCommentEntity (CommentDto dto);

    CommentDto commentEntityToCommentDto(Comment entity);

    Iterable<Comment> commentListDtoToCommentListEntity(Iterable<CommentDto> list);

    Iterable<CommentDto> commentListEntityToCommentListDto(Iterable<Comment> list);
}

-сервис

@Service
public class PostReadServiceImpl implements PostReadService {

    private PostRepository repository;

    private PostTransformer transformer;

    private CommentTransformer transformerComment;

    @Autowired
    public PostReadServiceImpl(PostRepository repository, PostTransformer transformer,
                               CommentTransformer transformerComment) {
        this.repository = repository;
        this.transformer = transformer;
        this.transformerComment = transformerComment;
    }


    @Transactional
    @Override
    public PostDto getEntryById(Long id) {

        Post entity = find(id);

        return this.transformer.transformEntityToDto(entity);
    }


    private Post find(Long id){

        return this.repository.findById(id).orElseThrow(() -> new RuntimeException("Post not found!"));
    }


    @Transactional
    @Override
    public Iterable<CommentDto> getListEntriesCommentById(Long id) {

        Iterable<Comment> listComment = findListComment(id);

        Iterable<CommentDto> commentDtoList = this.transformerComment.transformListEntityToDtoList(listComment);

        return commentDtoList;
    }

    private Iterable<Comment> findListComment(Long id){

        Post post = this.repository.findById(id).orElseThrow(() -> new RuntimeException("Post not found!"));
        List<Comment> comments = post.getComments();
        return comments;
    }
}

При использовании MapStruct я получаю бесконечную рекурсию. Даже если я не запрашивал связанную сущность комментариев у полученного объекта, я все равно вижу бесконечное вложение в режиме отладки, и это несмотря на то, что я выбрал

fetch = FetchType.LAZY

У кого есть идеи как это исправить и почему так работает?

1 Ответ

0 голосов
/ 25 марта 2020

Решение

  • отдых
@RestController
@RequestMapping("api/read")
public class PostReadRest {

    private static final String PATH_TO_READ_POST_BY_ID = "post/{id}";

    private static final String PATH_TO_READ_POST_LIST = "post/list";

    private PostReadService service;

    @Autowired
    public PostReadRest(PostReadService service) {
        this.service = service;
    }

    @GetMapping(PATH_TO_READ_POST_BY_ID)
    public PostDto getPostById(@PathVariable String id){

        Long idLong = Long.valueOf(id);

        return service.getEntryById(idLong);
    }

}

  • сервис
@Service
public class PostReadServiceImpl implements PostReadService {

    private PostRepository repository;

    private PostTransformer transformer;

    private CommentTransformer transformerComment;

    @Autowired
    public PostReadServiceImpl(PostRepository repository, PostTransformer transformer,
                               CommentTransformer transformerComment) {
        this.repository = repository;
        this.transformer = transformer;
        this.transformerComment = transformerComment;
    }


    @Transactional
    @Override
    public PostDto getEntryById(Long id) {

        Post entity = find(id);

        PostDto postDto = this.transformer.transformEntityToDto(entity);

        return postDto;
    }


    private Post find(Long id){

        return this.repository.findById(id).orElseThrow(() -> new RuntimeException("Post not found!"));
    }


}
  • mapper
@Mapper(componentModel = "spring")
public interface PostMapper {

    Post postDtoToPostEntity(PostDto dto);

    @Mapping(target = "post.comments", ignore = true)
    CommentDto toCommentDto(Comment entity);

    PostDto postEntityToPostDto(Post entity);

    Iterable<Post> postListDtoToPostListEntity(Iterable<PostDto> list);

    Iterable<PostDto> postListEntityToPostListDto(Iterable<Post> list);
}

  • @ Mapping (target = "post.comments", ignore = true)

post - это это имя поля «Комментарий» сущности.

comments - это имя поля «Пост» сущности, но это поле здесь - поле второго уровня. И это надо игнорировать.

  • Трансформаторы
@Component
public class PostTransformer {

    private PostMapper mapper;

    @Autowired
    public PostTransformer(PostMapper mapper) {
        this.mapper = mapper;
    }

    public PostDto transformEntityToDto(Post entity){

        PostDto postDto = this.mapper.postEntityToPostDto(entity);

        return postDto;
    }


    public Post transformDtoToEntity(PostDto dto){

        return this.mapper.postDtoToPostEntity(dto);
    }

    public Iterable<PostDto> transformListEntityToDtoList(Iterable<Post> listEntity){

        Iterable<PostDto> postDtoList = this.mapper.postListEntityToPostListDto(listEntity);

        return postDtoList;
    }

    public Iterable<Post> transformListDtoToEntityList(Iterable<PostDto> listDto){

        return this.mapper.postListDtoToPostListEntity(listDto);
    }

}

Результат

{
    "id": 1,
    "subject": "JPA Entity Graph In Action",
    "comments": [
        {
            "id": 1,
            "reply": "Nice !!",
            "post": {
                "id": 1,
                "subject": "JPA Entity Graph In Action",
                "comments": [],
                "user": {
                    "id": 1,
                    "name": "user1",
                    "email": "user1@test.com"
                }
            },
            "user": {
                "id": 2,
                "name": "user2",
                "email": "user2@test.com"
            }
        },
        {
            "id": 2,
            "reply": "Cool !!",
            "post": {
                "id": 1,
                "subject": "JPA Entity Graph In Action",
                "comments": [],
                "user": {
                    "id": 1,
                    "name": "user1",
                    "email": "user1@test.com"
                }
            },
            "user": {
                "id": 3,
                "name": "user3",
                "email": "user3@test.com"
            }
        }
    ],
    "user": {
        "id": 1,
        "name": "user1",
        "email": "user1@test.com"
    }
}

Это важно .

Если вы не указали на метод (он имеет аннотацию @Override) аннотацию @ Transactional , вы столкнетесь с Exception , если Вы используете - FetchType.LAZY

    @Transactional
    @Override
    public PostDto getEntryById(Long id) {

        Post entity = findEntry(id);

        List<Comment> comments = entity.getComments();

        PostDto postDto = this.transformer.transformEntityToDto(entity);

        return postDto;
    }

Во время выполнения транзакции, в то время как выполняется Mapper, будет выполняться дополнительный запрос, который получает вложенные записи из связанных таблиц.

...