spring jpa 延迟加载

本文介绍了解决JPA懒加载导致的LazyInitializationException异常的方法,包括配置视图拦截器和事务管理,确保延迟加载正常工作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最关键一句
 <prop key="hibernate.enable_lazy_load_no_trans">true</prop>
但是,另一个没有测试过,就是网页对 对象的延迟加载,下面第二种配置以后可以试试


<bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!-- 指定数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 指定Entity实体类包路径 -->
        <property name="packagesToScan">
            <list>
                <value>com.xxx.xxx.jpadomain</value>
            </list>
        </property>
        <!-- 指定JPA属性;如Hibernate中指定是否显示SQL的是否显示、方言等 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!-- 是否生成ddl文件 -->
                <property name="generateDdl" value="true"/>
                <!-- 是否展示sql -->
                <property name="showSql" value="false"/>
                <!-- 必要的数据库库使用的详细信息 -->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
                <!-- mysql,自行选择 -->
                <property name="database" value="MYSQL"/>
            </bean>
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy
                </prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.enable_lazy_load_no_trans">true</prop>
            </props>
        </property>
    </bean>



第二种:

Sping和hibernate在去年年底都发布了新的版本,现在我做的项目都将最新版本的spring和Hibernate引入了,使用效果良好。不过最近遇到了一个以前没有遇到的问题——实体的延时加载。

对于关系型数据库,表与表之间的某些字段是通过一对多、多对一或者是多对多的关系来维护的,因此Hibernate引入了延迟加载的优化方法。例如一个雇员,包含姓名,性别等等信息,而最重要的就是所属部门。这些员工与部门就存在着多对一的关系。当我从数据库中获取到雇员的时候,假如没有延迟加载优化,那么雇员的信息以及部门相关的属性都会一并加载下来,假如一个部门信息内再有关联的其它信息,那就会占用很多时间来查询。然而有时候,我们获取雇员仅仅是为了显示一下姓名。

上面是针对延迟加载应用场景的一个表述,下面是我的代码:

首先是部门表的关联代码

[java]  view plain  copy
  1. package blog.youkuaiyun.com.chaijunkun.pojo;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.HashSet;  
  5. import java.util.Set;  
  6.   
  7. import javax.persistence.CascadeType;  
  8. import javax.persistence.Column;  
  9. import javax.persistence.Entity;  
  10. import javax.persistence.FetchType;  
  11. import javax.persistence.GeneratedValue;  
  12. import javax.persistence.Id;  
  13. import javax.persistence.OneToMany;  
  14.   
  15.   
  16. @Entity  
  17. public class Department implements Serializable {  
  18.       
  19.     /** 
  20.      *  
  21.      */  
  22.     private static final long serialVersionUID = -3760808870590915399L;  
  23.   
  24.     @Id  
  25.     @GeneratedValue  
  26.     private Long departId;  
  27.       
  28.     @Column(nullable=false)  
  29.     private String departName;  
  30.       
  31.     @Column(nullable=false)  
  32.     private String departLocate;  
  33.       
  34.     @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="department")  
  35.     private Set<Employee> employees = new HashSet<Employee>(0);  
  36.   
  37.     public Long getDepartId() {  
  38.         return departId;  
  39.     }  
  40.   
  41.     public void setDepartId(Long departId) {  
  42.         this.departId = departId;  
  43.     }  
  44.   
  45.     public String getDepartName() {  
  46.         return departName;  
  47.     }  
  48.   
  49.     public void setDepartName(String departName) {  
  50.         this.departName = departName;  
  51.     }  
  52.   
  53.     public String getDepartLocate() {  
  54.         return departLocate;  
  55.     }  
  56.   
  57.     public void setDepartLocate(String departLocate) {  
  58.         this.departLocate = departLocate;  
  59.     }  
  60.   
  61.     public Set<Employee> getEmployees() {  
  62.         return employees;  
  63.     }  
  64.   
  65.     public void setEmployees(Set<Employee> employees) {  
  66.         this.employees = employees;  
  67.     }  
  68.       
  69. }  

接下来是雇员的关联代码

[java]  view plain  copy
  1. package blog.youkuaiyun.com.chaijunkun.pojo;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. import javax.persistence.Column;  
  6. import javax.persistence.Entity;  
  7. import javax.persistence.FetchType;  
  8. import javax.persistence.GeneratedValue;  
  9. import javax.persistence.Id;  
  10. import javax.persistence.JoinColumn;  
  11. import javax.persistence.ManyToOne;  
  12.   
  13. @Entity  
  14. public class Employee implements Serializable {  
  15.   
  16.     /** 
  17.      *  
  18.      */  
  19.     private static final long serialVersionUID = 7226562979319568974L;  
  20.   
  21.     @Id  
  22.     @GeneratedValue  
  23.     private Long empId;  
  24.       
  25.     @Column(nullable=false)  
  26.     private String fullName;  
  27.       
  28.     @Column(nullable=false)  
  29.     private Boolean sex;  
  30.       
  31.     @ManyToOne(fetch=FetchType.LAZY)  
  32.     @JoinColumn(name="departId")  
  33.     private Department department;  
  34.   
  35.     public Long getEmpId() {  
  36.         return empId;  
  37.     }  
  38.   
  39.     public void setEmpId(Long empId) {  
  40.         this.empId = empId;  
  41.     }  
  42.   
  43.     public String getFullName() {  
  44.         return fullName;  
  45.     }  
  46.   
  47.     public void setFullName(String fullName) {  
  48.         this.fullName = fullName;  
  49.     }  
  50.   
  51.     public Boolean getSex() {  
  52.         return sex;  
  53.     }  
  54.   
  55.     public void setSex(Boolean sex) {  
  56.         this.sex = sex;  
  57.     }  
  58.   
  59.     public Department getDepartment() {  
  60.         return department;  
  61.     }  
  62.   
  63.     public void setDepartment(Department department) {  
  64.         this.department = department;  
  65.     }  
  66.       
  67. }  

这里,雇员表的部门列我使用了延迟加载配置。其它配置都是JPA中普通的注解配置。列名称与属性名称相同时就不用配置@Column注解的name属性。

然后就按部就班地写了表操作的服务及实现。这里就不多说了。

接下来在Spring MVC中标注了@Controller的类的方法中尝试按照雇员id来获取雇员信息:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Controller  
  2. @RequestMapping(value="/show.do")  
  3. public class TestController {  
  4.       
  5.     @Resource  
  6.     private EmployeeService employeeService;  
  7.       
  8.     @SuppressWarnings({ "unchecked""rawtypes" })  
  9.     @RequestMapping  
  10.     public ModelAndView getEmployee(@RequestParam(required= true) Long empId, Map model){  
  11.         Employee employee= employeeService.find(empId);  
  12.         if (employee!=null){  
  13.             System.out.println(employee.getFullName());  
  14.             //下面代码出问题了  
  15.             System.out.println(employee.getDepartment().getDepartName());  
  16.             model.put("employee", employee);  
  17.             return new ModelAndView("show", model);  
  18.         }else{  
  19.             return null;  
  20.         }         
  21.     }  
  22. }  

当我尝试访问http://localhost/show.do?empId=1的时候发现出现了如下的错误:

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. org.hibernate.LazyInitializationException: could not initialize proxy - no Session at   

很明显是由于jpa的entityManager将事务关闭了,因此延迟加载时找不到存在的会话来运行接下来的自动查询。

在网上找了很多资料,最终找到了解决办法:

首先在配置JPA的EntityManager配置文件中加入如下配置:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <!-- 建立视图内拦截器来解决JPA中访问延迟加载属性时产生的无会话异常 -->  
  2.     <!-- LazyInitializationException: could not initialize proxy no session -->  
  3.     <!-- 此拦截器会注入到servlet配置中的DefaultAnnotationHandlerMapping中 -->  
  4.     <bean name="openEntityManagerInViewInterceptor"   
  5.         class="org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor">  
  6.         <property name="entityManagerFactory">  
  7.         <ref bean="entityManagerFactory" />  
  8.         </property>  
  9.     </bean>  

然后在配置Servlet的配置文件中更改支持@RequestMapping注解的配置:

原来的多数配置都是这样的:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />  

现在我们为这个默认的注解处理映射加入视图内拦截器来自动生成会话:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <bean  
  2.     class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">  
  3.     <property name="interceptors">  
  4.         <list>  
  5.             <ref bean="openEntityManagerInViewInterceptor" />  
  6.         </list>  
  7.     </property>   
  8. </bean>  

好了,加入了以上配置后,再访问同样的接口,发现问题解决了。如果你在使用JPA的时候打开了show_sql选项,你会看到执行了两条JPQL语句。


2014年11月14日补充:今天发现按照上述配置后仍然可能在懒加载时无法获取关联对象。经过检查,是由于没有加入事务造成的。首先要在spring配置文件中加入事务注解支持选项:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <tx:annotation-driven />  
该选项默认指定的事务管理器是bean id为transactionManager的对象,完整配置为:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">  
  2.     <property name="entityManagerFactory" ref="entityManagerFactory" />  
  3. </bean>  
  4. <tx:annotation-driven transaction-manager="transactionManager" />  
接下来最重要的是要在操作数据的service层增加@Transactional注解(javax.transaction.Transactional),例如:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Transactional  
  2. public String getRoleName(Long id){  
  3.     User u = userRepository.findOne(id);  
  4.     return u.getRole().getName();  
  5. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值