What is the difference between enitityManager.find and entityManager.createQuery?
Ok, I was using EJB 3.0 with hibernate, we dropped our .ear file into Easy-Beans 1.0.1 (with Hibernate) deploy directory embedded into Apache Tomcat 6.0.18. So my database had to persist things like this:
@Entity
@Table(name="AUTHOR")
public cla开发者_如何学JAVAss 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;
}
}
So when removing items I had an entity bean that went something like this:
@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();
}
}
}
Unfortunatelly, when my Servlet calls this bean it returns a "deleted entity passed to persist" exception; However changing the lines:
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)
makes the problem go away. The question I ask is why? In a similar situation I used em.createQuery
to retrieve and delete muliple entities and it worked without a hitch. So why won't it work now?
UPDATE: Calling
Query q=...
and then removing the BookAuthors removes the BookAuthors
from Books
but not from the Authors
. In second case it is removed from both sets. Both ba
have same hash and return true
when compared using baQuery.equals(baFind)
.
Any clarifications why two functions would return same object but calling remove
would behave differently depending on whether query
/find
is called?
Perhaps it's related to the lack of equals()
/hashCode()
in BookAuthor
. If so, it looks like in the case of query you have several different instances of BookAuthor
with the same state, so they are not removed from the corresponding sets in Author
and Book
.
As far as I know, Query.getSingleResult()
flushes the session under some circumstances, and I'm not sure aboutEntityManager.find()
. I would follow axtavt's suggestion and doublecheck if your entity is removed from sets properly. And also I would call this statement before em.remove(ba);
to make sure your ba
object was re-attached to the session:
ba = em.merge(ba);
精彩评论