我理解的hibernate懒加载:使用懒加载时,如果不是去获取关联对象的信息,是不会触发关联对象引起的数据库查询,映射...耗费的资源.在很多场合,如果这里使用即时加载就成为一个性能瓶颈,解决这种问题的需求是非常有必要的.这里所谓的解决就是我们不需要关联对象的信息时,就不要去查询数据库获取不必要的信息.但需要关联对象信息的时候,也要把数据从数据库取出来.如果不用spring,自己来管理hibernate的session,那么懒加载是不大问题的.默认通过对象去或者关联的对象是会自动触发查询sql语句的.当今天一般的java web开发都是离不开spring的.spring实在是太强大,好用.
因此本文讲的是集成spring的hibernate怎样使用懒加载...
相信大多数人在集成spring的hibernate,使用懒加载时遇到org.hibernate.LazyInitializationException: could not initialize proxy - no Session...
google解决方法也不少,比如方法一,二,三:http://blog.csdn.net/yaorongwang0521/article/details/7074573,但觉得都不是我最理想的方法.
从异常信息来看,是因为没有session而不能初始化代理.没有session就是session被关闭了,或者没有开启新的session造成的抛异常.
下面的解决方法是不必要开启新session,在一个方法开启一个session就足够,操作的sql都在同一个事务,要回滚,就让整个方法的操作回滚,这是绝大多数的需求.说到这里相信很多人都知道@Transactional自动跳出脑海了吧.对,关键就是用你将查询一般的信息和关联对象的信息放在同一个事务里面就可以了.@Transactional可以是方法级别也可以是类级别,最终还是要定义将操作被包含在一个方法里.为了缩小篇章,只贴关键代码,文末可下载源码,Action....
spring集成hibernate就不讲了,不会的可以看java企业开发四:ssh+JPA(hibernate实现).
一.pom.xml和基于注解配置的spring.AppConfig.java略(需要就下载文末的源码)
二.写domain和service层.(我个人的开发是没有dao层,操作数据库直接放在service层就行了,调来调去不觉得闲得蛋痛么,)
用户(职工)与部门举例.
User.java
import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.ForeignKey; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = "user") public class User { @Id @GeneratedValue private int id; @Column(length = 32) private String username; //部门与员工是多对一(N-1)关系.(多对一:几个实体指向目标实体) //设置级联:在更新用户的时候,同时把用户的部门级联信息保存到部门表去 //设置加载方式:懒加载 @ManyToOne(cascade = {CascadeType.MERGE,CascadeType.REFRESH },fetch=FetchType.LAZY) //(目标表也叫主表,例如这里的department表,包含此外键的表就是主表的从表,例如这里的user表,外键department_id是在user表里面) //外键:一个从表的外键与主表的主键进行关联.name是从表外键列名;referencedColumnName是主表的主键名;通过foreignKey设置外键信息,如外键名. @JoinColumn(name="department_id", referencedColumnName="id",foreignKey=@ForeignKey(name="fk_user_deparment")) private Department department; //setter and getter略.. }Department.java
import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Table(name="department") @Entity public class Department { @Id @GeneratedValue private int id; @Column(length=32) private String name; //setter and getter略..... }
import java.util.List; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.test.domain.Department; import com.test.domain.User; import com.test.service.UserService; @Service public class UserServiceImpl extends BaseServiceImpl<User> implements UserService{ //User的department是懒加载.查用户是不会去把用户的department信息映射到User的. @Transactional(readOnly=true) public List<User> findAllWithDepartment(){ List<User> list=findByJpql("select u from User u", 0, 10); for (User user : list) { Department department=user.getDepartment(); if (department!=null) { //getId也好,getName也好,触发去获取部门信息,只有getDepartment()是不足够的. department.getId(); } } return list; } }
@Test public void testFindAllWithoutDepartment() { //新建两个用户 User user=new User(); user.setUsername("username1"); Department department=new Department(); department.setName("department1"); departmentService.persist(department); user.setDepartment(departmentService.findById(department.getId())); userService.persist(user); user=new User(); user.setUsername("username2"); userService.persist(user); List<User> list=userService.findByJpql("select u from User u", 0, 10); for (User u : list) { System.out.println(u.getUsername()); //没有加载关联对象的信息,严禁使用关联对象的get方法,用了你就等着抛no session异常吧.也不要指望在这里加载关联对象的信息. //请在服务层另起方法,使用@Transactional /* Department d=u.getDepartment(); if(d!=null){ System.out.println(d.getName()); } */ } } @Test public void testFindAllWithDepartment() { //新建两个用户 User user=new User(); user.setUsername("username1"); Department department=new Department(); department.setName("department1"); departmentService.persist(department); user.setDepartment(departmentService.findById(department.getId())); userService.persist(user); user=new User(); user.setUsername("username2"); userService.persist(user); //默认的懒加载是不能加载用户的部门信息,去get必然出错.就因为查一般用户信息和查用户部门信息是多个sql语句. //所以有可能出现查一般用户信息,spring就关闭了session,而再没有开新的session. //重新写了findAllWithDepartment方法就是为了查一般用户信息和查用户部门信息在同一个事务(session). //所以就不会出现关闭session或session没初始化的异常了 List<User> list=userService.findAllWithDepartment(); for (User u : list) { //System.out.println(u.getUsername()); Department d=u.getDepartment(); if(d!=null){ System.out.println(d.getName()); } } }