Пакетная вставка Hibernate 4.3 Spring 4 со связующим столом
Позвольте мне начать с того, что я не программист на Java, но я пытаюсь изучить некоторые новые (для меня) вещи.
У меня есть два следующих DAO: Полка
package org.sample.bookshelf.model;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="shelves")
public class Shelf {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(cascade=CascadeType.ALL)
@JoinTable(name="shelf_books",
joinColumns = {@JoinColumn(name="shelf_id", referencedColumnName="id")},
inverseJoinColumns = {@JoinColumn(name="book_id", referencedColumnName="id")}
)
private Set<Book> shelfBooks;
public Shelf() {}
public Shelf(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Book> getShelfBooks() {
return shelfBooks;
}
public void setShelfBooks(Set<Book> shelfBooks) {
this.shelfBooks = shelfBooks;
}
}
и книга
package org.sample.bookshelf.model;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity
@Table(name="books")
public class Book {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String title;
@OneToOne(cascade=CascadeType.ALL)
@JoinTable(name="shelf_books",
joinColumns = {@JoinColumn(name="book_id", referencedColumnName="id")},
inverseJoinColumns = {@JoinColumn(name="shelf_id", referencedColumnName="id")}
)
private Shelf block;
public Book() {
}
public Book(String title)
{
this.title = title;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Shelf getShelf() {
return block;
}
public void setShelf(Shelf block) {
this.block = block;
}
}
как вы можете видеть, они связаны таблицей: *shelf_books*
Теперь в BookDaoImpl я хотел иметь метод с поведением " все или ничего", который позволил бы мне сохранять несколько книг, он выглядит следующим образом:
@Override
public void saveMulti(List<Book> books) {
Session sess = sessionFactory.openSession();
org.hibernate.Transaction tx = null;
try {
tx = sess.beginTransaction();
int i = 0;
for (Book b : books) {
logger.debug(String.format("%d %s ", b.getId(), b.getTitle()));
sess.saveOrUpdate(b);
i++;
if (i == 20) {
sess.flush();
sess.clear();
i = 0;
}
}
logger.debug("saving...");
tx.commit();
logger.debug("done...");
} catch (RuntimeException e) {
if (tx != null) tx.rollback();
e.printStackTrace();
} finally {
sess.close();
}
}
По какой-то причине данные в таблицу ассоциаций добавляются, а затем удаляются, это видно в следующем журнале, содержащем коллекцию удаления org.sample.bookshelf.model.Shelf.shelfBooks:
16:08:13,857 DEBUG BookDaoImpl:58 - saving...
16:08:13,857 DEBUG AbstractTransactionImpl:175 - committing
16:08:13,857 DEBUG AbstractFlushingEventListener:149 - Processing flush-time cascades
16:08:13,857 DEBUG AbstractFlushingEventListener:189 - Dirty checking collections
16:08:13,859 DEBUG AbstractFlushingEventListener:123 - Flushed: 0 insertions, 1 updates, 0 deletions to 11 objects
16:08:13,859 DEBUG AbstractFlushingEventListener:130 - Flushed: 0 (re)creations, 0 updates, 1 removals to 0 collections
16:08:13,859 DEBUG EntityPrinter:114 - Listing entities:
16:08:13,860 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=2, title=The City of the Sun, block=org.sample.bookshelf.model.Shelf#1}
16:08:13,860 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Shelf{id=1, shelfBooks=null, name=dystopia}
16:08:13,860 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=1, title=1984, block=org.sample.bookshelf.model.Shelf#1}
16:08:13,860 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=6, title=Logan's Run, block=org.sample.bookshelf.model.Shelf#1}
16:08:13,860 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=5, title=Slaughterhouse Five, block=org.sample.bookshelf.model.Shelf#1}
16:08:13,860 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=4, title=Fahrenheit 451, block=org.sample.bookshelf.model.Shelf#1}
16:08:13,861 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=3, title=The Honeymoon Trip of Mr. Hamilton, block=org.sample.bookshelf.model.Shelf#1}
16:08:13,861 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=10, title=Brave New World, block=org.sample.bookshelf.model.Shelf#1}
16:08:13,861 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=9, title=Lord of the Flies, block=org.sample.bookshelf.model.Shelf#1}
16:08:13,861 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=8, title=Do Androids Dream of Electric Sheep?, block=org.sample.bookshelf.model.Shelf#1}
16:08:13,861 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=7, title=The Futurological Congress, block=org.sample.bookshelf.model.Shelf#1}
16:08:13,867 DEBUG SQL:109 - /* update org.sample.bookshelf.model.Shelf */ update shelves set name=? where id=?
16:08:13,869 DEBUG AbstractCollectionPersister:1174 - Deleting collection: [org.sample.bookshelf.model.Shelf.shelfBooks#1]
16:08:13,869 DEBUG SQL:109 - /* delete collection org.sample.bookshelf.model.Shelf.shelfBooks */ delete from shelf_books where shelf_id=?
16:08:13,870 DEBUG AbstractCollectionPersister:1232 - Done deleting collection
16:08:14,199 DEBUG JdbcTransaction:113 - committed JDBC Connection
16:08:14,199 DEBUG JdbcTransaction:126 - re-enabling autocommit
16:08:14,200 DEBUG BookDaoImpl:60 - done...
Я думаю, по какой-то причине Шельф не знает о добавленных книгах, чтобы завершить, вот как я добавляю данные:
String titles[] = {
"1984",
"The City of the Sun",
"The Honeymoon Trip of Mr. Hamilton",
"Fahrenheit 451",
"Slaughterhouse Five",
"Logan's Run",
"The Futurological Congress",
"Do Androids Dream of Electric Sheep?",
"Lord of the Flies",
"Brave New World",
};
Vector<Book> books = new Vector<Book>(titles.length);
for (int i=0; i<titles.length; ++i) {
Book book = new Book(titles[i]);
book.setShelf(shelf);
books.add(book);
}
bookDao.saveMulti(books);
как вы можете видеть, я нигде не вызываю shelf.setShelfBooks. Если я сделаю это, все будет в порядке, но то, что делает hibernate, выглядит плохо, посмотрите на фрагмент следующего журнала:
16:20:15,015 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=8, title=Do Androids Dream of Electric Sheep?, block=org.sample.bookshelf.model.Shelf#1}
16:20:15,015 DEBUG EntityPrinter:121 - org.sample.bookshelf.model.Book{id=7, title=Fahrenheit 451, block=org.sample.bookshelf.model.Shelf#1}
16:20:15,021 DEBUG SQL:109 - /* update org.sample.bookshelf.model.Shelf */ update shelves set name=? where id=?
16:20:15,023 DEBUG AbstractCollectionPersister:1174 - Deleting collection: [org.sample.bookshelf.model.Shelf.shelfBooks#1]
16:20:15,023 DEBUG SQL:109 - /* delete collection org.sample.bookshelf.model.Shelf.shelfBooks */ delete from shelf_books where shelf_id=?
16:20:15,026 DEBUG AbstractCollectionPersister:1232 - Done deleting collection
16:20:15,027 DEBUG AbstractCollectionPersister:1256 - Inserting collection: [org.sample.bookshelf.model.Shelf.shelfBooks#1]
16:20:15,028 DEBUG SQL:109 - /* insert collection row org.sample.bookshelf.model.Shelf.shelfBooks */ insert into shelf_books (shelf_id, book_id) values (?, ?)
16:20:15,030 DEBUG SQL:109 - /* insert collection row org.sample.bookshelf.model.Shelf.shelfBooks */ insert into shelf_books (shelf_id, book_id) values (?, ?)
16:20:15,030 DEBUG SQL:109 - /* insert collection row org.sample.bookshelf.model.Shelf.shelfBooks */ insert into shelf_books (shelf_id, book_id) values (?, ?)
У меня есть чувство, что я делаю это неправильно, может кто-нибудь сказать мне, что должно быть правильно, чтобы делать то, что я хотел бы?
1 ответ
Я не пошел дальше, чем две сущности, потому что их отображение неверно, что, вероятно, является причиной ваших проблем.
Первая проблема:
Вы объявляете книги как
@OneToMany(cascade=CascadeType.ALL)
@JoinTable(name="shelf_books",
joinColumns = {@JoinColumn(name="shelf_id", referencedColumnName="id")},
inverseJoinColumns = {@JoinColumn(name="book_id", referencedColumnName="id")}
)
private Set<Book> shelfBooks;
Это означает, что существует ассоциация OneToMany, отображаемая таблицей соединений.
Затем вы заявляете следующее:
@OneToOne(cascade=CascadeType.ALL)
@JoinTable(name="shelf_books",
joinColumns = {@JoinColumn(name="book_id", referencedColumnName="id")},
inverseJoinColumns = {@JoinColumn(name="shelf_id", referencedColumnName="id")}
)
private Shelf block;
Это не имеет смысла.
Вы, вероятно, хотите, чтобы ассоциация была двунаправленной. Так как это OneToMany с одной стороны, это должно быть ManyToOne с другой стороны. А в двунаправленной ассоциации одна сторона ДОЛЖНА быть обратной стороной (используя атрибут mappedBy, а не объявлять, как отображается сопоставление, поскольку она уже определена на другой стороне), и одна сторона ДОЛЖНА быть стороной владельца. В ассоциации OneToMany обратная сторона ДОЛЖНА быть одной стороной. Таким образом, набор книг должен быть объявлен как:
@OneToMany(cascade = CascadeType.ALL, mappedBy = "block")
private Set<Book> shelfBooks;
Также обратите внимание, что использование CascadeType.ALL для ManyToOne не имеет особого смысла. Вы не хотите удалять полку, как только одна из содержащихся в ней книг будет удалена. Я бы посоветовал удалить все каскады, если вы действительно не понимаете, каковы последствия.