entityManager.find和entityManager.createQuery有什么区别?(问题涉及it技术)

3

好的,我使用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;
  }

}

因此,在删除项目时,我有一个实体bean,大致如下:

@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();
      }
   }
}

很不幸地,当我的Servlet调用这个bean时,它返回一个“传递已删除的实体以进行持久化”的异常;但是如果更改以下行:

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

to

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

我问的问题是为什么?在类似的情况下,我使用em.createQuery检索和删除多个实体时,它运行得非常顺利。那为什么现在不起作用呢?更新:调用Query q = ... 然后删除BookAuthors将从Books中删除BookAuthors,但未从 Authors中删除,而第二种情况会从两个集合中删除。两个ba具有相同的哈希值,并在使用baQuery.equals(baFind)进行比较时返回true

请说明为什么两个函数将返回相同的对象,但调用remove会根据调用query/find而表现出不同的行为?


2
我喜欢这些名字 - Biblioteka,Knjiga :-) - Denys Kniazhev-Support Ukraine
塞尔维亚人的名字,这意味着有些名字没有翻译:哦,你来自乌克兰。说得通。顺便提一下,谢谢你的提醒。 - Daniel Fath
无论如何 - 这行代码的意思是什么:em.remove(ak);,你从哪里得到了变量 ak?这是一个打字错误,你是不是想用 ba - Denys Kniazhev-Support Ukraine
是的(AutorKnjige ak -> BookAuthor ba)。很好的发现。 - Daniel Fath
2个回答

3
也许这与BookAuthor中缺少equals()/hashCode()有关。如果是这样的话,在查询的情况下,你可能有几个不同的BookAuthor实例具有相同的状态,因此它们不会从AuthorBook的相应集合中移除。

equalshashCodetoString存在于原始类中,以及getters/setters/constructor。我认为它们对于审核不重要,所以我跳过了它们。equalshashCode仅比较它们的键。因此,在Author中,equals仅比较id。 - Daniel Fath
@Daniel:不管怎样,你确定BookAuthor确实从集合中被移除了吗? - axtavt
1
通常认为在equals和hashCode中使用IDs是一种不好的做法——如果您的对象未附加到会话中,则IDs可能为0或null。我总是使用“自然”键。对于作者来说,这将是名字姓氏的组合。有关更多参考,请查看此问题:https://dev59.com/M3VD5IYBdhLWcg3wTJrF - Denys Kniazhev-Support Ukraine
@denis_k:谢谢,我不知道这个。因为这是作业,我们被建议使用ID在等号中,以确保对象相等=实体相等。另外,自然键在EJB中更难实现,在作者的情况下,有时现实生活中不同的人有相同的姓/名。@axtavt:好的,我今晚稍后尝试一下。 BookAuthor 确实没有继承,所以不应该有问题。 - Daniel Fath
@axtavt:instanceof 似乎不起作用。问题仍然存在。所有我的实体 bean 都是独立的,但第一种情况下它仍然无法删除 BookAuthor,原因不明。还是感谢您的帮助。 - Daniel Fath
显示剩余5条评论

1
据我所知,Query.getSingleResult() 在某些情况下会刷新会话,并且我不确定EntityManager.find()是否也会这样。我建议您遵循axtavt的建议,仔细检查实体是否已正确地从集合中移除。此外,在调用em.remove(ba);之前,我建议您先调用以下语句,以确保您的ba对象已重新附加到会话中:

ba = em.merge(ba);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接