В чем разница между enitityManager.find и entityManager.createQuery? - PullRequest
3 голосов
/ 02 декабря 2010

Хорошо, я использовал EJB 3.0 с Hibernate, мы поместили наш файл .ear в каталог развертывания Easy-Beans 1.0.1 (с Hibernate), встроенный в Apache Tomcat 6.0.18. Поэтому моя база данных должна была сохранять такие вещи:

@Entity
@Table(name="AUTHOR")
public class Author implements Serializable {

//////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "A_ID", unique=true, nullable = false)
private Integer id;

@Column (name = "A_NAME", unique = false, nullable = false)
private String name;

@Column (name = "A_LASTNAME", unique = false, nullable = false)
private String lastname;

@OneToMany(cascade = {ALL}, fetch = EAGER, mappedBy = "author")
private Set<BookAuthor> bookAuthors = new HashSet<BookAuthor>();

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

      Author author = (Author) o;

      if (id != null ? !id.equals(author.id) : author.id != null) return false;

      return true;
  }

  @Override
  public int hashCode() {
      return id != null ? id.hashCode() : 0;
  }  
}

@Entity
@Table(name = "BOOK" )
public class Book implements Serializable {
//////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////
@Id
@GeneratedValue (strategy = IDENTITY)
@Column(name = "B_ID", unique = true, nullable = false)
private Integer bid;

@Column(name = "B_YEAR", unique = false, nullable = true)
private Integer year;

@Column(name = "B_ISBN", unique = false, nullable = false)
private String isbn;

@Column(name = "B_TITLE", unique = false, nullable = false)
private String title;

@OneToMany(cascade = {ALL}, fetch = EAGER, mappedBy = "book")
private Set<BookAuthor> bookAuthors = new HashSet<BookAuthor>();

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

    Book book = (Book) o;

    if (isbn != null ? !isbn.equals(book.isbn) : book.isbn != null) return false;
    if (bid != null ? !kid.equals(book.bid) : book.bid != null) return false;

    return true;
  }

  @Override
  public int hashCode() {
    int result = bid != null ? bid.hashCode() : 0;
    result = 31 * result + (isbn != null ? isbn.hashCode() : 0);
    return result;
  }

}

@Entity
@Table(name = "BOOK_AUTHOR")
public class BookAuthor implements Serializable {

  //////////////////////////////////////////////////////////////////
  // Fields
  //////////////////////////////////////////////////////////////////
  @Id
  @GeneratedValue(strategy = IDENTITY)
  @Column(name = "BA_ID", unique=true, nullable = false)
  private Integer id;

  @Column(name = "BA_ROLE", unique = false, nullable = true)
  private String role;

  @ManyToOne
  @JoinColumn (name = "A_ID", referencedColumnName = "A_ID", nullable = false)
  private Author author;

  @ManyToOne
  @JoinColumn (name = "B_ID", referencedColumnName = "B_ID", nullable = false)
  private Book book;

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

    BookAuthor that = (BookAuthor) o;

    if (auhtor != null ? !author.equals(that.author) : that.author != null) return false;
    if (book != null ? !book.equals(that.book) : that.book!= null) return false;
    if (id != null ? !id.equals(that.id) : that.id != null) return false;

    return true;
  }

  @Override
  public int hashCode() {
    int result = id != null ? id.hashCode() : 0;
    result = 31 * result + (author != null ? author.hashCode() : 0);
    result = 31 * result + (book != null ? book.hashCode() : 0);
    return result;
  }

}

Итак, при удалении предметов у меня был бин сущности, который выглядел примерно так:

@Stateless
@Local(DeleteBookAuthor.class)
public class DeleteBookAuthorBean implements DeleteBookAuthor
{

   @PersistenceContext(unitName="Library")
   protected EntityManager em;

   @Override
   public void removeById(Integer id) {
      try{

        Query q = em.createQuery("SELECT ba FROM BookAuthor ba WHERE id = ?1");
        q.setParameter(1,id);

        BookAuthor ba = (BookAuthor) q.getSingleResult();

        ba.getAuthor().getBookAuthors().remove(ba);
        ba.getBook().getBookAuthors().remove(ba);

        em.remove(ba);
      }catch (Exception e){
         e.printStackTrace();
      }
   }
}

К сожалению, когда мой сервлет вызывает этот бин, он возвращает исключение «удаленная сущность передана для сохранения»; Однако меняются строки:

Query q = em.createQuery("SELECT ba FROM BookAuthor ba WHERE id = ?1");
q.setParameter(1,id);        
BookAuthor ba = (BookAuthor) q.getSingleResult(); 

до

BookAuthor ba = em.find(BookAuthor.class, id)

устраняет проблему. Вопрос, который я задаю, почему? В аналогичной ситуации я использовал em.createQuery для извлечения и удаления нескольких объектов, и это работало без проблем. Так почему же это не сработает сейчас?

ОБНОВЛЕНИЕ: Звонок Query q=..., а затем удаление BookAuthors удаляет BookAuthors из Books, но не из Authors. Во втором случае он удаляется из обоих наборов. Оба ba имеют одинаковый хэш и возвращают true при сравнении с использованием baQuery.equals(baFind).

Любые пояснения, почему две функции возвращают один и тот же объект, но вызов remove будет вести себя по-разному в зависимости от того, вызывается ли query / find?

Ответы [ 2 ]

3 голосов
/ 02 декабря 2010

Возможно, это связано с отсутствием equals() / hashCode() в BookAuthor.Если это так, похоже, что в случае запроса у вас есть несколько разных экземпляров BookAuthor с одним и тем же состоянием, поэтому они не удаляются из соответствующих наборов в Author и Book.

1 голос
/ 03 декабря 2010

Насколько я знаю, Query.getSingleResult() сбрасывает сессию при некоторых обстоятельствах, и я не уверен насчет EntityManager.find(). Я бы последовал совету Axtavt и дважды проверил бы, правильно ли ваша сущность удалена из сетов. А также я бы вызвал этот оператор до em.remove(ba);, чтобы убедиться, что ваш ba объект был повторно присоединен к сеансу:

ba = em.merge(ba);
...