东西好多啊,我的编写进度是跟不上了。把重点日志好好整理一下!以前编写的练习主要是练习基本功,现今的练习是要是怎么快怎么来。比如分页功能,以前是靠自己手动一点一点的写。而如今一个插件搞定,而且功能十分强大。
说简单的些,再怎么来就还是那点东西,CURD操作!今天早上佟佟领着大家将那个SimpleHibernateDao和ReflectionUtils看了一下,这两个东西是操作DAO与数据库交互的核心类。SimpleHibernateDao与汤兄弟那个实现原理是一样的,但这个功能更系统、全面。ReflectionUtils是对JAVA反射功能的增强。
我们知道ORM并不难,所以今天的剩下内容就是对表单的校验,分前台校验和后台校验。这里主要使用了JQuery的插件和其他小工具jar包。
由于内容比较多且零散,我本打算将这个项目系统的跟进。但是现在看来那是不可能的,所以我也得改变一下策略。对于这个项目,我只总结重点内容,比如昨天的SSH框架整合。以后也将只整理重点内容。似乎在这里的每一个项目,学校都考虑对同学们之前学习的知识进行提升,而不是进行熟练的复习,这样也好!因为如果只用SSH和简单的表单操作,那做这个项目将是一件十分容易的事。现在学校让我们接触一些扩展应用,这是非常好的!
一、SimpleHibernateDao和ReflectionUtils
Java的泛型和反射在应用中起着非常大的作用,极大的简化了开发和解耦。我们曾一度为第一个项目OA中运用的BaseDao<T>而兴奋,它在实际的项目开发中应该是应用广泛。今天佟佟将整理后的这个工具与我们分享,太好了。
程序中直接使用SimpleHibernateDao,而SimpleHibernateDao又使用ReflectionUtils。所以我从SimpleHibernateDao开始。
package cn.itcast.ems.orm.hibernate;
import java.io.Serializable; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.hibernate.Criteria; import org.hibernate.Hibernate; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.CriteriaSpecification; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.Restrictions; import org.hibernate.metadata.ClassMetadata; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.Assert; import cn.itcast.ems.utils.ReflectionUtils;
/** * 在 Service 层直接使用, 也可以扩展泛型 DAO 子类使用 * T: Dao 操作的对象类型 * PK: 主键类型 * @author Administrator * */ public class SimpleHibernateDao<T, PK extends Serializable> { protected Logger logger = LoggerFactory.getLogger(getClass());
protected SessionFactory sessionFactory;
protected Class<T> entityClass;
/** * 用于 Dao 层子类使用的构造函数 * 通过子类的泛型定义取得对象类型 Class * * 例如: public class EmployeeDao extends SimpleHibernateDao<User, String> */ public SimpleHibernateDao(){ this.entityClass = ReflectionUtils.getSuperGenericType(getClass()); }
/** * 用于省略 Dao 层, 在 Service 层直接使用通用 SimpleHibernateDao 的构造函数 * 在构造函数中定义对象类型 Class * @param sessionFactory * @param entityClass */ public SimpleHibernateDao(SessionFactory sessionFactory,Class<T> entityClass) { this.sessionFactory = sessionFactory; this.entityClass = entityClass; }
public SessionFactory getSessionFactory() { return sessionFactory; }
public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; }
/** * 获取当前 Session * @return */ public Session getSession(){ return sessionFactory.getCurrentSession(); }
/** * 保存新增或修改的对象 * @param entity */ public void save(T entity){ Assert.notNull(entity, "entity 不能为空"); getSession().saveOrUpdate(entity); logger.debug("save entity: {}", entity); }
public void delete(PK id){ Assert.notNull(id, "id 不能为空"); delete(get(id)); logger.debug("delete entity {},id is {}", entityClass.getSimpleName(), id); }
/** * 删除对象 * @param entity: 持久化对象或"瞬态"对象 */ public void delete(T entity){ Assert.notNull(entity, "entity 不能为空"); getSession().delete(entity); logger.debug("delete entity: {}", entity); }
/** * 按 id 获取对象 * @param id * @return */ @SuppressWarnings("unchecked") public T get(PK id){ Assert.notNull(id, "id不能为空"); return (T) getSession().get(entityClass, id); }
/** * 获取对象的主键名. * @return */ public String getIdName(){ ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass); return meta.getIdentifierPropertyName(); }
/** * 通过 Set 将不唯一的对象列表唯一化 * 主要用于 HQL/Criteria 预加载关联集合形成重复记录, 又不方便使用 distinct 查询语句时 * @param <X> * @param list * @return */ @SuppressWarnings("unchecked") public <X> List<X> distinct(List list){ Set<X> set = new LinkedHashSet<X>(list); return new ArrayList<X>(set); }
/** * 为 Criteria 添加 distinct transformer * @param criteria * @return */ public Criteria distinct(Criteria criteria){ criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY); return criteria; }
/** * 为 Query 添加 distinct transformer * @param query * @return */ public Query distinct(Query query){ query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY); return query; }
/** * 初始化对象. * 使用 load() 方法得到的仅是对象的代理, 在传到视图层前需要进行初始化 * * 只初始化 entity 的直接属性, 但不会初始化延迟加载的关联集合和属性 * 如需初始化关联属性, 可执行: Hibernate.initialize(user.getRoles()); * @param entity */ public void initEntity(T entity){ Hibernate.initialize(entity); }
public void initEntity(List<T> entityList){ for(T entity: entityList){ Hibernate.initialize(entity); } }
/** * 根据 Criterion 条件创建 Criteria * @param criterions * @return */ public Criteria createCriteria(Criterion ...criterions){ Criteria criteria = getSession().createCriteria(entityClass);
for(Criterion c: criterions){ criteria.add(c); }
return criteria; }
/** * 按 Criteria 查询唯一对象 * @param criterions: 数量可变的 Criterion * @return */ public T findUnique(Criterion...criterions){ return (T) createCriteria(criterions).uniqueResult(); }
/** * 按 Criteria 查询对象列表 * @param criterions: 数量可变的 Criterion * @return */ @SuppressWarnings("unchecked") public List<T> find(Criterion... criterions){ return createCriteria(criterions).list(); }
/** * 根据查询 HQL 与参数列表创建 Query 对象 * @param queryString * @param values * @return */ public Query createQuery(String queryString, Map<String, Object> values){ //String hql = "FROM Employee e where e.loginname = :loginname"; Assert.hasText(queryString, "queryString不能为空"); Query query = getSession().createQuery(queryString);
if(values != null){ query.setProperties(values); }
return query; }
/** * 根据查询 HQL 与参数列表创建 Query 对象 * @param queryString * @param values: 数来那个可变的参数, 按顺序绑定 * @return */ public Query createQuery(String queryString, Object... values){ Assert.hasText(queryString, "queryString不能为空"); Query query = getSession().createQuery(queryString);
if(values != null){ for(int i = 0; i < values.length; i++){ query.setParameter(i, values[i]); } }
return query; }
/** * 执行 hql 进行批量修改/删除操作 * @param hql * @param values * @return */ public int batchExecute(String hql, Map<String, Object> values){ return createQuery(hql, values).executeUpdate(); }
/** * 执行 hql 进行批量修改/删除操作 * @param hql * @param values * @return */ public int batchExecute(String hql, Object... values){ return createQuery(hql, values).executeUpdate(); }
/** * 按 HQL 查询唯一对象 * @param <X> * @param hql * @param values * @return */ @SuppressWarnings("unchecked") public <X> X findUnique(String hql, Map<String, Object> values){ return (X) createQuery(hql, values).uniqueResult(); }
/** * 按 HQL 查询唯一对象 * @param <X> * @param hql * @param values * @return */ @SuppressWarnings("unchecked") public <X> X findUnique(String hql, Object... values){ return (X) createQuery(hql, values).uniqueResult(); }
/** * 按 HQL 查询对象列表 * @param <X> * @param hql * @param values * @return */ @SuppressWarnings("unchecked") public <X> List<X> find(String hql, Map<String, Object> values){ return createQuery(hql, values).list(); }
/** * 按 HQL 查询对象列表 * @param <X> * @param hql * @param values * @return */ @SuppressWarnings("unchecked") public <X> List<X> find(String hql, Object... values){ return createQuery(hql, values).list(); }
/** * 按 id 列表获取对象列表 * @param ids * @return */ public List<T> findByIds(List<PK> ids){ return find(Restrictions.in(getIdName(), ids)); }
/** * 按属性查找唯一对象, 匹配方式为相等 * @param propertyName * @param value * @return */ @SuppressWarnings("unchecked") public T findUniqueBy(String propertyName, Object value){ Assert.hasText(propertyName, "propertyName不能为空"); Criterion criterion = Restrictions.eq(propertyName, value); return (T) createCriteria(criterion).uniqueResult(); }
/** * 按属性查找对象列表, 匹配方式为相等 * @param propertyName * @param value * @return */ public List<T> findBy(String propertyName, Object value){ Assert.hasText(propertyName, "propertyName不能为空");
Criterion criterion = Restrictions.eq(propertyName, value); return find(criterion); }
/** * 获取全部对象 * @return */ public List<T> getAll(){ return find(); } } |
对java反射的增强:
package cn.itcast.ems.utils;
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Date; import java.util.ArrayList; import java.util.Collection; import java.util.List;
import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.beanutils.converters.DateConverter; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.Assert;
/** * 反射的 Utils 函数集合 * 提供访问私有变量, 获取泛型类型 Class, 提取集合中元素属性等 Utils 函数 * @author Administrator * */ public class ReflectionUtils {
private static Logger logger = LoggerFactory.getLogger(ReflectionUtils.class);
/** * 将反射时的"检查异常"转换为"运行时异常" * @return */ public static IllegalArgumentException convertToUncheckedException(Exception ex){ if(ex instanceof IllegalAccessException || ex instanceof IllegalArgumentException || ex instanceof NoSuchMethodException){ throw new IllegalArgumentException("反射异常", ex); }else{ throw new IllegalArgumentException(ex); } }
/** * 转换字符串类型到 toType 类型 * @param value: 待转换的字符串 * @param toType: 提供类型信息的 Class, 可以是基本数据类型的包装类或指定格式日期型 * @return */ public static Object convertValue(Object value, Class<?> toType){ try { DateConverter dc = new DateConverter();
dc.setUseLocaleFormat(true); dc.setPatterns(new String[]{"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss"});
ConvertUtils.register(dc, Date.class);
return ConvertUtils.convert(value, toType); } catch (Exception e) { e.printStackTrace(); throw convertToUncheckedException(e); } }
/** * 提取集合中的对象的属性(通过 getter 方法), 组成 List * @param collection: 来源集合 * @param propertyName: 要提取的属性名 * @return */ @SuppressWarnings("unchecked") public static List fetchElementPropertyToList(Collection collection, String propertyName){ List list = new ArrayList();
try { for(Object obj: collection){ list.add(PropertyUtils.getProperty(obj, propertyName)); } } catch (Exception e) { e.printStackTrace(); convertToUncheckedException(e); }
return list; }
/** * 提取集合中的对象属性(通过 getter 函数), 组合成由分隔符分隔的字符串 * @param collection: 来源集合 * @param propertyName: 要提取的属性名 * @param seperator: 分隔符 * @return */ @SuppressWarnings("unchecked") public static String fetchElementPropertyToString(Collection collection, String propertyName, String seperator){ List list = fetchElementPropertyToList(collection, propertyName); return StringUtils.join(list, seperator); }
/** * 通过反射, 获得定义 Class 时声明的父类的泛型参数的类型 * 如: public EmployeeDao extends BaseDao<Employee, String> * @param clazz * @param index * @return */ @SuppressWarnings("unchecked") public static Class getSuperClassGenricType(Class clazz, int index){ Type genType = clazz.getGenericSuperclass();
if(!(genType instanceof ParameterizedType)){ logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType"); return Object.class; }
Type [] params = ((ParameterizedType)genType).getActualTypeArguments();
if(index >= params.length || index < 0){ logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + params.length); return Object.class; }
if(!(params[index] instanceof Class)){ logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); return Object.class; }
return (Class) params[index]; }
/** * 通过反射, 获得 Class 定义中声明的父类的泛型参数类型 * 如: public EmployeeDao extends BaseDao<Employee, String> * @param <T> * @param clazz * @return */ @SuppressWarnings("unchecked") public static<T> Class<T> getSuperGenericType(Class clazz){ return getSuperClassGenricType(clazz, 0); }
/** * 循环向上转型, 获取对象的 DeclaredMethod * @param object * @param methodName * @param parameterTypes * @return */ public static Method getDeclaredMethod(Object object, String methodName, Class<?>[] parameterTypes){ Assert.notNull(object, "object 不能为空");
for(Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){ try { //superClass.getMethod(methodName, parameterTypes); return superClass.getDeclaredMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { //Method 不在当前类定义, 继续向上转型 } //.. }
return null; }
/** * 使 filed 变为可访问 * @param field */ public static void makeAccessible(Field field){ if(!Modifier.isPublic(field.getModifiers())){ field.setAccessible(true); } }
/** * 循环向上转型, 获取对象的 DeclaredField * @param object * @param filedName * @return */ public static Field getDeclaredField(Object object, String filedName){ Assert.notNull(object, "object 不能为空"); Assert.hasText(filedName, "fieldName");
for(Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){ try { return superClass.getDeclaredField(filedName); } catch (NoSuchFieldException e) { //Field 不在当前类定义, 继续向上转型 } } return null; }
/** * 直接调用对象方法, 而忽略修饰符(private, protected) * @param object * @param methodName * @param parameterTypes * @param parameters * @return * @throws InvocationTargetException * @throws IllegalArgumentException */ public static Object invokeMethod(Object object, String methodName, Class<?> [] parameterTypes, Object [] parameters) throws InvocationTargetException{
Method method = getDeclaredMethod(object, methodName, parameterTypes);
if(method == null){ throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + object + "]"); }
method.setAccessible(true);
try { return method.invoke(object, parameters); } catch(IllegalAccessException e) { logger.error("不可能抛出的异常:{}", e.getMessage()); }
return null; }
/** * 直接设置对象属性值, 忽略 private/protected 修饰符, 也不经过 setter * @param object * @param fieldName * @param value */ public static void setFieldValue(Object object, String fieldName, Object value){ Field field = getDeclaredField(object, fieldName);
if (field == null) throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
makeAccessible(field);
try { field.set(object, value); } catch (IllegalAccessException e) { logger.error("不可能抛出的异常:{}", e.getMessage()); } }
/** * 直接读取对象的属性值, 忽略 private/protected 修饰符, 也不经过 getter * @param object * @param fieldName * @return */ public static Object getFieldValue(Object object, String fieldName){ Field field = getDeclaredField(object, fieldName);
if (field == null) throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
makeAccessible(field);
Object result = null;
try { result = field.get(object); } catch (IllegalAccessException e) { logger.error("不可能抛出的异常{}", e.getMessage()); }
return result; } } |
二、Struts的struts-menu插件、JQuery的Validate校验插件和displaytag-1.2分页插件
1. struts-menu插件:
我们的菜单栏使用的是多见的侧边树型菜单,在OA项目中我们使用的是JQuery的插件。但这次我们使用的是Struts的插件struts-menu。
将strtus-men中jar包拷贝到工程中,在struts-config.xml文件中注册struts-men插件:
<plug-in className="net.sf.navigator.menu.MenuPlugIn"> <set-property property="menuConfig" value="/WEB-INF/classes/menu-config.xml" /> </plug-in> |
将例子程序中的menu-config.xml文件添加到工程中,我们的菜单内容就是在这个配置文件里配置的,包含点击菜单时,菜单项的请求URL。
2.表单校验:jquery-validate
OK,这个VeryEasy!JQuery的插件用起来十分方便快捷。将jquery.validate.js和messages_cn.js添加到工程中,并在页面引入这两个JS文件。
为我们想校验的表单添加JavaScript代码:
$("#employeeForm").validate({rules: { loginname: { required: true, minlength: 6 } }}); |
校验规则,我们完全可以参看例子程序和帮助文档。
3.分页查询:displaytag-1.2
这个工具功能十分强悍,它可以根据我们的配置显示分页信息,并具有排序和将表格数据导出为pdf、excel、xml...文件格式的功能。但遗憾的是我们需要将所有符合条件的作息全部查询出来并传递给页面。所以超过一百万条记录的查询不建议使用此插件。
将displaytag-1.2.jar文件添加到我们的工程,并在显示查询分页信息的页面添加代码:
<display:table name="test" class="simple nocol" defaultsort="1"> <display:column property="city" group="1" /> <display:column property="project" /> <display:column property="amount" format="{0,number,0.00} $" headerClass="r" class="r" total="true" /> <display:column property="count" format="{0,number,0}" headerClass="r" class="r" total="true" /> </display:table> |
效果图如下:
Struts和JQuery有很多插件供我们选择,今天又接触到了一些新的应用。很愉快!在以后的工作或学习中,当我们需要某些表单控制或特殊效果进,都可以到网上搜索一下是否有相关的插件,现在的一般应用都有插件。不必我们为实现某些效果或功能而自己去编写代码了,这样很好!我们可以将时间用在更有价值的地方!