为什么hibernate重复行?

时间:2017-07-22 03:05:35

标签: postgresql hibernate jpa spring-boot spring-data

我正在使用Spring Boot starter spa,hibernate和Postgres来完成本教程,试图找出如何使用多对多关系跨越关系插入数据。我遵循了本教程:many-to-many tutorial但似乎在运行示例后,发布者表格为发布者a和发布者b提供了重复的发布者条目。谁能解释如何阻止这种情况发生?我已经读到了实施。实体的hashcode和equals方法可以解决这个问题,但是在为实体植入以下代码之后,我仍然会遇到同样的问题。

@Entity
public class Publisher {
    private int id;
    private String name;
    private Set<Book> books;

    public Publisher(){

    }

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

    public Publisher(String name, Set<Book> books){
        this.name = name;
        this.books = books;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @ManyToMany(mappedBy = "publishers")
    public Set<Book> getBooks() {
        return books;
    }

    public void setBooks(Set<Book> books) {
        this.books = books;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Publisher publisher = (Publisher) o;

        return name.equals(publisher.name);
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}

@Entity
public class Book {
    private int id;
    private String name;
    private Set<Publisher> publishers;

    public Book() {
    }

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

    public Book(String name, Set<Publisher> publishers){
        this.name = name;
        this.publishers = publishers;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "book_publisher",
            joinColumns = @JoinColumn(name = "book_id",
                    referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "publisher_id", referencedColumnName = "id"))
    public Set<Publisher> getPublishers() {
        return publishers;
    }

    public void setPublishers(Set<Publisher> publishers) {
        this.publishers = publishers;
    }

    @Override
    public String toString() {
        String result = String.format(
                "Book [id=%d, name='%s']%n",
                id, name);
        if (publishers != null) {
            for(Publisher publisher : publishers) {
                result += String.format(
                        "Publisher[id=%d, name='%s']%n",
                        publisher.getId(), publisher.getName());
            }
        }

        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Book book = (Book) o;

        return name.equals(book.name);
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}

select * from publisher

的输出
[
 {"id": 2,"name": "Publisher B"},
 {"id": 3,"name": "Publisher A"},
 {"id": 5,"name": "Publisher C"},
 {"id": 6,"name": "Publisher B"},
 {"id": 7,"name": "Publisher A"}
]

为什么id = 6且id = 7?

1 个答案:

答案 0 :(得分:2)

要回答您的问题,我们必须仔细查看触发对数据库的所有查询的public void run(String... strings)方法 - https://github.com/hellokoding/jpa-manytomany-springboot-hsql/blob/master/src/main/java/com/hellokoding/jpa/HelloJpaApplication.java

第35和49行之间有:

    final Publisher publisherA = new Publisher("Publisher A");
    final Publisher publisherB = new Publisher("Publisher B");
    final Publisher publisherC = new Publisher("Publisher C");

    bookRepository.save(new HashSet<Book>(){{
        add(new Book("Book A", new HashSet<Publisher>(){{
            add(publisherA);
            add(publisherB);
        }}));

        add(new Book("Book B", new HashSet<Publisher>(){{
            add(publisherA);
            add(publisherC);
        }}));
    }});

此部分负责保留前3个发布者:发布者A,发布者B和发布者C.首先Book,&#34;预订A&#34;创建发布者A和发布者B.并且因为这些发布者被分配给一个值,所以Hibernate将这些实体标记为&#34; persisted&#34;。感谢这一点&#34; Book B&#34;正在保存,只创建了Publisher C和&#34; Book B&#34;使用对发布者A的现有引用,并且不创建新对象。

现在让我们来看看第56和70行之间会发生什么:

    // save a couple of publishers
    final Book bookA = new Book("Book A");
    final Book bookB = new Book("Book B");

    publisherRepository.save(new HashSet<Publisher>() {{
        add(new Publisher("Publisher A", new HashSet<Book>() {{
            add(bookA);
            add(bookB);
        }}));

        add(new Publisher("Publisher B", new HashSet<Book>() {{
            add(bookA);
            add(bookB);
        }}));
    }});

在这里,我们可以看到相反的情况。此处Book对象与相关Publisher一起保存,而不是保存Publisher并将Book与每本书相关联。关键信息是通过publisherRepository.save()调用保存的两个发布者都是全新的对象 - 这两个对象都是使用Set.add()方法中的构造函数实例化的。这两个发布者是完全新的对象,它们只与Publisher A和Publisher B共享相同的名称。但是从对象标识的角度来看,它们不是同一个对象。这段代码还将创建两个Book对象。只有两个,因为两个发布者共享对同一本书A和书B对象的引用。

这就是为什么当你列出所有发布者时,你会得到这样的东西:

Publisher A    -|
Publisher C     |----- created in bookRepository.save() call
Publisher B    -|
Publisher A  -|----- created in publisherRepository.save() call
Publisher B  -| 

我希望它有所帮助。