0

Database Tables

post
tag
ref_post_tag
post and tag has a Many-to-Many relationship

Entities

Post

@Entity
@Table(name = "post")
public class Post implements Serializable{
    private static final long serialVersionUID = 1783734013146305964L;

    public enum Status {
        DRAFT, REMOVED, LIVE;
    }

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private String id;

    @Column(name = "title")
    private String title;

    @Column(name = "create_time")
    private LocalDateTime createTime;

    @Column(name = "update_time")
    private LocalDateTime updateTime;

    @Column(name = "content")
    private String content;

    @Column(name = "status")
    @Enumerated(EnumType.STRING)
    private Status status;

    @ManyToMany
    @JoinTable(
            name = "ref_post_tag",
            joinColumns = @JoinColumn(name="post_id",referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name="tag_id", referencedColumnName = "id"))
    private List<Tag> tagList;
...
}  

Tag

@Entity
@Table(name="tag")
public class Tag implements Serializable{
    private static final long serialVersionUID = -7015657012681544984L;

    @Id
    @Column(name="id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(name="name")
    private String name;

    @Column(name="description")
    private String description;

    @ManyToMany(mappedBy = "tagList")
    private List<Post> postList;

    public Integer getId() {

        return id;
    }
...
}

Tag Repo

public interface TagRepo extends CrudRepository<Tag, Integer>{
}

service implementation

@Service
public class TagServiceImpl implements TagService{

    @Autowired
    private TagRepo tagRepo;

    @Override
    public void addTag(Tag tag) {
        tagRepo.save(tag);
    }

    @Override
    public Tag getTag(Integer id) {
        Tag tag = tagRepo.findOne(id);
        return tag;
    }

    @Override
    public List<Tag> findAllTags() {
        return CollectionUtil.toArrayList(tagRepo.findAll());
    }
}

sample test (Updated)

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestContextConfiguration.class)
@Transactional
public abstract class ServiceTest {
}

public class TagServiceTest extends ServiceTest{

    @Autowired
    private TagService tagService;

    @Autowired
    private TagRepo tagRepo;

    @Test
    @Transactional
    public void addTag() throws Exception {
        Tag tag = new Tag();
        tag.setName("new tag");
        tag.setDescription("this is a new tag");
        tagService.addTag(tag);

        Tag tagCreated = tagRepo.findOne(tag.getId());
        assertNotNull(tagCreated);
        assertEquals(tagCreated.getName(), tag.getName());

    }

    @Test
    public void getTag() throws Exception {
        Tag tag = tagService.getTag(1); // tag "java" has an ID of "1"
        assertNotNull(tag);
        assertEquals(tag.getName(), "java");
        assertEquals(143,tag.getPostList().size()); // 143 posts under tag "java"
    }

}

Question

The sample test case passes. It means that the postList in fetched Tag is also eagerly fetched and filled.

Is Spring data repository's methods eagerly fetching by default?
If yes, what is the best way to change this to lazy fetching?

Minjun Yu
  • 3,497
  • 4
  • 24
  • 39
  • Show us the **complete** code of the test class. – JB Nizet Apr 29 '16 at 20:27
  • @JBNizet I have updated the sample test class, thank you. – Minjun Yu Apr 29 '16 at 20:53
  • Show us the **complete**, as in, **complete** code of the test class. Do the same for ServiceTest. – JB Nizet Apr 29 '16 at 20:58
  • @JBNizet My bad, I add the parent test class. I realized that it might be caused by the Transactional annotation, but I don't know why if that is the case. – Minjun Yu Apr 29 '16 at 21:03
  • That's it. Since your test is transactional, calling size() on the list returned by tag.getPostList() lazily initializes the list. – JB Nizet Apr 29 '16 at 21:05
  • @JBNizet Thank you, You totally saved my day. I tested it without the Transaction annotation, the getTag() fails. The spring data repo uses lazy-fetching on one-to-many, many-to-many relationship by default. If transaction annotation is used, then I can lazily fetch the child object,(postList in my case). If Transaction annotation is not used, lazyInitializationException will be thrown when I try to lazily fetch the postList by calling getter. Am I correct? Should I answer my own question so that it is easier for others to see? – Minjun Yu Apr 29 '16 at 21:14
  • Not completely. The Spring data repo isn't the one deciding if lazy loading is used or not. All it does is delegate to EntityManager.find(). Whether you use Spring or not, the JPA specification applies: a toMany association is lazy by default, unless you set the fetch attribute on the annotation to EAGER. Also, what causes the collection to be loaded from the database is not the call to getPostList(). That just returns an uninitialized collection. Calling any method on that collection (size(), in your example), is what triggers the lazy loading. – JB Nizet Apr 29 '16 at 21:19
  • @JBNizet Thank you, that clears my confusion. – Minjun Yu Apr 29 '16 at 21:22
  • Possible duplicate of [how to lazy load collection when using spring-data-jpa, with hibernate, from an console application](http://stackoverflow.com/questions/14938748/how-to-lazy-load-collection-when-using-spring-data-jpa-with-hibernate-from-an) – Deepak Agrawal Apr 30 '16 at 01:31

0 Answers0