Spring+Hibernate+MVC:Controller层中引入@Transaction对Service层设计的简化

Spring+Hibernate是目前Java应用开发中比较常见的组合,在开发WEB应用的时候,可能会结合一些其他的框架如Struts,这里的介绍以Spring MVC为WEB框架。

目前采用的分层设计(MVC)中,数据的持久化获取主要都是在Service中完成的,而Controller主要通过调用Service的相应接口获得Model,然后返回给View,这个模式对于设计来说是相当完善且被我们经常使用。

当我们引入Hibernate作为持久机制后,其采用的Object Map导航自动加载需要的数据时,对我们操作数据就更是便捷。

上面说了很多,可能与本文关联不大,本文的主要欲说明的问题在于:

Controller层经常需要请求Service层相应的Model,且这些Model可能采用了Lazy加载机制,因此需要Service进行提前加载完毕后返回加载完毕的Model,Controller然后返回这个对象给View进行解析 ,代码和图示如下:
 
A、实体定义1 MyChildOfObject.java
@Entity
@Table("t_myobject_child")
public class MyChildOfObject {
 @Id @Column(name="child_id") 
 @GeneratedValue(generator="general_seq")
 @AccessType("property")
 private Long id;
 
 @ManyToOne(fetch=FetchType.LAZY, optional=false)
 @JoinColumn(name="myobject_id")
 private MyObject parent;
 ...
}

A、实体定义2: MyObject.java
@Entity
@Table("t_myobject")
public class MyObject {
 
 @Id @Column(name="myobject_id") 
 @GeneratedValue(generator="general_seq")
 @AccessType("property")
 private Long id;
 
 @OneToMany(mappedBy="parent")
 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
 private Collection<MyChildOfObject> childs = new ArrayList<MyChildOfObject>();
 ...
 
 public Collection<MyChildOfObject> getChilds() {
  return this.childs;
 }
 ... 
}

B、Service定义:MyService.java
@Service
@Transactional(propagation=Propagation.SUPPORTS)
public class MyService {
 private SessionFactory sessionFactory;
 
 @Autowired
 public void setSessionFactory(SessionFactory sessionFactory) {
  this.sessionFactory = sessionFactory;
 }
 
 public MyObject getMyObject(Long id) {
  return (MyObject) this.sessionFactory.getCurrentSession().get(MyObject.class, id);
 }
}

C、Controller定义:MyController.java
@Controller
@RequestMapping("/myobject.eric")
public class MyController {
 private MyService myService;
 private String childsListView;
 
 @RequestMapping(params="action=listChilds")
 public String renderChildsOfMyObject(@RequestParam("myobjectId") Long myobjectId
  , HttpServletRequest request) throws Exceptions {
  MyObject myObject = this.myService.getMyObject(myobjectId);
  request.setAttribute("myObject", myObject);
  return this.childsListView;
 }
 ...
 @Autowired
 public void setMyService(MyService myService) {
  this.myService = myService;
 }
}

上述代码和图示基本完成了工作,但在页面显示的时候会出现一个异常,提示MyObject的childs没有加载,这个就是这次的问题所在了。

 注:这里的代码是基于Spring v2.5和Hibernate 3.2.x。

解决方案(一):修改实体定义,强制加载所有的childs

public class MyObject {
 ... 
 @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
 private Collection<MyChildOfObject> childs = new ArrayList<MyChildOfObject>();
 ...
}

点评:使用这个方式后,问题是解决了且很优雅,但也引入了一个最大的问题:性能,毕竟每次都加载全部的子对象可能不是每个应用都需要的。

解决方案(二):修改Service接口,提供一个可以返回包括子对象的接口



public class MyService {
 ... 
 public MyObject getMyObjectIncludeChilds(Long id) {
  MyObject myObject = this.getMyObject(id);
  // 加载子元素
  myObject.getChilds().size;
 }

}
点评:使用这个方式后,问题解决了但不够优雅,毕竟以后类似的需求都需要核心Service增加相应的接口,最后可能导致这个核心Service干的正活数量远低于这些组装工作

解决方案(三):提供一个专门进行组装用的Wrapper Service 

public interfact IMyService {
 public MyObject getMyObject(Long id);
}

public class MyService implements IMyService {
 ...
}

@Service("myServiceWrapper")
@Transactional(propagation=Propagation.SUPPORTS)
public class MyServiceWrapper implements IMyService {
 private MyService myService;
 
 @Autowired
 public void setMyService(MyService myService) {
  this.myService = myService;
 }
 
 public MyObject getMyObject(Long id) {
  MyObject myObject = this.myService.getMyObject(id);
  myObject.getChilds().size;
  return myObject;
 }

}

public class MyController {
 private IMyService myService;
 ...
 @Autowired
 public void setMyService(@Qualifier("myServiceWrapper") IMyService myService) {
  this.myService = myService;
 }
}

点评:此方案基本就基于接口编程了(Spring极力推荐的),在确保核心Service接口不污染的情况下,对需要的额外组装功能完全交给Wrapper类来实现。这个方案对于有多种客户端时(如即有基于Brower的b/s,又有Java Client)将非常有效,这也是区别与方案(四)的一个重要点;这个方案的主要缺点是类的数量会显著上升。

解决方案(四):在Controller中引入@Transaction


原设计方案全部保留,唯一需要变更的是Controller的代码中加入@Transaction
public class MyController { 
 @RequestMapping(params="action=listChilds")
 @Transactional(propagation=Propagation.SUPPORTS)
 public String renderChildsOfMyObject(@RequestParam("myobjectId") Long myobjectId
  , HttpServletRequest request) throws Exceptions {
  ...
 }

点评:以非常小的变更(只是多了@Transactional声明)就解决了问题,同时不产生性能影响又不污染核心Service接口。这个方案的缺点在于A)、当存在多种客户端时(如即有基于Brower的b/s,又有Java Client),此方案就失效了,这个时候可能要考虑方案(三)了;B)、在Controller层就接触到了事务,不过由于我们使用了声明式事务,对代码的影响基本无,同时又是配置的SUPPORTS方式,对Connection也没有影响,还是处于可控状态。

上述的几种解决方案都能够解决遇到的问题,同时也各有利弊,主要的关键是看哪种更适合我们当前的应用了。象我现在的项目,由于是完全基于b/s结构的,不存在异种客户端,所以就直接上了方案(四)。

原文地址:http://blog.csdn.net/MatrixII/article/details/1998428

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值