您的位置:首页 > 编程语言 > Java开发

Struts2泛型通用crud的action,让一切变得简单起来

2010-12-17 17:13 393 查看
对实体对象的crud操作绝对是重复性操作,不同的实体对象做着差不多的增删改。通过sturts2的一个泛型crudAction完全可以简化这些操作的。官方有个例子,http://struts.apache.org/2.2.1/docs/crud-demo-i.html ,我觉得是不给力的,有兴趣的可以看看这个不给力的demo。

泛型CrudAction的设计思路是,CrudAction提供基本的crud操作,由于是泛型的所以可以针对任意类型的实体类操作。所以,继承了CrudAction就可以让增删改的操作简单起来。

首先需要了解struts2的2个接口,需要crudAction实现:

第一个ModelDriven<T>接口,这个接口本身就是泛型的,如下:

/**
* ModelDriven Actions provide a model object to be pushed onto the ValueStack
* in addition to the Action itself, allowing a FormBean type approach like Struts.
*
* @author Jason Carreira
*/
public interface ModelDriven<T> {
/**
* Gets the model to be pushed onto the ValueStack instead of the Action itself.
*
* @return the model
*/
T getModel();
}


作用是显而易见的,就是让页面直接访问实体对象。

第二个接口是Preparable,如下:

/**
* Preparable Actions will have their <code>prepare()</code> method called if the {@link com.opensymphony.xwork2.interceptor.PrepareInterceptor}
* is applied to the ActionConfig.
*
* @author Jason Carreira
* @see com.opensymphony.xwork2.interceptor.PrepareInterceptor
*/
public interface Preparable {
/**
* This method is called to allow the action to prepare itself.
*
* @throws Exception thrown if a system level exception occurs.
*/
void prepare() throws Exception;

}


顾名思义是,在action执行前调用这个方法。这就给予了机会通过页面参数,去数据库查出实体类对象。供给crud的方法去处理。说到这个接口,很多人会结合paramsPrepareParamsStack这个拦截器使用。这是一个奇妙的拦截器,因为本来执行顺序是,preparable -> 装配参数拦截器, 应用了这个拦截器以后就变成: 装配参数 -> preparable -> 装配参数。 这样的目的是为了,在perpare方法中拿到一个装配好参数的实体对象(没有关联到数据库),以后利用参数查出数据库持久化对象,以后在执行一次装配参数向持久化对象里设置值。

但我觉得没有必要,而且这样做在有set集合的关系实体类中会有容易出错的地方。所以我并没有使用paramsPrepareParamsStack这个拦截器,而是使用了默认的。

现在让我们看代码:

/*
* Copyright (c) scott.cgi Rights Reserved.
* Email: scott.cgi@gmail.com
*/
package com.scottcgi.common.struts2.action;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;

import javax.annotation.Resource;

import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import com.scottcgi.common.exception.ServiceException;
import com.scottcgi.common.spring.dao.Page;
import com.scottcgi.common.spring.service.CrudService;

/**
* 提供CRUD操作的Action
*
* @author scott.Cgi
* @since  2010-8-25
*/
public abstract class CrudAction<T, PK extends Serializable> extends BaseAction
implements ModelDriven<T>, Preparable {

private static final long	serialVersionUID	= 7909985827753544081L;

@Resource(name = "crudService")
private CrudService<T, PK> crudService;

// 实体对象列表
protected List<T> entityList = Collections.<T>emptyList();
// 实体对象
protected T entity;
// 实体类主键
protected PK id;
// 实体类的名字
protected String entityClassName = "";

// 默认分页排序,子类选择性覆盖
protected String orderBy = "id desc";
// 分页对象
protected Page<T> page;

// 本页第一条数据在总数据中的位置
private String start;
// 分页大小
private String limit;
// 批量删除的id
private String[] ids;

/**
* 根据泛型类型实例化对象
*
* @return 泛型类型的对象
* @throws Exception
*/
@SuppressWarnings("unchecked")
public T instanceAnnotationObject() throws Exception {
Class<?> cl = this.getClass();
// 得到泛型类型参数数组就是<>里面的2个值
Type[] types = ((ParameterizedType) cl.getGenericSuperclass()).getActualTypeArguments();
try {
return ((Class<T>) types[0]).newInstance();
} catch (Exception e) {
log.error("crudAction的泛型类型实例化失败! 错误信息: {}", e.getMessage());
throw e;
}
}

/* (non-Javadoc)
* @see com.opensymphony.xwork2.Preparable#prepare()
*/
@SuppressWarnings("unchecked")
@Override
public void prepare() throws Exception {
// 利用反射新建enity对象
this.entity = this.instanceAnnotationObject();
this.entityClassName = this.entity.getClass().getSimpleName();

String id = this.request.getParameter("id");
if(id != null) {
// 根据主键从数据库查找entity
try {
this.id = (PK) Long.valueOf(id);
T dataEntity = crudService.read((Class<T>)entity.getClass(), this.id);
if(dataEntity != null) {
this.entity = dataEntity;
} else {
this.entity = this.instanceAnnotationObject();
}
} catch(NumberFormatException e) {
log.error("实体对象对象[{}]的id:{}转换数字失败!", this.entityClassName, id);
} catch (Exception e) {
log.error("读取实体对象[{}]失败! id: {}, 错误原因 : {}",new Object[]{
this.entityClassName,
id,
e.getMessage()
});

throw e;
}
}
}

/* (non-Javadoc)
* @see com.opensymphony.xwork2.ModelDriven#getModel()
*/
@Override
public T getModel() {
return entity;
}

/**
* 读取实体类对象
*
* @return 操作是否成功
*/
public boolean doRead() {
// prepare已经完成此功能
// 使用getModel()获得实体类
log.info("成功读取实体对象[{}]", this.entityClassName);
return true;
}

/**
* 获得实体类分页列表
*
* @return 操作是否成功
*/
public boolean doListPage() {
try {
int pageSize = Integer.valueOf(limit);
// 计算当前页面号
int pageNo = Integer.valueOf(start) / pageSize + 1;

this.page = crudService.readPage(entity.getClass(), pageNo, pageSize, this.orderBy);
} catch (ServiceException e) {
log.error("读取实体类[{}]列表失败! 错误原因 : {}", new Object[]{
entity.getClass().getName(),
e.getMessage()
});

return false;
}

log.info("成功读取实体对象[{}]列表", this.entityClassName);
return true;
}

/**
* 获得实体类所有对象列表
*
* @return 操作是否成功
*/
@SuppressWarnings("unchecked")
public boolean doList() {
try{
this.entityList = crudService.readList((Class<T>)entity.getClass());
} catch(ServiceException e){
log.error("读取实体类[{}]列表失败! 错误原因: {}", this.entityClassName, e.getMessage());
return false;
}

return true;
}

/**
* 创建实体类数据
*
* @return 操作是否成功
*/
public boolean doCreate() {
try {
crudService.createOrUpdate(entity);
} catch (ServiceException e) {
log.error("创建实体对象[{}]失败! 错误原因  : {}", this.entityClassName, e.getMessage());

return false;
}

log.info("成功创建实体对象[{}]", this.entityClassName);
return true;
}

/**
* 更新实体类数据
*
* @return 操作是否成功
*/
public boolean doUpdate() {
try {
crudService.createOrUpdate(entity);
} catch (ServiceException e) {
log.error("更新实体对象[{}]失败! 错误原因  : {}", this.entityClassName, e.getMessage());

return false;
}

log.info("成功更新实体对象[{}]", this.entityClassName);
return true;
}

/**
* 删除实体对象数据
*
* @return 操作是否成功
*/
public boolean doDelete() {
try {
crudService.delete(entity);
} catch (ServiceException e) {
log.error("删除实体对象[{}]失败! 错误原因  : {}", this.entityClassName, e.getMessage());

return false;
}

log.info("成功删除实体对象[{}]", this.entityClassName);
return true;
}

/**
* 批量删除
*
* @return 操作是否成功
*/
public boolean doBatchDelete() {
try {
StringBuilder sb = new StringBuilder();

for(String id : ids) {
sb.append(id).append(",");
}

if(sb.lastIndexOf(",") == sb.length() - 1) {
sb.deleteCharAt(sb.length() - 1);
}

log.info("批量删除的实体类[{}]的ids: {}", this.entityClassName, sb.toString());

crudService.batchDelete(this.entityClassName, sb.toString());
} catch(ServiceException e) {
log.error("批量删除实体对象[{}]失败! 错误原因: {}", this.entityClassName, e.getMessage());

return false;
}

return true;
}

/**
* @return the entity
*/
public T getEntity() {
return entity;
}

/**
* @param entity the entity to set
*/
public void setEntity(T entity) {
this.entity = entity;
}

/**
* @return the id
*/
public PK getId() {
return id;
}

/**
* @param id the id to set
*/
public void setId(PK id) {
this.id = id;
}

/**
* @return the page
*/
public Page<T> getPage() {
return page;
}

/**
* @param page the page to set
*/
public void setPage(Page<T> page) {
this.page = page;
}

/**
* @return the start
*/
public String getStart() {
return start;
}

/**
* @param start the start to set
*/
public void setStart(String start) {
this.start = start;
}

/**
* @return the limit
*/
public String getLimit() {
return limit;
}

/**
* @param limit the limit to set
*/
public void setLimit(String limit) {
this.limit = limit;
}

/**
* @return the ids
*/
public String[] getIds() {
return ids;
}

/**
* @param ids the ids to set
*/
public void setIds(String[] ids) {
this.ids = ids;
}
}


可以看到crudAction是泛型的,子类继承的时候需要提供,实体类类型和主键类型。prepare()方法是关键,首先利用反射使用泛型参数new 了一个实体类型的对象,以后利用页面的 实体类对象的id 去数据库查找。 这样一来,如果有id那么entity对象就是数据库的持久化对象,如果没有id则是一个new 出来的非持久化对象。

接下来,会执行装配参数拦截器,无论是否是持久化对象,都会把页面的参数装配到entitiy中。 在后来,所有的crud方法,我们使用的就是这个entity,或保存,或删除,或怎么都可以了。

我把Crud方法里面的异常吃掉了,把方法转换成boolean返回值的了。这是为了让子类有机会去做日志记录和信息返回。

下面看一个使用的例子:

@Namespace("/test")
public class TestAction extends CrudAction<Test, Long> {
@Action("createOrUpdateTest")
public String createOrUpdateTest(){
String result = "{success:true}";

// this.entity这里拿到的对象已经是装配要页面参数的了

// 调用父类方法,页面参数有id就是更新,没有id就是新增
if(this.doUpdate()) {
log.info("更新Test成功!");
} else {
log.error("更新Test失败!");
result = "{success:false}";
}

this.setAjaxInputStream(result);
return AJAX;
}
// 其它几个操作都差不多
}


这里使用了一种比较特别的使用ajax的方法。

见这里: http://blog.csdn.net/tom_221x/archive/2010/09/03/5862267.aspx

后面贴上curdService和entityDao:

* Copyright (c) scott.cgi Rights Reserved.
package com.scottcgi.common.spring.service;

import java.io.Serializable;
import java.util.List;

import org.springframework.stereotype.Service;

import com.scottcgi.common.exception.ServiceException;
import com.scottcgi.common.spring.dao.Page;

/**
* 提供Entity的CURD操作
*
* @author scott.cgi
* @since  2010-8-25
*/
@Service("crudService")
public class CrudService<T, PK extends Serializable> extends BaseService {

/**
* 读取
*
* @param entityClass  实体对象类型对象
* @param id           主键
* @return             实体对象
*/
public T read(Class<T> entityClass, PK id) throws ServiceException {
try {
return this.entityDao.get(entityClass, id);
} catch(Exception e) {
log.error("Dao 异常 = {}", e.getMessage());
throw new ServiceException("Dao 操作失败!");
}
}

/**
* 读取实体类列表
*
* @param entityClass  实体类的类型
* @return			        实体类对象列表
* @throws ServiceException
*/
public List<T> readList(Class<T> entityClass)throws ServiceException {
try{
return this.entityDao.loadAll(entityClass);
} catch(Exception e) {
log.error("Dao 异常  = {}", e.getMessage());
throw new ServiceException("Dao 操作失败!");
}
}

/**
* 分页读取
*
* @param entity   实体对象
* @param pageNo   页面号
* @param pageSize 分页大小
* @param orderBy  可选的排序字段和升序或降序关键字
* @return         page对象
*/
public Page<T> readPage(Class<?> entityClass, int pageNo, int pageSize, Object... orderBy) throws ServiceException {
String name = entityClass.getName();
String countHql = "select count(*) from " + name;
String hql = "from " + name;

if(orderBy != null) {
if(orderBy.length == 1) {
hql += " order by " + orderBy[0];
}
}

try {
return this.entityDao.queryPage(pageNo, pageSize, countHql, hql);
} catch(Exception e) {
log.error("Dao 异常 = {}", e.getMessage());
throw new ServiceException("Dao 操作失败!");
}
}

/**
* 新建或更新
*
* @param entity 实体对象
*/
public void createOrUpdate(T entity) throws ServiceException {
try {
this.entityDao.saveOrUpdate(entity);
} catch(Exception e) {
log.error("Dao 异常 = {}", e.getMessage());
throw new ServiceException("Dao 操作失败!");
}
}

/**
* 删除
*
* @param entity 实体对象
*/
public void delete(T entity) throws ServiceException {
try {
this.entityDao.delete(entity);
} catch(Exception e) {
log.error("Dao 异常 = {}", e.getMessage());
throw new ServiceException("Dao 操作失败!");
}
}

/**
* 批量删除
*
* @param className       实体类名
* @param ids 				实体对象一个或多个id
* @throws ServiceException
*/
public void batchDelete(String className, String ids) throws ServiceException {
try {
List<T> l = this.entityDao.find("from " + className + " where id in (" + ids + ")");
this.entityDao.deleteAll(l);
} catch(Exception e) {
log.error("Dao 异常 = {}", e.getMessage());
throw new ServiceException("Dao 操作失败!");
}
}
}


/*
* Copyright (c) scott.cgi Rights Reserved.
* Email: scott.cgi@gmail.com
*/
package com.scottcgi.common.spring.dao;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**
* 继承spring HibernateDaoSupport
* 提供hibernate entity基本操作
*
* @author scott.Cgi
* @since 2010-08-21
*
*/
public class EntityDao extends HibernateDaoSupport {
/**
* 根据主键查找实体类,找不到返回null
*
* @param id  实体类主键
* @return    实体类对象
* @throws DataAccessException
*/
public <T, PK extends Serializable> T get(Class<T> entityClass, PK id) throws DataAccessException {
return this.getHibernateTemplate().get(entityClass, id);
}

/**
* 根据主键,加载实体类
*
* @param <T>
* @param <PK>
* @param entity  实体类对象
* @param id      主键
* @throws DataAccessException
*/
public <T, PK extends Serializable> void load(T entity, PK id) throws DataAccessException {
this.getHibernateTemplate().load(entity, id);
}

/**
* 更新或创建实体对象
*
* @param  entity  实体对象
* @throws DataAccessException
*/
public <T> void saveOrUpdate(T entity) throws DataAccessException {
this.getHibernateTemplate().saveOrUpdate(entity);
}

/**
* 删除实体对象
*
* @param  entity  实体对象
* @throws DataAccessException
*/
public <T> void delete(T entity) throws DataAccessException {
this.getHibernateTemplate().delete(entity);
}

/**
* 重新装载实体对象
*
* @param  entity 实体对象
* @throws DataAccessException
*/
public <T> void refresh(T entity) throws DataAccessException {
this.getHibernateTemplate().refresh(entity);
}

/**
* 解除实体对象与hibernate session的关系
*
* @param entity  实体对象
* @throws DataAccessException
*/
public <T> void evict(T entity) throws DataAccessException {
this.getHibernateTemplate().evict(entity);
}

/**
* 根据查询语句查询
*
* @param <T>
*            实体类的类型
* @param hql
*            查询语句
* @param values
*            查询参数
* @return 查询结果集
* @throws DataAccessException
*
*/
@SuppressWarnings("unchecked")
public <T> List<T> find(String hql, Object... values)
throws DataAccessException {
return this.getHibernateTemplate().find(hql, values);
}

/**
* 根据hibernate注解或映射文件的查询语句查询
*
* @param <T>
*            实体类的类型
* @param queryName
*            查询语句的名称 (语句包含?占位符)
* @param values
*            参数名称 (?占位符的值)
* @return 结果集
* @throws DataAccessException
*
*/
@SuppressWarnings("unchecked")
public <T> List<T> findByNamedQuery(String queryName, Object... values)
throws DataAccessException {
return this.getHibernateTemplate().findByNamedQuery(queryName, values);
}

/**
* 根据hql语句进行更新或删除
*
* @param hql
*            查询语句
* @param values
*            查询参数
* @return 操作影响的行数
* @throws DataAccessException
*/
public int update(String hql, Object... values)
throws DataAccessException {
return this.getHibernateTemplate().bulkUpdate(hql, values);
}

/**
* 执行回调接口,获得使用原生hibernate sessoin的能力
*
* @param <T>
*            泛型类型
* @param action
*            hibernate回调接口
* @return 泛型结果
* @throws DataAccessException
*/
public <T> T execute(HibernateCallback<T> action)
throws DataAccessException {
return this.getHibernateTemplate().execute(action);
}

/**
* 根据查询语句分页
*
* @param <T>      泛型参数
* @param pageNo   当前页数
* @param pageSize 每页容量
* @param countHql 计算总记录数的查询语句
* @param hql      查询语句
* @param values   查询参数
* @return         分页结果集
* @throws DataAccessException
*/
@SuppressWarnings("unchecked")
public <T> Page<T> queryPage(final int pageNo, final int pageSize,
String countHql, final String hql, final Object... values)
throws DataAccessException {

List<Long> countlist = this.getHibernateTemplate().find(countHql, values);
long totalCount = countlist.get(0);

final int startIndex = (pageNo - 1) * pageSize;

if (totalCount < 1) {
return new Page<T>();
}

List<T> result = this.getHibernateTemplate().execute(
new HibernateCallback<List<T>>() {
@Override
public List<T> doInHibernate(Session session)
throws HibernateException, SQLException {
Query query = session.createQuery(hql);
if (values != null) {
for (int i = 0; i < values.length; i++) {
query.setParameter(i, values[i]);
}
}
return query.setFirstResult(startIndex)
.setMaxResults(pageSize).list();
}
});

return new Page<T>(totalCount, pageNo, pageSize, result);
}

/**
* 刷新hibernate seesion为触发的数据库操作
*
* @throws DataAccessException
*/
public void flush() throws DataAccessException {
this.getHibernateTemplate().flush();
}

/**
* 移除hibernate session cache的所有对象
* 取消所有为触发的数据库操作
*
* @throws DataAccessException
*/
public void clean() throws DataAccessException {
this.getHibernateTemplate().clear();
}

/**
* 删除实体对象集合
*
* @param <T>       实体类的类型
* @param entities  实体对象集合
* @throws DataAccessException
*/
public <T> void deleteAll(Collection<T> entities) throws DataAccessException {
this.getHibernateTemplate().deleteAll(entities);
}

/**
* 保存或更新实体对象集合
*
* @param <T>       实体类的类型
* @param entities  实体对象集合
* @throws DataAccessException
*/
public <T> void saveOrUpdateAll(Collection<T> entities) throws DataAccessException {
this.getHibernateTemplate().saveOrUpdateAll(entities);
}

/**
* 获得所有实体类对象
*
* @param <T>  		       泛型参数
* @param entityClass  实体类的类型
* @return			        实体类对象列表
* @throws DataAccessException
*/
public <T> List<T> loadAll(Class<T> entityClass) throws DataAccessException {
return this.getHibernateTemplate().loadAll(entityClass);
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: