有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我有两门课:书本课和会员课。在成员类中有两个存储书籍的哈希集:borrowedBooksreturnedBooks

当我从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) 个答案

  1. # 1 楼答案

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

    会议/跨城市边界

    您的控制器不是transactionnal(这是正常的)

    @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请求(调用您的控制器),有一个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