废话不说了,下面来介绍一下我自己在工作中封装的一套持久层封装。 该封装是在spring+JPA
的基础上完成的(以前用自定义注解写过一套JDBC的应用,不过联查起来太费事了)
类结构图如下1、BaseDao 主要是封装并进行普通的增删改查的操作,该类我设计成 protected 的访问权限,其目的是不想让别的层
出现SQL语句
public abstract class BaseDaoForJPA { @PersistenceContext private EntityManager entityManager; /** * 保存实体,实体必须使用符合JPA规范的注解进行配置 * (具体请查阅JPA EntityManager.persist 文档说明) */ protected final void executeSave(Object entity){ this.entityManager.persist(entity); } /** * 更新实体,实体必须使用符合JPA规范的注解进行配置 * (具体请查阅JPA EntityManager.merge 文档说明) */ protected final void executeUpdate(Object entity){ this.merge(entity); } /** * 根据主键ID查询实体,实体必须使用符合JPA规范的注解进行配置 * @param id 主键 * @param clazz 需要查询的实体类 */ protected final <T>T queryForEntity(Object id,Class<T> clazz){ T entity = entityManager.find(clazz, id); return entity; } /** * 根据实体Class 查询改实体所有的记录, 实体必须使用符合JPA规范的注解进行配置 * 由于是查询所有记录,输出效率考虑 请慎重使用 * @param clazz 需要查询的实体类 */ protected final <T> List<T> queryForList(Class<T> clazz){ return this.queryForList(clazz, null, null); } /** * 更新实体,实体必须使用符合JPA规范的注解进行配置 * @param HQL 语言 <code>update User o set o.userName = ? where o.id = ?</code> * @param 预处理参数 <code>new Object{"张三",1} ; </code> */ protected final boolean executeUpdate(String updateHQL,Object[] parameterArray){ return this.execute(updateHQL, parameterArray); } /** * 更新实体,实体必须使用符合JPA规范的注解进行配置 * @param HQL 语言 <code>update User o set o.userName = ? where o.id = ?</code> */ protected final boolean executeUpdate(String updateHQL){ return this.executeUpdate(updateHQL, null); } /** * 删除实体,实体必须使用符合JPA规范的注解进行配置 */ protected final <T> void executeDelete(T entity){ this.entityManager.remove(entity); } /** * 根据主键ID删除实体,实体必须使用符合JPA规范的注解进行配置 * @param id 主键 * @param clazz 需要删除实体类 */ protected final <T> void executeDelete(Class<T> clazz, Object id){ this.executeDelete(this.queryForEntity(id, clazz)); } /** * 删除实体,实体必须使用符合JPA规范的注解进行配置 * @param HQL 语言 <code>delete User o where o.id = ?</code> */ protected final <T> boolean executeDelete(String deleteHQL,Object[]parameterArray){ return this.execute(deleteHQL, parameterArray); } /** * 根据实体Class,条件,参数 查询记录, 实体必须使用符合JPA规范的注解进行配置 * @param clazz 需要查询的实体类 * @param conditions put("userName","like") * @param parameterArray new Object[]{"%zhang%"} */ protected final <T> List <T> queryForList(Class<T> clazz,LinkedHashMap<String,String> conditions,Object[]parameterArray){ return this.queryForList(clazz, conditions, parameterArray, null); } /** * 根据实体Class,条件,参数 查询记录,可以对相应的记录进行排序 实体必须使用符合JPA规范的注解进行配置 * @param clazz 需要查询的实体类 * @param conditions put("userName","like") * @param parameterArray new Object[]{"%zhang%"} * @param order put("id","asc") */ protected final <T> List <T> queryForList(Class<T> clazz,LinkedHashMap<String,String> conditions,Object[]parameterArray,LinkedHashMap<String,String>orderBy){ String queryHQL = getQueryString(clazz,conditions,orderBy); return queryForList(queryHQL,parameterArray); } /** * 使用HQL 进行实体的查询 实体必须使用符合JPA规范的注解进行配置 * @param queryHQL select o from User o where o.id = 36 */ protected final <T> List <T> queryForList(String queryHQL){ return this.queryForList(queryHQL, null); } /** * 根据实体Class 查询改实体所有的记录,可以对相应的记录进行排序 实体必须使用符合JPA规范的注解进行配置 * 由于是查询所有记录,输出效率考虑 请慎重使用 * @param clazz 需要查询的实体类 * @param order put("id","asc") */ protected final <T> List<T> queryForList(Class<T>clazz,LinkedHashMap<String,String>orderBy){ return this.queryForList(clazz, null, null, orderBy); } /** * 使用HQL 进行实体的查询 实体必须使用符合JPA规范的注解进行配置 * @param queryHQL select o from User o where o.id =? * @param parameterArray new Object[]{36} */ protected final <T> List <T> queryForList(String queryHQL,Object[]parameterArray){ return this.queryForList(queryHQL, parameterArray, 0, 0); } /** * 使用HQL查询整数(任何可以返回整数的HQL语句) 实体必须使用符合JPA规范的注解进行配置 * @param queryHQL select o.id from User o where o.id =36 */ protected final int queryForInt(String queryIntHQL){ return this.queryForInt(queryIntHQL, null); } /** * 使用HQL查询整数(任何可以返回整数的HQL语句) 实体必须使用符合JPA规范的注解进行配置 * @param queryHQL select o.id from User o where o.id =36 * @param parameterArray new Object[]{36} */ protected final int queryForInt(String queryIntHQL,Object[]parameterArray){ Query query = entityManager.createQuery(queryIntHQL); setQueryParameterValues(parameterArray,query); int result = Integer.parseInt(query.getSingleResult().toString()); return result; } final List queryForList(String queryHQL,Object[]parameterArray,int firstResult,int maxResult){ Query query = entityManager.createQuery(queryHQL); setQueryParameterValues(parameterArray,query); if(firstResult>=0) query.setFirstResult(firstResult); if(maxResult>0) query.setMaxResults(maxResult); return query.getResultList(); } final String getOrderBy(LinkedHashMap<String,String> orderBy){ if(orderBy==null||orderBy.size()==0)return ""; StringBuilder orderByBuilder = new StringBuilder(" order by "); Set<String> orderFields = orderBy.keySet(); for(String orderField : orderFields){ orderByBuilder.append(" o.").append(orderField).append(" ").append(orderBy.get(orderField)).append(","); } orderByBuilder.delete(orderByBuilder.length()-1, orderByBuilder.length()); return orderByBuilder.toString(); } final void setQueryParameterValues(Object[]parameterArray,Query query){ if(parameterArray==null||parameterArray.length==0)return; for(int i=0 ; i<parameterArray.length;i++){ query.setParameter(i+1, parameterArray[i]); } } final <T> String getQueryString(Class<T> clazz,LinkedHashMap<String,String> conditions,LinkedHashMap<String,String>orderBy){ StringBuilder queryBuilder = new StringBuilder(" select o from "); queryBuilder.append(getEntityFullName(clazz)).append(" o ").append(getCondtitons(conditions)).append(getOrderBy(orderBy)); return queryBuilder.toString(); } final <T> String getEntityFullName(Class<T> clazz){ return clazz.getName(); } final String getCondtitons(LinkedHashMap<String,String> conditions){ StringBuilder conditionsBuilder = new StringBuilder(" where 1=1 "); if(conditions==null||conditions.size()==0){ return conditionsBuilder.toString(); } Set<String>conditonFields = conditions.keySet(); for(String conditionField : conditonFields){ conditionsBuilder.append(" and o."); conditionsBuilder.append(conditionField); conditionsBuilder.append(" "); conditionsBuilder.append(conditions.get(conditionField)); conditionsBuilder.append(" ? "); } return conditionsBuilder.toString(); } private final boolean execute(String execuetHQL,Object[]parameterArray){ Query query = entityManager.createQuery(execuetHQL); this.setQueryParameterValues(parameterArray, query); return query.executeUpdate()>0?true:false; } private final <T> T merge(T entity){ return entityManager.merge(entity); } }
2、PaginationDaoSupport ,该类继承BaseDao,其目的是在原有的基础上面扩充分页功能,该类设计成抽象类,方法设计成protected . 其目的是不想让别的层出现SQL语句
public abstract class PagingationDaoSupportForJPA extends BaseDaoForJPA { /** * 根据实体 当前页码,单页记录数 Class,条件,参数 分页查询记录,并且排序 实体必须使用符合JPA规范的注解进行配置 * @param currentPage 当前页码 * @param pageSize 单页记录数 * @param preparedParameterArray new Object[]{13} * @param queryForListQHL * </br>HQL语句 <code>select o from User o where o.id >? * </br>或者from User o where o.id >?</code> * */ protected final <T>PageObject<T> queryForPaginationList(int currentPage, int pageSize, String queryForListHQL,Object[] preparedParameterArray) { int dataCount = queryCount(queryForListHQL, preparedParameterArray); PageObject<T> pageObject = new PageObject<T>(dataCount, currentPage,pageSize); pageObject.setPageList(queryForList(queryForListHQL, preparedParameterArray, pageObject.getStartPoint(),pageObject.getEndPoint())); return pageObject; } /** * 根据实体 当前页码,单页记录数 Class,条件,参数 分页查询记录,并且排序 实体必须使用符合JPA规范的注解进行配置 * @param currentPage 当前页码 * @param pageSize 单页记录数 * @param queryForListQHL * </br>HQL语句 <code>select o from User o where o.id > 10 * </br>或者from User o where o.id >10</code> */ protected final <T>PageObject<T> queryForPaginationList(int currentPage, int pageSize, String queryForListHQL){ return this.queryForPaginationList(currentPage, pageSize, queryForListHQL,null); } /** * 根据实体 当前页码,单页记录数 Class,条件,参数 分页查询记录,并且排序 实体必须使用符合JPA规范的注解进行配置 * @param currentPage 当前页码 * @param pageSize 单页记录数 * @param clazz 需要查询的实体类 * @param conditions put("userName","like") * @param parameterArray new Object[]{"%zhang%"} * @param orderBy put("id","asc") */ protected final<T>PageObject<T> queryForPaginationList(int currentPage,int pageSize,Class<T> clazz,LinkedHashMap<String,String>conditions,Object[]preparedParameterArray,LinkedHashMap<String,String> orderBy){ String queryForListHQL = this.getQueryString(clazz, conditions, orderBy); return this.queryForPaginationList(currentPage, pageSize, queryForListHQL, preparedParameterArray); } /** * 根据实体 当前页码,单页记录数 Class,条件,参数 分页查询记录 实体必须使用符合JPA规范的注解进行配置 * @param currentPage 当前页码 * @param pageSize 单页记录数 * @param clazz 需要查询的实体类 * @param conditions put("userName","like") * @param parameterArray new Object[]{"%zhang%"} */ protected final<T>PageObject<T>queryForPaginationList(int currentPage,int pageSize,Class<T> clazz,LinkedHashMap<String,String>conditions,Object[]preparedParameterArray){ return queryForPaginationList(currentPage, pageSize,clazz,conditions,preparedParameterArray,null); } /** * 根据实体 当前页码,单页记录数 Class 分页查询记录 实体必须使用符合JPA规范的注解进行配置 * @param currentPage 当前页码 * @param pageSize 单页记录数 * @param clazz 需要查询的实体类 */ protected final<T>PageObject<T> queryForPaginationList(int currentPage,int pageSize,Class<T> clazz){ return queryForPaginationList(currentPage, pageSize,clazz,null,null); } /** * 根据实体 当前页码,单页记录数 Class 分页查询记录 实体必须使用符合JPA规范的注解进行配置 默认单页数据记录数为10 * @param currentPage 当前页码 * @param clazz 需要查询的实体类 */ protected final<T>PageObject<T> queryForPaginationList(int currentPage,Class<T> clazz){ return queryForPaginationList(currentPage,clazz,null,null); } /** * 根据实体 当前页码,单页记录数 Class,条件,参数 分页查询记录 实体必须使用符合JPA规范的注解进行配置 默认单页数据记录数为10 * @param currentPage 当前页码 * @param clazz 需要查询的实体类 * @param conditions put("userName","like") * @param parameterArray new Object[]{"%zhang%"} */ protected final<T>PageObject<T>queryForPaginationList(int currentPage,Class<T> clazz,LinkedHashMap<String,String>conditions,Object[]preparedParameterArray){ return queryForPaginationList(currentPage,clazz,conditions,preparedParameterArray,null); } /** * 根据实体 当前页码,单页记录数 Class,条件,参数 分页查询记录,并且排序 实体必须使用符合JPA规范的注解进行配置 默认单页数据记录数为10 * @param currentPage 当前页码 * @param pageSize 单页记录数 * @param preparedParameterArray new Object[]{13} * @param queryForListQHL * </br>HQL语句 <code>select o from User o where o.id >? * </br>或者from User o where o.id >?</code> * */ protected final <T>PageObject<T> queryForPaginationList(int currentPage,String queryForListHQL,Object[] preparedParameterArray) { int dataCount = queryCount(queryForListHQL, preparedParameterArray); PageObject<T> pageObject = new PageObject<T>(dataCount, currentPage); pageObject.setPageList(queryForList(queryForListHQL, preparedParameterArray, pageObject.getStartPoint(),pageObject.getEndPoint())); return pageObject; } /** * 根据实体 当前页码,单页记录数 Class,条件,参数 分页查询记录,并且排序 实体必须使用符合JPA规范的注解进行配置 默认单页数据记录数为10 * @param currentPage 当前页码 * @param pageSize 单页记录数 * @param queryForListQHL * </br>HQL语句 <code>select o from User o where o.id > 10 * </br>或者from User o where o.id >10</code> */ protected final <T>PageObject<T> queryForPaginationList(int currentPage, String queryForListHQL){ return this.queryForPaginationList(currentPage,queryForListHQL,null); } /** * 根据实体 当前页码,单页记录数 Class,条件,参数 分页查询记录,并且排序 实体必须使用符合JPA规范的注解进行配置 默认单页数据记录数为10 * @param currentPage 当前页码 * @param clazz 需要查询的实体类 * @param conditions put("userName","like") * @param parameterArray new Object[]{"%zhang%"} * @param orderBy put("id","asc") */ protected final<T>PageObject<T> queryForPaginationList(int currentPage,Class<T> clazz,LinkedHashMap<String,String>conditions,Object[]preparedParameterArray,LinkedHashMap<String,String> orderBy){ String queryForListHQL = this.getQueryString(clazz, conditions, orderBy); return this.queryForPaginationList(currentPage, queryForListHQL, preparedParameterArray); } private final int queryCount(String queryForListHQL,Object[] preparedParameterArray) { StringBuilder countHQLBuilder = new StringBuilder(" select count(*) "); try { countHQLBuilder.append(queryForListHQL.substring(queryForListHQL.toLowerCase().indexOf("from"))); } catch (RuntimeException ex) { throw new BadSqlGrammarException("SQL : ", queryForListHQL, null); } return queryForInt(countHQLBuilder.toString(), preparedParameterArray); } }
3、PageObject 分页的封装对象
/** * @author Yi Su * @param <T> 泛型:like List<T></br> */ public class PageObject<T> { private int currentPage; private int dataCount; private int pageSize; private int maxPage; private List<T> pageList; private int startPoint; private int endPoint; private PaginationStrategy paginationStrategy; List<Integer> pageNumberList; public List<Integer> getPageNumberList() { return pageNumberList; } /** * @param dataCount 总数据数 * @param currentPage 当前页 * @param pageSize 页面大小 * 注: 默认分页逻辑 startPoint & endPoint 是JPA实现</br> * 如果需要使用到其他数据库请实现PaginationStrategy接口</br> * 使用该类的 setPaginationStrategy 方法获得相应的实现</br> */ public PageObject(int dataCount, int currentPage) { this.pageSize = 10; this.dataCount = dataCount; this.currentPage = currentPage; maxPage = dataCount % pageSize == 0 ? dataCount / pageSize : dataCount/ pageSize + 1; paginationStrategy = new PaginaionStrategyForJPA(); setDataPoint(paginationStrategy); setPageNumberList(); } private void setPageNumberList(){ this.pageNumberList = new ArrayList<Integer>(); // 1 2 3 4 5 6 7 int pageNumber = currentPage-3; while(pageNumber<=0){ pageNumber++; } int count = 0; while(pageNumber<=maxPage-1&&count < 7){ count ++; this.pageNumberList.add(pageNumber); pageNumber++; } } /** * @param dataCount 总数据数 * @param currentPage 当前页 * @param pageSize 页面大小 * 注: 默认分页逻辑 startPoint & endPoint 是oracle实现</br> * 如果需要使用到其他数据库请实现PaginationStrategy接口</br> * 使用该类的 setPaginationStrategy 方法获得相应的实现</br> */ public PageObject(int dataCount, int currentPage,int pageSize) { if(pageSize==0)throw new IllegalArgumentException(" 单页数据设置 [pageSize]不能为小于等于 0 当前[pageSize]的值是 : "+pageSize); this.pageSize = pageSize; this.dataCount = dataCount; this.currentPage = currentPage; maxPage = dataCount % pageSize == 0 ? dataCount / pageSize : dataCount/ pageSize + 1; paginationStrategy = new PaginaionStrategyForJPA(); setDataPoint(paginationStrategy); setPageNumberList(); } private void setDataPoint(PaginationStrategy paginationStrategy){ paginationStrategy.setDataPoint(currentPage, pageSize); startPoint = paginationStrategy.getStartPoint(); endPoint = paginationStrategy.getEndPoint(); } /** * 默认的实现是 PaginationStrategyForJPA * 如果需要实现其他数据库的分页逻辑,请继承 PaginationStrategy,传入当前页面和页面大小设置不同数据库的分页 * @param paginationStrategy */ public void setPaginationStrategy(PaginationStrategy paginationStrategy){ this.paginationStrategy = paginationStrategy; setDataPoint(paginationStrategy); } /** * 获得当前页码 * @return 当前页码 */ public int getCurrentPage() { return currentPage; } /** * 获得单页数据数 * @return 单页数据数 */ public int getPageSize() { return pageSize; } /** * 获得一共有多少页 * @return 一共有多少页 */ public int getMaxPage() { return maxPage; } /** * 获得总数据数 * @return 总数据数 */ public int getDataCount() { return dataCount; } /** * 获得分页起始点 * @return 分页起始点 */ public int getStartPoint() { return startPoint; } /** * 获得分页结束点 * @return 分页结束点 */ public int getEndPoint() { return endPoint; } /** * 设置分页对象集合 * @return 分页对象集合 */ public List<T> getPageList() { return pageList; } /** * 获得分页对象集合 * @param pageList 分页对象集合 */ public void setPageList(List<T> pageList) { this.pageList =pageList; } @Override public String toString() { StringBuilder pageObjectPrintBuilder = new StringBuilder(); pageObjectPrintBuilder.append(" 当前页码 ").append(currentPage).append(" 总数据数 ").append(dataCount); pageObjectPrintBuilder.append(" 起始点 ").append(startPoint).append(" 结束点 ").append(endPoint); pageObjectPrintBuilder.append(" 总页数 ").append(maxPage). append(" 单页数据数 ").append(pageSize); return pageObjectPrintBuilder.toString(); } }
4、PaginationStrategy 分页策略,当时因为使用的是JDBC编码,需要考虑到应付以后会发生的变化(不同数据库的分页处理时不一样的)
public interface PaginationStrategy { /** * 获取数据结束点 * @return */ public int getStartPoint(); /** * 获取数据起始点 * @return */ public int getEndPoint(); /** * 设置起分页始点和结束点 * @param currentPage * @param pageSize */ public void setDataPoint(int currentPage,int pageSize); }
5、基于JPA的分页策略实现
public class PaginaionStrategyForJPA implements PaginationStrategy{ private int startPoint; private int endPoint; public int getEndPoint() { return endPoint; } public int getStartPoint() { return startPoint; } public void setDataPoint(int currentPage,int pageSize) { startPoint= (currentPage - 1) * pageSize; endPoint = pageSize; } }
6、使用方法
(1) 编写Dao 层代码,我接口就省略了
public class UserDaoImpl extends PagingationDaoSupportForJPA implements UserDao { public void addUser(SysUser user) { executeSave(user); } public boolean deleteUser(int id) { this.executeDelete(SysUser.class, id); return true; } public void modifyUser(SysUser user) { this.executeUpdate(user); } public PageObject<SysUser> queryUserList(int currentPage, int pageSize, SysUser user) { StringBuilder queryForListHQLBuilder = new StringBuilder(" select o from User o where 1=1 "); List<Object> preparedParameterList = new ArrayList<Object>(); if(user.getUserName()!=null&&user.getUserName().trim().length()>0){ queryForListHQLBuilder.append(" and o.userName like ?"); preparedParameterList.add("%"+user.getUserName()+"%"); } return queryForPaginationList(currentPage, pageSize, queryForListHQLBuilder.toString(), preparedParameterList.toArray()); } public SysUser queryUserById(int userId) { return this.queryForEntity(userId, SysUser.class); }
(2) Service层代码,我接口就省略了
@Service(value="userService") public class UserServiceImpl extends UserService { @Autowired private UserDao userDao; @Transactional protected void addUser(SysUser user) { this.userDao.addUser(user); } @Transactional protected void deleteUser(int id) { if(id <= 0 ){ throw new BusinessException(" User.userId is necessary"); } this.userDao.deleteUser(id); } @Transactional protected void modifyUser(SysUser user) { userDao.modifyUser(user); } @Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly = true) public SysUser queryUserById(int userId) { return userDao.queryUserById(userId); } @Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly = true) public PageObject<SysUser> queryUserList(int currentPage, int pageSize,SysUser user) { return userDao.queryUserList(currentPage, pageSize, user); } }
注:我之所以把增,更,删 设置为protected,主要为了在抽象类上编写模版,来处理业务 。
举个例子,更新和添加主要差别是主键
@Transactional public void operUser(SysUser user){ if(user.getId()>0) { this.modifyUser(user); } else{ this.addUser(user); } }
总结: 以上功能是我在工作中总结和编写的。如果有任何疑问可以和我联系和探讨