簡體   English   中英

如何在多對多關系中的一個操作中保存多個實體 [Spring Boot 2,JPA,Hibernate,PostgreSQL]

[英]How to save multiple entity in one action related in Many to Many Relationship [Spring Boot 2, JPA, Hibernate, PostgreSQL]

我寫在這里是為了對解決方案有所了解,簡而言之,我面臨的問題是:我有兩個雙向多對多關系的實體,例如我有以下 Post 和 Tag 實體:

@Data
@Entity
@Table(name = "posts")
public class Post {


    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    /*...*/

    @ManyToMany( cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH} )
    @JoinTable(name = "post_tag", 
            joinColumns = @JoinColumn(name = "post_id", referencedColumnName = "id"), 
            inverseJoinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "id"))
    @JsonIgnoreProperties("posts")
    private Set<Tag> tags = new HashSet<>();

}

@Data
@Entity
@Table(name = "tags")
public class Tag {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;    

    @NaturalId
    private String text;

    @ManyToMany(mappedBy = "tags")//, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
    @JsonIgnoreProperties("tags")
    private Set<Post> posts = new HashSet<>();

}

我的問題是,在 HTTP POST 操作中,我獲取了帖子的數據和與之相關的標簽集合,如果“文本”已經存在於數據庫中,我必須保存所有條件以不復制標簽實體. 假設我們有一個帶有給定數據的 Map,代碼如下:

Post post = new Post();
String heading = (String) payload.get("heading");
String content = (String) payload.get("content");
post.setHeading(heading);
post.setContent(content);
Set<Tag> toSaveTags = new HashSet<Tag>();
List list = (List) payload.get("tags");
for (Object o : list) {
    Map map = (Map) o;
    String text = (String) map.get("text");
    Tag tag = new Tag();
    tag.setText(text);
    post.getTags().add(tag);
    tag.getPosts().add(post);
    log.info("post has {}# tag", post.getTags().size());
    toSaveTags.add(tag);
};
//method to save it all
postRepository.saveWithTags(post, toSaveTags);

我的解決方案是使用上述方法設計一個存儲庫 class,如下所示:

@Repository
public class PostTagsRepositoryImpl implements PostTagsRepository {

    @Autowired
    private EntityManagerFactory emf;

    @Override
    public Post saveWithTags(Post post, Collection<Tag> tags) {
        EntityManager entityManager = emf.createEntityManager();
        post.getTags().clear();
        for (Tag tag : tags) {

            tag.getPosts().clear();
            Tag searchedTag = null;
            try {
                searchedTag = entityManager.createQuery(
                        "select t "
                        + "from Tag t "
                        + "join fetch t.posts "
                        + "where t.text = :text", Tag.class)
                        .setParameter("text", tag.getText())
                        .getSingleResult();
            } catch (NoResultException e) {/* DO NOTHING */}
            if (searchedTag == null) {
                post.getTags().add(tag);
                tag.getPosts().add(post);
            } else {
                entityManager.getTransaction().begin();
                entityManager.detach(searchedTag);

                post.getTags().add(searchedTag);
                searchedTag.getPosts().add(post);
                entityManager.merge(searchedTag);
                entityManager.getTransaction().commit();
            }
        }

        entityManager.getTransaction().begin();
        entityManager.merge(post);
        entityManager.getTransaction().commit();
        return post;
    }

}

我的問題是:我可以更好地實施它嗎? 也許在一個查詢/交易中? 你能給我一些建議嗎?

幾點:

  • 您鏈接兩個實體,然后clear存儲庫中的關系。 由於您在它們之間沒有不變量,因此第一個鏈接是無用的。

  • 也許在一個查詢/交易中?

單個查詢實際上是不可能的,但單個事務確實是您需要實現的以避免不一致問題。

  • 不幸的是,級聯合並不適用於 naturalid,因此您必須自己產生這種行為。 所以對於每個標簽驗證它是否存在:
Session session = entityManager.unwrap(Session.class);

Tag t= session.bySimpleNaturalId(Tag.class).load(“some txt”);

根據結果,您必須將現有標簽一(已經在 db 上並通過bySimpleNaturalId恢復)或新標簽加載到Post object 中。 然后在Post上級聯合並將執行 rest。

  • 您總是在每次調用您的存儲庫時創建新的實體管理器。 您應該通過直接注入共享實體管理器來克服這個問題。
@Autowired
Private EntityManager em;

它是線程安全的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM