【SSH项目实战02】基本增删查改、Service和Action的抽取以及使用
上一节我们搭建好了Struts2、Hibernate和Spring的开发环境,并成功将它们整合在一起。这节主要完成一些基本的增删改查以及Service、Dao和Action的抽取。 1. Service层的抽取上一节中,我们在service层简单写了save和update方法,这里我们开始完善该部分的代码,然后对service层的代码进行抽取。 1.1完善CategoryService层对数据库的操作无非是增删改查,首先我们来完善CategoryService层的接口和实现: //CategoryService接口 public interface CategoryService extends BaseService<Category> { public void save(Category category); //插入 public void update(Category category);//更新 public void delete(int id); //删除 public Category get(int id); //获取一个Category public List<Category> query(); //获取全部Category }对CategoryService接口的具体实现: public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService { private SessionFactory sessionFactory; //Spring会注进来 public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } protected Session getSession() { //从当前线程获取session,如果没有则创建一个新的session return sessionFactory.getCurrentSession(); } @Override public void save(Category category) { getSession().save(category); } @Override public void update(Category category) { getSession().update(category); } @Override public void delete(int id) { /*第一种方法有个弊端,就是没删除一次得先查询一次 Object obj = getSession().get(Category.class,id); if(obj != null) { getSession().delete(obj); }*/ String hql = "delete Category while id=:id"; getSession().createQuery(hql) // .setInteger("id",id) // .executeUpdate(); } @Override public Category get(int id) { return (Category) getSession().get(Category.class,id); } @Override public List<Category> query() { String hql = "from Category"; return getSession().createQuery(hql).list(); } } 1.2 Service层抽取实现完成了CategoryService后,我们来抽取Service层的基础实现。思路是这样的:我们抽取一个基础接口BaseService以及基础接口的实现BaseServiceImpl,后面开发的时候,如果需要新的Service,只需要做两步即可:首先定义一个新的接口xxxService继承BaseService接口,这个接口可以增加新的抽象方法;然后定义一个新的实现类xxxServiceImpl继承BaseServiceImpl并实现xxxService接口即可。这样更加便于项目的维护。 我们先根据上面的CategoryService接口来创建BaseService接口: //基础接口BaseService,使用泛型 public interface BaseService<T> { public void save(T t); public void update(T t); public void delete(int id); public T get(int id); public List<T> query(); }然后再根据CategoryServiceImpl实现类创建BaseService接口的实现类BaseServiceImpl: /** * @Description TODO(公共模块的抽取) * @author eson_15 * */ @SuppressWarnings("unchecked") public class BaseServiceImpl<T> implements BaseService<T> { private Class clazz; //clazz中存储了当前操作的类型,即泛型T private SessionFactory sessionFactory; public BaseServiceImpl() { //下面三个打印信息可以去掉,这里是给自己看的 System.out.println("this代表的是当前调用构造方法的对象" + this); System.out.println("获取当前this对象的父类信息" + this.getClass().getSuperclass()); System.out.println("获取当前this对象的父类信息(包括泛型信息)" + this.getClass().getGenericSuperclass()); //拿到泛型的参数类型 ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass(); clazz = (Class)type.getActualTypeArguments()[0]; } public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } protected Session getSession() { //从当前线程获取session,如果没有则创建一个新的session return sessionFactory.getCurrentSession(); } @Override public void save(T t) { getSession().save(t); } @Override public void update(T t) { getSession().update(t); } @Override public void delete(int id) { System.out.println(clazz.getSimpleName()); String hql = "delete " + clazz.getSimpleName() + " as c where c.id=:id"; getSession().createQuery(hql) // .setInteger("id",id) // .executeUpdate(); } @Override public T get(int id) { return (T) getSession().get(clazz,id); } @Override public List<T> query() { String hql = "from " + clazz.getSimpleName(); return getSession().createQuery(hql).list(); } }抽取完了后,我们就可以改写CategoryService接口和CategoryServiceImpl实现类了。如下: //CategoryService接口继承BaseService接口 public interface CategoryService extends BaseService<Category> { /* * 只要添加CategoryService本身需要的新的方法即可,公共方法已经在BaseService中了 */ } /** * @Description TODO(模块自身的业务逻辑) * @author eson_15 * */ public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService { /* * 只需实现CategoryService接口中新增的方法即可,公共方法已经在BaseServiceImpl中实现了 */ }从代码中可以看出,新增的Service只需要继承BaseService接口,然后在接口中新增本Service所需要的业务逻辑即可。新增的ServiceImpl只需要继承BaseServiceImpl并实现新增的业务逻辑即可。 但是别忘了很重要的一点:就是修改Spring的配置文件beans.xml中的bean。 <!-- 泛型类是不能实例化的,所以要加lazy-init属性 --> <bean id="baseService" class="cn.it.shop.service.impl.BaseServiceImpl" lazy-init="true"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="categoryService" class="cn.it.shop.service.impl.CategoryServiceImpl" parent="baseService"/>将原来categoryService中的property干掉,然后增加parent属性,指明继承baseService;然后配置一下baseService,将sessionFactory配到baseService中去,另外要注意一点:设置lazy-init属性为true,因为baseService是泛型类,泛型类是不能实例化的。至此,Service层的抽取就搞定了。 2. Service层添加一个Account刚刚抽取好了Service层,那么现在我们想写一个Account(管理员)的service就很简单了: 首先写一个AccountService接口继承BaseService: public interface AccountService extends BaseService<Account> { //注意BaseService里的泛型现在是Account /* * 只要添加AccountService本身需要的新的方法即可,公共方法已经在BaseService中了 */ }然后写一个AccountServiceImpl实现类继承BaseServiceImpl实现类,并实现AccountService接口即可: public class AccountServiceImpl extends BaseServiceImpl<Account> implements AccountService { /* * 只需实现AccountService接口中新增的方法即可,公共方法已经在BaseServiceImpl中实现了 */ //管理登陆功能,后期再完善 }最后在beans.xml文件里加上如下配置: <bean id="accountService" class="cn.it.shop.service.impl.AccountServiceImpl" parent="baseService" />这样就写好了一个新的service了,以后需要添加service就遵循这个流程,非常方便。 3. Action的抽取3.1 Action中往域(request,session,application等)中存数据我们知道,在Action中可以直接通过ActionContext.getContext()去获取一个ActionContext对象,然后通过该对象再去获得相应的域对象;也可以通过实现xxxAware接口来注入相应的域对象。我们先来看一下这两种方法: public class CategoryAction extends ActionSupport implements RequestAware,SessionAware,ApplicationAware{ private Category category; private CategoryService categoryService; public void setCategoryService(CategoryService categoryService) { this.categoryService = categoryService; } public String update() { System.out.println("----update----"); categoryService.update(category); return "index"; } public String save() { System.out.println("----save----"); return "index"; } public String query() { //解决方案一,采用相应的map取代原来的内置对象,这样与jsp没有依赖,但是代码量比较大 // ActionContext.getContext().put("categoryList",categoryService.query()); //放到request域中 // ActionContext.getContext().getSession().put("categoryList",categoryService.query()); //放到session域中 // ActionContext.getContext().getApplication().put("categoryList",categoryService.query()); //放到application域中 //解决方案二,实现相应的接口(RequestAware,ApplicationAware),让相应的map注入 request.put("categoryList",categoryService.query()); session.put("categoryList",categoryService.query()); application.put("categoryList",categoryService.query()); return "index"; } public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } private Map<String,Object> request; private Map<String,Object> session; private Map<String,Object> application; @Override public void setApplication(Map<String,Object> application) { this.application = application; } @Override public void setSession(Map<String,Object> session) { this.session = session; } @Override public void setRequest(Map<String,Object> request) { this.request = request; } }还是上一节整合三大框架时的CategoryAction类,我们在里面加了一个query方法,在该方法中,我们通过向request域、session域和application域中存入查询的结果。第一种方法是直接使用ActionContext来实现,不需要实现任何接口,但是代码量较大;第二种方法通过实现RequestAware、SessionAware和ApplicationAware接口,实现该接口的三个抽象方法把request、session和application注入进来,然后赋给相应的成员变量中,这样就可以在query方法中向域中存放查询结果了。这代码量貌似比第一种方法更大……但是我们可以抽取,先往下看。 我们在index.jsp中新加一个查询连接来测试能否将查询结果显示出来: <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> <a href="${pageContext.request.contextPath }/category_update.action?category.id=2&category.type=gga&category.hot=false">访问update</a> <a href="category_save.action">访问save</a> <a href="category_query.action">查询所有类别</a><br/> <c:forEach items="${requestScope.categoryList }" var="category"> ${category.id } | ${category.type } | ${category.hot } <br/> </c:forEach> <c:forEach items="${sessionScope.categoryList }" var="category"> ${category.id } | ${category.type } | ${category.hot } <br/> </c:forEach> <c:forEach items="${applicationScope.categoryList }" var="category"> ${category.id } | ${category.type } | ${category.hot } <br/> </c:forEach> </body> </html> 3.2抽取BaseAction
|