Spring MVC + Hibernate:无法初始化代理 - 没有Session

And*_*708 5 spring hibernate spring-mvc

注意:请参阅我自己对此问题的回答,以获取我如何解决此问题的示例.

我在Spring MVC 4 + Hibernate 4项目中遇到以下异常:

org.hibernate.LazyInitializationException:懒得初始化一个角色集合:com.mysite.Company.acknowledgements,无法初始化代理 - 没有Session

在阅读了很多关于这个问题的其他问题后,我理解为什么会发生这种异常,但我不确定如何以一种好的方式解决它.我正在做以下事情:

  1. 我的Spring MVC控制器调用服务中的方法
  2. service方法调用DAO类中的方法
  3. DAO类中的方法通过Hibernate获取实体对象并将其返回给调用服务
  4. 该服务将获取的对象返回给控制器
  5. 控制器将对象传递给视图(JSP)
  6. 该视图尝试迭代一个懒惰加载的多对多关联(因此是一个代理对象)
  7. 抛出异常是因为此时会话已关闭,并且代理无法加载关联数据

我之前使用过PHP和doctrine2,这种做事方式没有问题.我试图找出解决这个问题的最佳方法,因为到目前为止我找到的解决方案看起来并不那么好:

  • 急切加载关联,可能会加载大量不必要的数据
  • 调用Hibernate.initialize(myObject.getAssociation());- 这意味着我必须遍历关联来初始化它们(我猜),而且事实上我必须这样做,有点使得延迟加载不那么整洁
  • 使用Spring过滤器在视图中打开会话,但我怀疑这是一件好事吗?

我试着@Transactional在我的服务中使用,但没有运气.这是有道理的,因为我试图访问我的服务方法返回尚未加载的数据.理想情况下,我希望能够从我的视图中访问任何关联.我想在我的服务中初始化关联的缺点是我必须明确定义我需要的数据 - 但这取决于使用服务的上下文(控制器).我不确定我是否可以在我的控制器中执行此操作而不会丢失DBAL层提供的抽象.我希望这是有道理的.无论哪种方式,如果我不必总是明确定义我想要哪些数据可用于我的视图,那就太好了,但只是让视图做它的事情.如果那是不可能的,那么我只是在寻找最优雅的解决方案.

以下是我的代码.

视图

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<h1><c:out value="${company.name}" /> (ID: <c:out value="${company.id}" />)</h1>

<c:forEach var="acknowledgement" items="${company.acknowledgements}">
    <p><c:out value="${acknowledgement.name}" /></p>
</c:forEach>
Run Code Online (Sandbox Code Playgroud)

调节器

@Controller
public class ProfileController {
    @Autowired
    private CompanyService companyService;

    @RequestMapping("/profile/view/{id}")
    public String view(Model model, @PathVariable int id) {
        Company company = this.companyService.get(id);
        model.addAttribute("company", company);

        return "viewCompanyProfile";
    }
}
Run Code Online (Sandbox Code Playgroud)

服务

@Service
public class CompanyServiceImpl implements CompanyService {
    @Autowired
    private CompanyDao companyDao;

    @Override
    public Company get(int id) {
        return this.companyDao.get(id);
    }
}
Run Code Online (Sandbox Code Playgroud)

DAO

@Repository
@Transactional
public class CompanyDaoImpl implements CompanyDao {
    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public Company get(int id) {
        return (Company) this.sessionFactory.getCurrentSession().get(Company.class, id);
    }
}
Run Code Online (Sandbox Code Playgroud)

公司实体

@Entity
@Table(name = "company")
public class Company {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    // Other fields here

    @ManyToMany
    @JoinTable(name = "company_acknowledgement", joinColumns = @JoinColumn(name = "company_id"), inverseJoinColumns = @JoinColumn(name = "acknowledgement_id"))
    private Set<Acknowledgement> acknowledgements;

    public Set<Acknowledgement> getAcknowledgements() {
        return acknowledgements;
    }

    public void setAcknowledgements(Set<Acknowledgement> acknowledgements) {
        this.acknowledgements = acknowledgements;
    }

    // Other getters and setters here
}
Run Code Online (Sandbox Code Playgroud)

确认实体

@Entity
public class Acknowledgement {
    @Id
    private int id;

    // Other fields + getters and setters here

}
Run Code Online (Sandbox Code Playgroud)

mvc-dispatcher-servlet.xml(它的一部分)

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan">
            <list>
                <value>com.mysite.company.entity</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <tx:annotation-driven />
Run Code Online (Sandbox Code Playgroud)

提前致谢!

小智 10

正如@ Predrag Maric所说,编写具有不同初始化实体关联的服务方法可能是一种选择.

OpenSessionInView是一个退出讨论模式,但在您的情况下可以是一个解决方案.

另一个选择是设置

<prop key="hibernate.enable_lazy_load_no_trans">true</prop>

属性.它旨在解决org.hibernate.LazyInitializationException问题,自hibernate 4.1.6起可用.

那么这个属性有什么作用呢?它只是告诉Hibernate如果在当前未初始化的代理中设置的会话关闭,它应该打开一个新的会话.您应该知道,如果您还有用于管理当前事务的任何其他打开会话,则此新打开的会话将不同,并且除非是JTA,否则它可能不会参与当前事务.由于这种TX副作用,您应该小心系统中可能存在的副作用.

在此处查找更多信息:http://blog.harezmi.com.tr/hibernates-new-feature-for-overcoming-frustrating-lazyinitializationexceptions使用hibernate.enable_lazy_load_no_trans解决Hibernate Lazy-Init问题


Ala*_*Hay 2

最简单、最透明的解决方案是 OSIV 模式。我相信您知道,在本网站和其他地方都有很多关于此(反)模式和替代方案的讨论,因此无需再次讨论。例如:

为什么 Hibernate Open Session in View 被认为是一种不好的做法?

然而,在我看来,并非所有对 OSIV 的批评都是完全准确的(例如,在渲染视图之前事务不会提交?真的吗?如果您使用 Spring 实现,则/sf/answers/746537081/

另外,请注意 JPA 2.1 引入了 Fetch Graphs 的概念,它使您可以更好地控制加载的内容。我还没有尝试过,但也许这最终能解决这个长期存在的问题!

http://www.thoughts-on-java.org/2014/03/jpa-21-entity-graph-part-1-named-entity.html


归档时间:

查看次数:

24034 次

最近记录:

8 年,8 月 前