0

I am using spring jpa and lombok to define the java bean Topic. Each topic will have many comments. My onetomany configuration is

@Entity
@Table(name = "TOPIC")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Topic implements Serializable {

@OneToMany(
            fetch = FetchType.LAZY, 
            cascade = CascadeType.REMOVE,
            mappedBy = "topic"
        )
    private Set<Comment> comments= new HashSet<>();

The restful api I created is like this. The serialization seems to be the issue, and it always fetch the comments. As mentioned by Chris, I have added @JsonIgnore, and it seems solving the issue. But what if I want to load the comments, @JsonIgnore won't return the comments in the serialization.

@GetMapping("/topics")
   public List<Topic> getAllTopics(@SortDefault(sort = "topicName", direction = DESC) Sort sort) {
      return topicService.getAllTopics(sort);
   }
PLee
  • 393
  • 9
  • 26
  • 1
    Where/how do you see it loading? – pirho Dec 03 '21 at 13:12
  • The printed SQL in the console shows it pulls comments. @pirho – PLee Dec 03 '21 at 15:10
  • This should work, what is your JPA provider? How are you checking the mapping to verify it was fetched or not - anything that accesses the relationship, such as serialization or even a toString call, could be forcing the relationship to be fetched – Chris Dec 03 '21 at 16:41
  • @Chris, I use hibernate. Because it printed two SQL statements in the console, and the second one is to get comments from the comment table. I have added the restful api in the desc. Will this be the cause for the comment pull? Thanks – PLee Dec 03 '21 at 19:11
  • Try to add 100 or 1000 comments. Does it make any difference? – pirho Dec 03 '21 at 19:26
  • 1
    Check the results on the other side - if it includes the comments, then yes, this is the cause. It has to check the collection to serialize them, which causes them to be fetched on lazy collections. Your serialization process isn't JPA, so it cannot know anything about the JPA annotations and what you want to do with this collection - try seeing what you are using and marking it as @JsonIgnore if using Jackson – Chris Dec 03 '21 at 21:39
  • @Chris, comments are empty [] in the results returned from ```getAllTopics()```. serialization seems to be the issue, it won't fetch the comments after I add ```@JsonIgnore```. But what if I need to fetch the comments, JsonIgnore will ignore the comments. any suggestions? Thanks – PLee Dec 04 '21 at 15:45
  • @pirho. no difference – PLee Dec 04 '21 at 15:46
  • Yes. As Chris stated comments are always fetched because of serialization. You did not mention that originally, only that you use repo.findAll(). If you re-formulate your original question to better reflect your problem as you stated in your previous comment (with `@JsonIgnore`) there is an answer. – pirho Dec 04 '21 at 17:30

1 Answers1

1

I believe that what you actually need is a projection. Lazy fetch is not something that can be easily configured runtime not to mention preventing loading lazy items.

Instead you should declare the data you want to be fetched from the database with a DTO class or even better with an interface telling what to show.

See below repository (I have omitted the service part since you did not show it either):

public interface TopicRepository extends PagingAndSortingRepository<Topic, Long> {  
    List<Topic> findAll(Sort sort);

    // bean convention getter declarations for fields to fetch
    interface TopicWithoutComments {
        Long getId();
        String getTopicName();
    }

    List<TopicWithoutComments> findAllBy(Sort sort);
}

Now yiu need separate methods (or any other way you want to decide whether to show or not the comments), first the original:

@GetMapping("/topics")
public List<Topic> getAllTopics(@SortDefault(sort = "topicName", 
        direction = Direction.DESC) Sort sort) {
    return topicRepository.findAll(sort);
}

It outputs something like:

Hibernate: select comments0_.topic_id as topic_id3_0_0_, comments0_.id as id1_0_0_, comments0_.id as id1_0_1_, comments0_.text as text2_0_1_, comments0_.topic_id as topic_id3_0_1_ from comment comments0_ where comments0_.topic_id=?
...

for each Topic found. Then the projection endpoint could be like:

@GetMapping("/topics/wocomments")
public List<TopicWithoutComments> getAllTopicsWithoutComments(
        @SortDefault(sort = "topicName",
            direction = Direction.DESC) Sort sort) {
    return topicRepository.findAllBy(sort);
}

which would print only one line (assuming there are no other one-to-many relationships on Topic):

Hibernate: select topic0_.id as col_0_0_, topic0_.topic_name as col_1_0_ from topic topic0_ order by topic0_.topic_name desc

pirho
  • 11,565
  • 12
  • 43
  • 70
  • Thanks pirho. I think your answer is the correct direction. I am currently using your way to change my project. I have posted another question in https://stackoverflow.com/questions/70266789/spring-jpa-repository-returns-the-entity-class-instead-of-the-dto-interface. Would you mind helping me with it? Thanks – PLee Dec 07 '21 at 21:02