Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/353.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring和Hibernate web应用程序:如何解决“org.Hibernate.LazyInitializationException”?_Java_Sql_Spring_Hibernate_Lazy Loading - Fatal编程技术网

Java Spring和Hibernate web应用程序:如何解决“org.Hibernate.LazyInitializationException”?

Java Spring和Hibernate web应用程序:如何解决“org.Hibernate.LazyInitializationException”?,java,sql,spring,hibernate,lazy-loading,Java,Sql,Spring,Hibernate,Lazy Loading,我使用的是Spring4和Hibernate4,其中我有两个类,Person和Book,它们映射到数据库中的两个表。人与书之间的关系为1:M,即一个人可以有许多书 Person表中的关系定义为: @OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name="PERSON_ID") private List<Book> books; 使用适当的getter和setter方法 但在我为一个人展

我使用的是Spring4和Hibernate4,其中我有两个类,Person和Book,它们映射到数据库中的两个表。人与书之间的关系为1:M,即一个人可以有许多书

Person表中的关系定义为:

@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name="PERSON_ID")
private List<Book> books;
使用适当的getter和setter方法

但在我为一个人展示书籍的方法中:

// Calls books.jsp for a Person.
@RequestMapping(value = "/books", method = RequestMethod.GET)
public String listBooks(@RequestParam("id") String id, 
                        Model model) {    
    logger.info(PersonController.class.getName() + ".listBooks() method called.");                 

    Person person = personService.get(Integer.parseInt(id));        
    // List<Book> books = bookService.listBooksForPerson(person.getId());

    // Set view.
    model.addAttribute("person", person);
    // model.addAttribute("books", books);
    return "view/books";
}
所以我有两个选择:

我可以使用快速抓取-这确实有效,但忽略了整个过程 惰性抓取原则。 解决问题。这是我的首选,因为这是我和其他人学习的应用程序。 如果这个问题的答案是在服务或DAO代码中指定/使用事务,那么通常我的数据库更新在服务类中被注释为@TRANSACTIONAL。并实施为:

 @Override
public void insert(Person person)  {
    logger.info(PersonDAOImpl.class.getName() + ".insert() method called.");

    Session session = sessionFactory.openSession();
    Transaction transaction = session.getTransaction();        
    try {
        transaction.begin();
        session.save(person);
        transaction.commit();            
    }
    catch(RuntimeException e) {
        transaction.rollback();
        throw e;
    }
    finally {
        session.close();
    }
}
My DAO get方法在其SQL中不使用联接,例如:

@Override
public List<Book> listBooksForPerson(Integer id) {
    logger.info(BookDAOImpl.class.getName() + ".listBooksForPerson() method called.");

    Session session = sessionFactory.openSession();          
    try {    
        Query query = session.createQuery("FROM Book WHERE PERSON_ID = " + id);  
        return query.list();                                  
    }        
    finally {
        session.close();
    }         
}
但如何将查询的输出映射到Person所在的Person对象类型:

// Attributes.    
@Id
@Column(name = "ID", unique = true, nullable = false)    
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;

@Column(name = "NAME", nullable = false, length=50)      
private String name;

@Column(name = "ADDRESS", nullable = false, length=100)
private String address;

@Column(name = "TELEPHONE", nullable = false, length=10)
private String telephone;

@Column(name = "EMAIL", nullable = false, length=50)
private String email;

@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY) 
@JoinColumn(name="PERSON_ID")
private List<Book> books;
其中已删除上面的listBooksForPerson方法


但可以说,我仍然使用表之间关系的一端作为所有者。Book表现在不再使用@ManyToOne标记或Person实例。这是使延迟抓取最终看起来有效的唯一方法。

正如您可能已经知道的,问题是一旦将实体发送到您的视图,它就会与EntityManager分离,因此延迟加载在此时将不起作用。如果尝试在listBooks中使用personService.get,则在加载书籍之前,检索到的人员将与EntityManager分离。因此,当您试图访问视图中的书籍时,您将得到该错误

答案是确保在分离实体之前将视图所需的数据加载到实体中。不要使用personService.get;而是使用HQL查询和联接获取语法来加载所需的惰性字段。尝试以下方法:

SELECT p FROM Person p LEFT JOIN FETCH p.books WHERE p.id = ...
在这种情况下,将加载书籍,您可以在视图中使用它

您还可以级联连接回迁。例如,如果这本书包含一个你需要处理的章节的惰性集合,你可以使用:

SELECT p FROM Person p LEFT JOIN FETCH p.books book LEFT JOIN FETCH book.chapters WHERE p.id = ...

面对面,所有书籍和这些书籍中的所有章节都将可供查看。

如下:让我看看我是否正确理解了这一点:我可以通过服务从控制器调用PersonDAO方法,其中,我执行“SELECT p FROM Person p LEFT JOIN FETCH p.books WHERE p.id=…”,一旦该数据被检索到控制器中,它就会通过指定页面名被简单地写入视图?@MrMorgan:我不确定您简单地写入视图是什么意思,但它可以被视图访问,特别是books.jsp。我想您有代码在那里迭代Person.books以生成列表。这段代码是您的错误的来源,现在可以工作了。我已经尝试了我认为您的意思,并修改了问题,以显示一个由于语法原因导致SQL错误的方法,尽管我不确定为什么。但是你是否建议我的person.get方法可以使用SQL SELECT p FROM person p LEFT JOIN FETCH p.books其中p.id=…?@MrMorgan:你发布的代码中甚至没有使用listBooksForPerson,它被注释掉了,所以我不确定你为什么要修改它……我尝试过从Person控制器调用listBooksForPerson的新版本,但它不适用于惰性抓取。所以我要修改getPerson,这样它就可以使用一个SQL连接Book,一次就为这个人获取书籍。这个人有一本书的列表。但这可以等到明天,这里是黎明。谢谢你的帮助,我明天会更新这个问题。
@Override
public Person getPersonAndBooks(Integer id) {
    logger.info(PersonDAOImpl.class.getName() + ".listBooksForPerson() method called.");

    Session session = sessionFactory.openSession();          
    try {    

        SQLQuery query = session.createSQLQuery("SELECT * FROM Person JOIN Book ON Person.ID = book.PERSON_ID AND Person.ID = " + id);  

        // HOW TO GET PERSON OBJECT HERE WITH BOOKS?

    }        
    finally {
        session.close();
    }         
}
// Attributes.    
@Id
@Column(name = "ID", unique = true, nullable = false)    
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;

@Column(name = "NAME", nullable = false, length=50)      
private String name;

@Column(name = "ADDRESS", nullable = false, length=100)
private String address;

@Column(name = "TELEPHONE", nullable = false, length=10)
private String telephone;

@Column(name = "EMAIL", nullable = false, length=50)
private String email;

@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY) 
@JoinColumn(name="PERSON_ID")
private List<Book> books;
@Override
public Person get(Integer personId) {
    logger.info(PersonDAOImpl.class.getName() + ".get() method called.");

    Session session = sessionFactory.openSession();

    try {                         
        Query query = session.createQuery(" from Person as p left join fetch p.books where p.personId = :personId").setParameter("personId", personId);                      
        Person person = (Person) query.list().get(0); 
        Hibernate.initialize(person.getBooks());            
        return person;

        // return (Person) session.get(Person.class, personId);
    }        
    finally {
        session.close();            
    }         
}
SELECT p FROM Person p LEFT JOIN FETCH p.books WHERE p.id = ...
SELECT p FROM Person p LEFT JOIN FETCH p.books book LEFT JOIN FETCH book.chapters WHERE p.id = ...