上篇博文《JPA数据懒加载LAZY和实时加载EAGER(二)》讲到,如果使用懒加载来调用关联数据,必须要保证主查询session(数据库连接会话)的生命周期没有结束,否则,你是无法抽取到数据的。那么如果保证自己想要获取关联数据时,数据库会话session还存在呢?今天讲解一种方法,借助Spring提供的@Transactional注解来实现方法内部的操作在同一次数据库连接中执行。需要注意的是,使用@Transactional注解,必须要保证方法通过Spring组件解析方式处理,spring代理会为方法注入事务拦截逻辑。
关于spring的事务实现原理,可以参考《Spring @Transactional工作原理》
一、为实体类ProcessBlock和Node添加一对多的关联关系。Set<Node>属性使用懒加载方式。下面给出代码。
@Entity(name = "nbpm_processblock") @NamedQuery(name = "ProcessBlock.findByName", query = "select p from nbpm_processblock p where p.name=?1") public class ProcessBlock implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") long id; @Column(name = "name") String name; @Column(name = "description") String description; String subClass; @OneToMany(mappedBy = "processblock", cascade = CascadeType.ALL,fetch=FetchType.LAZY) // @JoinColumn(name="processblock_id") Set<Node> nodeSet = new HashSet<Node>();
二、为查询方法添加事务注解
@Transactional @RequestMapping(value = "/list", method = RequestMethod.GET) public String list(ModelMap model,HttpSession session) { //ProcessBlockRepository processBlock = ApplicationContextUtil.instance.getBean(ProcessBlockRepository.class); //List<ProcessBlock> list = processBlock.findByName1("主干流程"); Map<String, Object> params = new HashMap<>(); params.put("name", "主干流程"); List<ProcessBlock> list = ApplicationContextUtil.instance.getJpaUtil().list( "select u from simm.spring.entity.ProcessBlock u where u.name=:name", params, ProcessBlock.class); System.out.println("准备获取懒加载数据"); Set<Node> nodes = list.get(0).getNodeSet();
三、调试一下,查看调用情况
1、测试代码先执行List<ProcessBlock>查询,之后输出"准备获取懒加载数据",最后查询第一条ProcessBlock对应的节点集合。通过执行日志很容易看出,懒加载的数据只有在后续的代码逻辑中被调用后,才会生成sql查询,在数据库中执行获取数据。
2、先获取List<ProcessBlock>列表后,在来获取第一个ProcessBlock对象对应的节点集合。调试显示已经成功获取到数据。
至此,测试完毕。用法很简单,但是关于spring事务的实现这块还是很有意思的,一次数据库连接在线程中通过ThreadLocal变量传递,其生命周期靠事务拦截器来控制。这个过程在ORM框架中应该是一种比较通用的思路,在Hibernate中如此,其他框架应该也是相差无几。感谢你的阅读,欢迎留言讨论交流!