Home > AI > Backend > SpringBoot >

Cascade.MERGE is final tested successfully

Chinese version

User和Role之间是多对多的关系,cascade选用Cascade.MERGE就行。

Role会有些初始数据在系统启动时存进去,也可以再添加。User可以和这些Role绑定关系。User删除时不会影响Role。

下面是实验的相关代码,应该可以运行。

bean / ForumPost.java

@Getter
@Setter
@ToString
@NoArgsConstructor
@Entity
@Table(name="forum_post")
public class ForumPost {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @ManyToMany(cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
    @JoinTable(
            name = "forum_post_tag",
            joinColumns = @JoinColumn(name = "post_id"),
            inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    private List<ForumTag> tags = new ArrayList<>();
}

bean/ForumTag.java

@Getter
@Setter
@ToString
@NoArgsConstructor
@Entity
@Table(name="forum_tag")
public class ForumTag {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    public ForumTag(String name){
        this.name = name;
    }
}

repository/ForumPostRepository.java

public interface ForumPostRepository extends JpaRepository<ForumPost, Long> {
    Optional<ForumPost> findByTitle(String title);

}

repository/ForumTagRepository.java

public interface ForumTagRepository extends JpaRepository<ForumTag, Long> {
    Optional<ForumTag> findByName(String name);
}

test/ForumTagRepositoryTest.java

@SpringBootTest
public class ForumTagRepositoryTest {

    @Autowired
    private ForumTagRepository tagRepository;

    @Autowired
    private ForumPostRepository postRepository;



    @Test
    void injectedComponentsAreNotNull(){
        assertNotNull(tagRepository);
        assertNotNull(postRepository);
    }


    private String tagName1 = "SpringBoot";
    private String tagName2 = "SwiftUI";
    private String tagName3 = "Java";
    private String postTitle1 = "Tutorial about Springboot";
    private String postTitle2 = "Tutorial about SwiftUI";
    private String postTitle3 = "Tutorial about Java";
    private ForumTag tag1 = new ForumTag(tagName1);
    private ForumTag tag2 = new ForumTag(tagName2);
    private ForumTag tag3 = new ForumTag(tagName3);



    private void detach() {
        // 1- detach
        if (!postRepository.findByTitle(postTitle1).isEmpty()) {
            ForumPost post1 = postRepository.findByTitle(postTitle1).get();
            post1.setTags(null);
            postRepository.save(post1);
        }
        if (!postRepository.findByTitle(postTitle2).isEmpty()) {
            ForumPost post2 = postRepository.findByTitle(postTitle2).get();
            post2.setTags(null);
            postRepository.save(post2);
        }
        if (!postRepository.findByTitle(postTitle3).isEmpty()) {
            ForumPost post3 = postRepository.findByTitle(postTitle3).get();
            post3.setTags(null);
            postRepository.save(post3);
        }
    }

    public void clearData() {
        // 1- detach
        detach();

        // 2- delete
        tagRepository.deleteAll();
        postRepository.deleteAll();
    }


    public void prepareData() {
        tagRepository.save(tag1);
        tagRepository.save(tag2);
        tagRepository.save(tag3);

        ForumPost post1 = new ForumPost();
        post1.setTitle(postTitle1);
        post1.setTags(Arrays.asList(tag1));

        ForumPost post2 = new ForumPost();
        post2.setTitle(postTitle2);
        post2.setTags(Arrays.asList(tag1, tag2));

        ForumPost post3 = new ForumPost();
        post3.setTitle(postTitle3);
        post3.setTags(Arrays.asList(tag1, tag2, tag3));

        postRepository.save(post1);
        postRepository.save(post2);
        postRepository.save(post3);
    }





    @Test
    public void test_save_tag_and_save_post() throws ResourceNotFoundException {

        clearData();

        // we have a few pre-saved tags
        tagRepository.save(tag1);
        tagRepository.save(tag2);
        tagRepository.save(tag3);

        assertEquals(tagRepository.findByName(tagName1).get().getName(), tagName1);
        assertEquals(tagRepository.findByName(tagName2).get().getName(), tagName2);
        assertEquals(tagRepository.findByName(tagName3).get().getName(), tagName3);

        // see if we can save these posts and connect with tags
        ForumPost post1 = new ForumPost();
        post1.setTitle(postTitle1);
        post1.setTags(Arrays.asList(tag1));

        ForumPost post2 = new ForumPost();
        post2.setTitle(postTitle2);
        post2.setTags(Arrays.asList(tag1, tag2));

        ForumPost post3 = new ForumPost();
        post3.setTitle(postTitle3);
        post3.setTags(Arrays.asList(tag1, tag2, tag3));

        postRepository.save(post1);
        postRepository.save(post2);
        postRepository.save(post3);

        assertEquals(postRepository.findByTitle(postTitle1).get().getTitle(), postTitle1);
        assertEquals(postRepository.findByTitle(postTitle1).get().getTags().size(), 1);
        assertEquals(postRepository.findByTitle(postTitle1).get().getTags().get(0).getName(), tagName1);


        assertEquals(postRepository.findByTitle(postTitle2).get().getTitle(), postTitle2);
        assertEquals(postRepository.findByTitle(postTitle2).get().getTags().size(), 2);
        assertEquals(postRepository.findByTitle(postTitle2).get().getTags().get(0).getName(), tagName1);
        assertEquals(postRepository.findByTitle(postTitle2).get().getTags().get(1).getName(), tagName2);


        assertEquals(postRepository.findByTitle(postTitle3).get().getTitle(), postTitle3);
        assertEquals(postRepository.findByTitle(postTitle3).get().getTags().size(), 3);
        assertEquals(postRepository.findByTitle(postTitle3).get().getTags().get(0).getName(), tagName1);
        assertEquals(postRepository.findByTitle(postTitle3).get().getTags().get(1).getName(), tagName2);
        assertEquals(postRepository.findByTitle(postTitle3).get().getTags().get(2).getName(), tagName3);
    }



    @Test
    public void test_save_post() throws ResourceNotFoundException {

        clearData();

        // let's save post directly to see how tags
        ForumPost post1 = new ForumPost();
        post1.setTitle(postTitle1);
        post1.setTags(Arrays.asList(tag1));

        ForumPost post2 = new ForumPost();
        post2.setTitle(postTitle2);
        post2.setTags(Arrays.asList(tag1, tag2));

        ForumPost post3 = new ForumPost();
        post3.setTitle(postTitle3);
        post3.setTags(Arrays.asList(tag1, tag2, tag3));


        Exception exception1 = assertThrows(InvalidDataAccessApiUsageException.class, () -> { postRepository.save(post1); });
        Exception exception2 = assertThrows(InvalidDataAccessApiUsageException.class, () -> { postRepository.save(post2); });
        Exception exception3 = assertThrows(InvalidDataAccessApiUsageException.class, () -> { postRepository.save(post3); });

        assertEquals("org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.example.spring_data_mysql.forum.bean.ForumTag",
                exception1.getMessage());
        assertEquals("org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.example.spring_data_mysql.forum.bean.ForumTag",
                exception2.getMessage());
        assertEquals("org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.example.spring_data_mysql.forum.bean.ForumTag",
                exception3.getMessage());

    }


    @Test
    public void test_delete_post_to_see_tag() {
        clearData();
        prepareData();

        // 1- delete post
        ForumPost post1 = postRepository.findByTitle(postTitle1).get();
        postRepository.deleteById(post1.getId());

        ForumPost post2 = postRepository.findByTitle(postTitle2).get();
        postRepository.deleteById(post2.getId());

        ForumPost post3 = postRepository.findByTitle(postTitle3).get();
        postRepository.deleteById(post3.getId());

        // 2- check tag
        assertEquals(tagRepository.findByName(tagName1).get().getName(), tagName1);
        assertEquals(tagRepository.findByName(tagName2).get().getName(), tagName2);
        assertEquals(tagRepository.findByName(tagName3).get().getName(), tagName3);
    }



    @Test
    public void test_delete_tag_to_see_post() {
        clearData();
        prepareData();

        // 1- delete tag
        ForumTag tag1 = tagRepository.findByName(tagName1).get();
        ForumTag tag2 = tagRepository.findByName(tagName2).get();
        ForumTag tag3 = tagRepository.findByName(tagName3).get();

        assertThrows(DataIntegrityViolationException.class, () -> { tagRepository.deleteById(tag1.getId()); });
        assertThrows(DataIntegrityViolationException.class, () -> { tagRepository.deleteById(tag2.getId()); });
        assertThrows(DataIntegrityViolationException.class, () -> { tagRepository.deleteById(tag3.getId()); });
    }


    @Test
    public void test_detach_post_then_delete_tag() {
        clearData();
        prepareData();
        detach();

        ForumTag tag1 = tagRepository.findByName(tagName1).get();
        tagRepository.deleteById(tag1.getId());

        ForumTag tag2 = tagRepository.findByName(tagName2).get();
        tagRepository.deleteById(tag2.getId());

        ForumTag tag3 = tagRepository.findByName(tagName3).get();
        tagRepository.deleteById(tag3.getId());

        // check post
        assertEquals(postRepository.findByTitle(postTitle1).get().getTitle(), postTitle1);
        assertEquals(postRepository.findByTitle(postTitle1).get().getTags().size(), 0);

        assertEquals(postRepository.findByTitle(postTitle2).get().getTitle(), postTitle2);
        assertEquals(postRepository.findByTitle(postTitle2).get().getTags().size(), 0);

        assertEquals(postRepository.findByTitle(postTitle3).get().getTitle(), postTitle3);
        assertEquals(postRepository.findByTitle(postTitle3).get().getTags().size(), 0);

    }
}

关于数据库测试讲下,是用真实数据库。测试了如下情况

Test caseStatus
Tag存好后,是否可以存Post通过
Tag没有数据,存Post时是否会把Tag也存进去不会,因为没有使用Cascade.PERSIST
是否可以单独删除Post,Tag不会影响通过
是否可以单独删除Tag不可以,要先接触相关Post的绑定,没有任何绑定了,才可以删除这个Tag

完整代码在这个仓库,https://github.com/tutehub/sample-spring/tree/develop

Leave a Reply