基于注解的Spring MVC与JPA如何解决实体的延时加载问题
2012-06-18 19:05
567 查看
本文出处:http://blog.csdn.net/chaijunkun/article/details/7673931,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。
Sping和Hibernate在去年年底都发布了新的版本,现在我做的项目都将最新版本的Spring和Hibernate引入了,使用效果良好。不过最近遇到了一个以前没有遇到的问题——实体的延时加载。
对于关系型数据库,表与表之间的某些字段是通过一对多、多对一或者是多对多的关系来维护的,因此Hibernate引入了延迟加载的优化方法。例如一个雇员,包含姓名,性别等等信息,而最重要的就是所属部门。这些员工与部门就存在着多对一的关系。当我从数据库中获取到雇员的时候,假如没有延迟加载优化,那么雇员的信息以及部门相关的属性都会一并加载下来,假如一个部门信息内再有关联的其它信息,那就会占用很多时间来查询。然而有时候,我们获取雇员仅仅是为了显示一下姓名。
上面是针对延迟加载应用场景的一个表述,下面是我的代码:
首先是部门表的关联代码
接下来是雇员的关联代码
这里,雇员表的部门列我使用了延迟加载配置。其它配置都是JPA中普通的注解配置。列名称与属性名称相同时就不用配置@Column注解的name属性。
然后就按部就班地写了表操作的服务及实现。这里就不多说了。
接下来在Spring MVC中标注了@Controller的类的方法中尝试按照雇员id来获取雇员信息:
当我尝试访问http://localhost/show.do?empId=1的时候发现出现了如下的错误:
很明显是由于jpa的entityManager将事务关闭了,因此延迟加载时找不到存在的会话来运行接下来的自动查询。
在网上找了很多资料,最终找到了解决办法:
首先在配置JPA的EntityManager配置文件中加入如下配置:
然后在配置Servlet的配置文件中更改支持@RequestMapping注解的配置:
原来的多数配置都是这样的:
现在我们为这个默认的注解处理映射加入视图内拦截器来自动生成会话:
好了,加入了以上配置后,再访问同样的接口,发现问题解决了。如果你在使用JPA的时候打开了show_sql选项,你会看到执行了两条JPQL语句。
2014年11月14日补充:今天发现按照上述配置后仍然可能在懒加载时无法获取关联对象。经过检查,是由于没有加入事务造成的。首先要在spring配置文件中加入事务注解支持选项:
Sping和Hibernate在去年年底都发布了新的版本,现在我做的项目都将最新版本的Spring和Hibernate引入了,使用效果良好。不过最近遇到了一个以前没有遇到的问题——实体的延时加载。
对于关系型数据库,表与表之间的某些字段是通过一对多、多对一或者是多对多的关系来维护的,因此Hibernate引入了延迟加载的优化方法。例如一个雇员,包含姓名,性别等等信息,而最重要的就是所属部门。这些员工与部门就存在着多对一的关系。当我从数据库中获取到雇员的时候,假如没有延迟加载优化,那么雇员的信息以及部门相关的属性都会一并加载下来,假如一个部门信息内再有关联的其它信息,那就会占用很多时间来查询。然而有时候,我们获取雇员仅仅是为了显示一下姓名。
上面是针对延迟加载应用场景的一个表述,下面是我的代码:
首先是部门表的关联代码
package blog.csdn.net.chaijunkun.pojo; import java.io.Serializable; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; @Entity public class Department implements Serializable { /** * */ private static final long serialVersionUID = -3760808870590915399L; @Id @GeneratedValue private Long departId; @Column(nullable=false) private String departName; @Column(nullable=false) private String departLocate; @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="department") private Set<Employee> employees = new HashSet<Employee>(0); public Long getDepartId() { return departId; } public void setDepartId(Long departId) { this.departId = departId; } public String getDepartName() { return departName; } public void setDepartName(String departName) { this.departName = departName; } public String getDepartLocate() { return departLocate; } public void setDepartLocate(String departLocate) { this.departLocate = departLocate; } public Set<Employee> getEmployees() { return employees; } public void setEmployees(Set<Employee> employees) { this.employees = employees; } }
接下来是雇员的关联代码
package blog.csdn.net.chaijunkun.pojo; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; @Entity public class Employee implements Serializable { /** * */ private static final long serialVersionUID = 7226562979319568974L; @Id @GeneratedValue private Long empId; @Column(nullable=false) private String fullName; @Column(nullable=false) private Boolean sex; @ManyToOne(fetch=FetchType.LAZY) @JoinColumn(name="departId") private Department department; public Long getEmpId() { return empId; } public void setEmpId(Long empId) { this.empId = empId; } public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } public Boolean getSex() { return sex; } public void setSex(Boolean sex) { this.sex = sex; } public Department getDepartment() { return department; } public void setDepartment(Department department) { this.department = department; } }
这里,雇员表的部门列我使用了延迟加载配置。其它配置都是JPA中普通的注解配置。列名称与属性名称相同时就不用配置@Column注解的name属性。
然后就按部就班地写了表操作的服务及实现。这里就不多说了。
接下来在Spring MVC中标注了@Controller的类的方法中尝试按照雇员id来获取雇员信息:
@Controller @RequestMapping(value="/show.do") public class TestController { @Resource private EmployeeService employeeService; @SuppressWarnings({ "unchecked", "rawtypes" }) @RequestMapping public ModelAndView getEmployee(@RequestParam(required= true) Long empId, Map model){ Employee employee= employeeService.find(empId); if (employee!=null){ System.out.println(employee.getFullName()); //下面代码出问题了 System.out.println(employee.getDepartment().getDepartName()); model.put("employee", employee); return new ModelAndView("show", model); }else{ return null; } } }
当我尝试访问http://localhost/show.do?empId=1的时候发现出现了如下的错误:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session at
很明显是由于jpa的entityManager将事务关闭了,因此延迟加载时找不到存在的会话来运行接下来的自动查询。
在网上找了很多资料,最终找到了解决办法:
首先在配置JPA的EntityManager配置文件中加入如下配置:
<!-- 建立视图内拦截器来解决JPA中访问延迟加载属性时产生的无会话异常 --> <!-- LazyInitializationException: could not initialize proxy no session --> <!-- 此拦截器会注入到servlet配置中的DefaultAnnotationHandlerMapping中 --> <bean name="openEntityManagerInViewInterceptor" class="org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor"> <property name="entityManagerFactory"> <ref bean="entityManagerFactory" /> </property> </bean>
然后在配置Servlet的配置文件中更改支持@RequestMapping注解的配置:
原来的多数配置都是这样的:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
现在我们为这个默认的注解处理映射加入视图内拦截器来自动生成会话:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="interceptors"> <list> <ref bean="openEntityManagerInViewInterceptor" /> </list> </property> </bean>
好了,加入了以上配置后,再访问同样的接口,发现问题解决了。如果你在使用JPA的时候打开了show_sql选项,你会看到执行了两条JPQL语句。
2014年11月14日补充:今天发现按照上述配置后仍然可能在懒加载时无法获取关联对象。经过检查,是由于没有加入事务造成的。首先要在spring配置文件中加入事务注解支持选项:
<tx:annotation-driven />该选项默认指定的事务管理器是bean id为transactionManager的对象,完整配置为:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" />接下来最重要的是要在操作数据的service层增加@Transactional注解(javax.transaction.Transactional),例如:
@Transactional public String getRoleName(Long id){ User u = userRepository.findOne(id); return u.getRole().getName(); }
相关文章推荐
- 在Spring整合 SpringMVC,SpringData和 JPA 时,如何解决 Lazy懒加载问题?
- 非J2EE 容器环境下Spring +JPA 多持久化单元/多个JAR归档注解实体 的实体扫描问题及解决办法
- 解决JPA懒加载典型的N+1问题-注解@NamedEntityGraph
- 解决JPA延迟加载错误问题
- 让spring帮助你在MVC层解决JPA的缓迟加载问题
- 延时加载图片(终极版,解决一行多张图片无法显示的问题)
- 让spring帮助你在MVC层解决JPA的缓迟加载问题
- 解决Hibernate 注解在属性上 ,多对一,一对一懒加载 当调用one那一方getId 也会查数据库的问题
- 如何解决加载大图片时内存溢出的问题
- 让spring帮助你在MVC层解决JPA的缓迟加载问题
- Spring使用OpenSessionInViewFilter解决Hibernate的lazy延时加载问题
- 让spring帮助你在MVC层解决JPA的缓迟加载问题
- 让spring帮助你在MVC层解决JPA的缓迟加载问题
- Spring Boot如何让Web API自动生成文档,并解决swagger-annotations的API注解description属性废弃的问题
- 如何解决万能地图下载器下载的影像加载到ArcGIS “变色”的问题
- Spring使用OpenSessionInViewFilter解决Hibernate的lazy延时加载问题
- 如何解决加载大图片时内存溢出的问题 OOM
- springMVC实体用注解管理,多对多 set集合元素排序问题 解决
- 由浅入深写java分布式(4)基于注解 dubbo 一个app同时存在consumer和provider自启动失败的问题,以dubbo和spring注解加载顺序的问题
- Windows10应用加载失败不响应问题如何解决