java如果我删除同一类中另一个集合的所有元素,为什么Hibernate会删除集合中的所有元素?
我有两门课:书本课和会员课。在成员类中有两个存储书籍的哈希集:borrowedBooks
和returnedBooks
当我从borrowedBooks
中删除一本书并将其放入returnedBook
中时,Hibernate将完成这项工作,除了borrowedBooks
中的最后一个元素之外,其他所有元素都不会出现问题。但是,如果删除borrowedBooks
中的最后一个元素,Hibernate也会删除returnedBook
中的所有书籍。因此,在场景结束时,在borrowedBooks
中没有书籍,但在returnedBooks
中也没有书籍
例如:
1) borrowedBooks: a, b, c
1) returnedBooks:
---
2) borrowedBooks: a, b
2) returnedBooks: c
---
3) borrowedBooks: a
3) returnedBooks: b, c
---
4) borrowedBooks: -
4) returnedBooks: -
这真的不可理解!为什么会发生这种情况?非常感谢你的帮助! 以下是我的课程:
@Entity
public class Book {
@Id
@TableGenerator(...)
@GeneratedValue(generator = "Book_Barcode")
private long barcode;
private long isbn=0;
private String bookTitle="";
// some other fields
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (barcode ^ (barcode >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Book other = (Book) obj;
if (barcode != other.barcode)
return false;
return true;
}
}
@Entity
public class Member {
@Id
@TableGenerator(...)
@GeneratedValue(generator = "Member_Id")
private int id;
@OneToMany(fetch = FetchType.EAGER) //please ignore FetchType!
private Set<Book> returnedBooks = new HashSet<>();
@OneToMany(fetch = FetchType.EAGER)
private Set<Book> borrowedBooks = new HashSet<>();
@OneToMany(fetch = FetchType.EAGER)
private Set<Book> renewedBooks = new HashSet<>();
}
@Repository
public class MemberDAOImpl implements MemberDAO {
@Autowired
private SessionFactory sessionFactory;
@Override
@Transactional
public void borrowBook(Member member, Book book) {
if (book.getStatus().equals("Available") &&
book.getAvailableAmount() > 0) {
Session currentSession = sessionFactory.getCurrentSession();
book.setBorrowedDate(new Date());
book.setAvailableAmount(book.getAvailableAmount() - 1);
book.setStatus("Borrowed");
Member newMember = currentSession.find(Member.class, member.getId());
newMember.getBorrowedBooks().add(book);
}
}
@Override
@Transactional
public void returnBook(Member member, Book book) {
if (book.getStatus().equals("Borrowed")) {
Session currentSession = sessionFactory.getCurrentSession();
Member newMember = currentSession.find(Member.class, member.getId());
newMember.getReturnedBooks().add(book);
newMember.getBorrowedBooks().remove(book);
book.setBorrowedDate(null);
book.setAvailableAmount(book.getAvailableAmount() + 1);
book.setStatus("Available");
book.setRenewedTimes(0);
}
}
}
@Controller
@RequestMapping("/member")
public class MemberController {
static Member member;
@Autowired
private MemberService memberService;
@Autowired
private BookService bookService;
@GetMapping("/bookBorrow")
public String bookBorrow(@RequestParam("barcode") long barcode, Model model) {
Book book = bookService.searchBookByBarcode(barcode);
memberService.borrowBook(member, book);
model.addAttribute("booksList", member.getBorrowedBooks());
model.addAttribute("member", member);
return "member-specific-home-page";
}
@GetMapping("/bookReturn")
public String bookReturn(@RequestParam("barcode") long barcode, Model model) {
Book book = bookService.searchBookByBarcode(barcode);
memberService.returnBook(member, book);
model.addAttribute("booksList", member.getReturnedBooks());
model.addAttribute("member", member);
return "member-specific-home-page";
}
}
因此,我相信public void borrowBook(...)
没有问题。
在public void returnBook(...)
中有什么错误吗?
我花了很多时间,但我找不到一个方法。。。提前谢谢
================================ 冬眠有点问题! 例如:如果我的借书集中有3本书,并且如果我试图归还它们:那么从借书中移除并放入归还的书
首次返回:
休眠:更新书籍设置金额=?,availableAmount=?,书名=?,借入日期=?,description=?,编辑日期=?,版本=?,isbn=?,发布日期=?,语言=?,第页=?,价格=?,发布者=?,注册日期=?,更新日期=?,更新时间=?,状态=?其中条形码=
休眠:插入到成员书中(成员id,返回的图书条形码)值(?,)
休眠:从成员簿中删除其中成员id=?和借来的书_条形码=
第二次返回:
休眠:更新书籍设置金额=?,availableAmount=?,书名=?,借入日期=?,description=?,编辑日期=?,版本=?,isbn=?,发布日期=?,语言=?,第页=?,价格=?,发布者=?,注册日期=?,更新日期=?,更新时间=?,状态=?其中条形码=
休眠:插入到成员书中(成员id,返回的图书条形码)值(?,)
休眠:从成员簿中删除其中成员id=?和借来的书_条形码=
第三次返回:
休眠:更新书籍设置金额=?,availableAmount=?,书名=?,借入日期=?,description=?,编辑日期=?,版本=?,isbn=?,发布日期=?,语言=?,第页=?,价格=?,发布者=?,注册日期=?,更新日期=?,更新时间=?,状态=?其中条形码=
休眠:插入到成员书中(成员id,返回的图书条形码)值(?,)
休眠:从成员簿中删除其中成员id=
请查看“最后一次”删除操作:从Member_Book中删除,其中Member_id=强> 为什么只是“where Member_id=?”
# 1 楼答案
不确定这是否是正确答案,但代码中有两件事:
会议/跨城市边界
您的控制器不是transactionnal(这是正常的)
你的
memberService.returnBook()
是:因此,我们可以推断,对于单个HTTP请求(调用您的控制器),有一个hibernate会话在
bookService.searchBook...
中打开,另一个在memberService.returnBook
中打开当两个hibernate会话共享相同的实体时,“有趣”的事情就会发生。您不应该使用从
memeberService
内的bookService
获取的book
实例,至少在不重新连接它的情况下是这样(事实上,我的建议是让你的整个控制器调度到一个事务和hibernate会话,而不是两个,并且不必担心类似的事情)
“有趣”是什么意思?主要问题是,hibernate保证在单个会话中,它给持久对象(book、member)的任何句柄都是相同的,如:它们是
==
。如果您在一个会话中,那么bookService.load(id) == bookService.load(id)
。在当前的Controller实现中,情况并非如此,因此对象可能相同,也可能不同这有点问题,因为。。。问题2
HashCode/Equals设计
您的hashCode和equals与合理的意图不一致。基本上你的书
hashCode
是条形码的散列。您的等号是条形码上的==
比较。我打赌你的意思是.equals()
,但你写了“==”因此,除非两本书具有相同的条形码字符串实例(极不可能),否则它们永远不相等
那么会发生什么呢
在hibernate会话中获取一本书,然后返回一个我将调用的实例Book@1,带有条形码“123”,这是我将调用的字符串实例String@1.
然后关闭hibernate会话,然后进入另一个会话
在此会话中,您将加载一个成员,Member@1,其中有一组书籍,用于休眠加载,但您处于新会话中。所以hibernate每次都会加载一个新的book实例,最终得到book实例Book@2,带有条形码“123”,即String@2(字符串包含相同的字符,它们是
.equals
,但不是==
)所以你把它拿走了Book@1从Member@1这是我的书。实现的作用是什么?看来Book@2和Book@1都是一样的
.hashcode()
明智地说,他们是.equals()
明智吗?事实并非如此。@Book1不是@Book2,也不等于@Book2,因此它不会被删除另一个后果是,无论你做什么Book@1不会被hibernate跟踪,因为创建Book@1关门了。所以它实际上变成了一个灰色区域:如果你加上Book@1到Member@1.hibernate怎么知道它实际上不是您刚刚创建的新书?它应该知道吗
现在怎么办
修正你的平等。很好,你有一个书籍的“自然键”,你可以使用它,但是你可能在你应该使用的地方使用了
==
有适当的交易边界。如果您不花时间创建同一实体的不同实例的情况,Hibernate将工作得更好
用户正确命名:您所称的MemberDAO不是DAO。DAO根据查询模式访问和存储对象。这里,您的MemberDAO根据业务逻辑操作不同的实体(例如,如果我归还一本书,则增加一个可用性计数器)。这不是刀的工作,而是服务的工作。正确的命名通常会帮助您设置正确的事务边界,这将有助于正确的会话划分。(例如,除非你“偷工减料”,否则在DAO实现中使用@Transactionnal通常是可疑的。如果你编写了一个非常简单的CRUD服务,并且你想节省时间,但当业务逻辑潜入时,使用@Transactionnal DAO就会产生代码气味)
最后,使用逐步调试程序。冬季ate通常不会发送与对象实例状态不匹配的SQL