gpt4 book ai didi

java - 如果我删除同一类中另一个集合的所有元素,为什么 Hibernate 会删除一个集合中的所有元素?

转载 作者:塔克拉玛干 更新时间:2023-11-02 20:22:22 26 4
gpt4 key购买 nike

我有两个类(class):Book 和 Member。在 Member 类中有两个 HashSet 存储书籍:borrowedBooksreturnedBooks .

当我从 borrowedBooks 中删除一本书时并将其放入returnedBook , 除了 borrowedBooks 中的最后一个元素之外,Hibernate 将毫无问题地做到这一点。 .但是,如果我删除 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: -

这实在是无法理解!为什么会这样?非常感谢你的帮助!
这是我的类(class):
@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(...)有什么问题吗? ?
我花了很多时间,但我找不到办法......提前谢谢!

=================================
Hibernate 有问题!
例如:如果我有 3 本书在借来的图书中,并且我尝试归还它们:所以从借来的图书中删除并放入退回的图书。

第一次返回:

hibernate : 更新书 set amount=?, availableAmount=?, bookTitle=?, borrowedDate=?, description=?, addedDate=?, edition=?, isbn=?, issueDate=?, language=?, page=?, price=?, publisher =?、registrationDate=?、renewedDate=?、renewedTimes=?、status=?哪里条形码=?

hibernate : 插入成员(member)手册 (Member_id, returnedBooks_barcode) 值 (?, ?)

hibernate : 从 Member_Book 中删除成员ID =?和借来的Books_barcode=?

第二次回归:

hibernate : 更新书 set amount=?, availableAmount=?, bookTitle=?, borrowedDate=?, description=?, addedDate=?, edition=?, isbn=?, issueDate=?, language=?, page=?, price=?, publisher =?、registrationDate=?、renewedDate=?、renewedTimes=?、status=?哪里条形码=?

hibernate : 插入成员(member)手册 (Member_id, returnedBooks_barcode) 值 (?, ?)

hibernate : 从 Member_Book 中删除成员ID =?和借来的Books_barcode=?

第三次回归:

hibernate : 更新书 set amount=?, availableAmount=?, bookTitle=?, borrowedDate=?, description=?, addedDate=?, edition=?, isbn=?, issueDate=?, language=?, page=?, price=?, publisher =?、registrationDate=?、renewedDate=?、renewedTimes=?、status=?哪里条形码=?

hibernate : 插入成员(member)手册 (Member_id, returnedBooks_barcode) 值 (?, ?)

hibernate : 从 Member_Book 中删除成员ID =?

请查看“最后”删除操作: 从 Member_Book 中删除 Member_id=?
为什么只是“在哪里 Member_id=?” ???

最佳答案

不确定这是正确的答案,但您的代码中有两件事:

session /事务边界

您的 Controller 不是事务性的(没关系)。

@GetMapping("/bookBorrow")
public String bookBorrow(@RequestParam("barcode") long barcode, Model model) {
Book book = bookService.searchBookByBarcode(barcode);
memberService.returnBook(member, book);
//...

您的 memberService.returnBook() 是:
@Transactional
public void returnBook(Member member, Book book) {

因此我们可以推断,对于单个 HTTP 请求(调用您的 Controller ),为 bookService.searchBook... 打开了一个 hibernate session ,在 memberService.returnBook 打开了另一个 hibernate session 。

当您有两个共享相同实体的 hibernate session 时,会发生“有趣”的事情。您不应该使用从 book 内部的 bookService 获得的 memeberService 实例,至少在不重新附加它的情况下是这样。

(实际上我的建议是让你的整个 Controller 分派(dispatch)到一个事务和 hibernate session ,而不是两个,并且不必担心这样的事情)。

“好笑”是什么意思?主要问题是 hibernate 向您保证,在单个 session 中,它为您提供的持久对象(书、成员)的任何句柄都是相同的,如:它们是 == 。如果您在单个 session 中,则为 bookService.load(id) == bookService.load(id) 。在您当前的 Controller 实现中并非如此,因此对象可能相同也可能不同。

这是一种probelmatic,因为...问题2

HashCode/Equals 设计

您的 hashCode 和 equals 与合理的意图不一致。基本上你的书 hashCode 是条形码的散列。你的 equals 是条码上的 == 比较。我打赌你的意思是 .equals() ,但你写了“==”。

因此,除非两本书具有相同的条形码字符串实例(极不可能),否则它们永远不会相等。

那么会发生什么?

好吧,你在 hibernate session 中获取一本书,你会得到一个我称之为 Book@1 的实例,带有条形码“123”,这是一个我称之为 String@1 的字符串实例。

然后关闭 hibernate session ,然后进入另一个 session 。

在此 session 中,您加载了一个成员 Member@1,该成员有一组书籍,加载时 hibernate ,但您处于一个新 session 中。因此,hibernate 每次都会加载一个新的 book 实例,最终得到 Book 实例 Book@2,条形码为“123”,即 String@2(字符串包含相同的字符,它们是 .equals ,但它们不是 == )。

因此,您从 Member@1 的图书集中删除 Book@1。实现有什么作用?看起来 Book@2 和 Book@1 是否相同。 .hashcode() 明智的,他们是。 .equals() 明智吗?他们不是。 @Book1 不是 @Book2 并且不等于它,所以它不会被删除。

另一个后果是,无论您对 Book@1 所做的任何事情都不会被 hibernate 跟踪,因为创建 Book@1 的 session 已关闭。所以它实际上变成了一个灰色区域:如果将 Book@1 添加到 Member@1 会怎样。 hibernate怎么会知道它其实不是你刚刚创作的新书呢?它应该知道吗?

怎么办 ?

修正你的平等。你有一个书籍的“自然键”并使用它很好,但你可能使用了 ==,而你应该使用 .equals()
有适当的事务边界。如果您不花时间创建具有相同实体的不同实例的情况,Hibernate 会工作得更好。

用户正确命名:您所说的 MemberDAO 不是 DAO。 DAO 根据查询模式访问和存储对象。在这里,您的 MemberDAO 根据业务逻辑操作不同的实体(例如,如果我还书,则增加可用性计数器)。这不是 DAO 的工作,而是服务的工作。正确的命名通常会帮助您设置正确的事务边界,这将有助于正确的 session 划分。 (例如,在 DAO 实现中使用 @Transactionnal 通常是可疑的,除非你“偷工减料”。如果你编写一个非常简单的 CRUD 服务并且你想节省时间,但当业务逻辑潜入时,这是一个有@Transactionnal DAO 的代码气味)。

最后,使用逐步调试器。 Hibernate 通常不会发送与对象实例的状态不匹配的 SQL。

关于java - 如果我删除同一类中另一个集合的所有元素,为什么 Hibernate 会删除一个集合中的所有元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52383930/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com