Mybatis常用分页插件实现快速分页处理技巧
在未分享整个查询分页的执行代码之前,先了解一下执行流程。
1.总体上是利用mybatis的插件拦截器,在sql执行之前拦截,为查询语句加上limitXX
2.用一个Page对象,贯穿整个执行流程,这个Page对象需要用Java编写前端分页组件
3.用一套比较完整的三层entity,dao,service支持这个分页架构
4.这个分页用到的一些辅助类
注:分享的内容较多,这边的话我就不把需要的jar一一列举,大家使用这个分页功能的时候缺少什么就去晚上找什么jar包即可,尽可能用maven包导入因为maven能减少版本冲突等比较好的优势。
我只能说尽可能让大家快速使用这个比较好用的分页功能,如果讲得不明白,欢迎加我QQ一起探讨1063150576,。莫喷哈!还有就是文章篇幅可能会比较大,不过花点时间,把它看完并实践一下一定会收获良多。
第一步:既然主题是围绕怎么进行分页的,我们就从mybatis入手,首先,我们把mybatis相关的两个比较重要的配置文件拿出来做简要的理解,一个是mybatis-config.xml,另外一个是实体所对应的mapper配置文件,我会在配置文件上写好注释,大家一看就会明白。
mybatis-config.xml
<!DOCTYPEconfiguration PUBLIC"-//mybatis.org//DTDConfig3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--全局参数--> <settings> <!--使全局的映射器启用或禁用缓存。--> <settingname="cacheEnabled"value="false"/> <!--全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。--> <settingname="lazyLoadingEnabled"value="true"/> <!--当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。--> <settingname="aggressiveLazyLoading"value="true"/> <!--是否允许单条sql返回多个数据集(取决于驱动的兼容性)default:true--> <settingname="multipleResultSetsEnabled"value="true"/> <!--是否可以使用列的别名(取决于驱动的兼容性)default:true--> <settingname="useColumnLabel"value="true"/> <!--允许JDBC生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。default:false--> <settingname="useGeneratedKeys"value="false"/> <!--指定MyBatis如何自动映射数据基表的列NONE:不隐射PARTIAL:部分FULL:全部--> <settingname="autoMappingBehavior"value="PARTIAL"/> <!--这是默认的执行类型(SIMPLE:简单;REUSE:执行器可能重复使用preparedstatements语句;BATCH:执行器可以重复执行语句和批量更新)--> <settingname="defaultExecutorType"value="SIMPLE"/> <!--使用驼峰命名法转换字段。--> <settingname="mapUnderscoreToCamelCase"value="true"/> <!--设置本地缓存范围session:就会有数据的共享statement:语句范围(这样就不会有数据的共享)defalut:session--> <settingname="localCacheScope"value="SESSION"/> <!--设置但JDBC类型为空时,某些驱动程序要指定值,default:OTHER,插入空值时不需要指定类型--> <settingname="jdbcTypeForNull"value="NULL"/> <settingname="logPrefix"value="dao."/> </settings> <!--别名是一个较短的Java类型的名称--> <typeAliases> <typeAliastype="com.store.base.model.StoreUser" alias="User"></typeAlias> <typeAliastype="com.store.base.secondmodel.pratice.model.Product" alias="Product"></typeAlias> <typeAliastype="com.store.base.secondmodel.base.Page" alias="Page"></typeAlias> </typeAliases> <!--插件配置,这边为mybatis配置分页拦截器,这个分页拦截器需要我们自己实现--> <plugins> <plugininterceptor="com.store.base.secondmodel.base.pageinterceptor.PaginationInterceptor"/> </plugins> </configuration>
一个ProductMapper.xml作为测试对象,这个mapper文件就简单配置一个需要用到的查询语句
<?xmlversion="1.0"encoding="UTF-8"?> <!DOCTYPEmapperPUBLIC"-//mybatis.org//DTDMapper3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mappernamespace="com.store.base.secondmodel.pratice.dao.ProductDao"> <sqlid="baseColumns"> id,product_nameasproductName,product_noasproductNo,priceasprice </sql> <selectid="findList"resultType="com.store.base.secondmodel.pratice.model.Product"> select<includerefid="baseColumns"/>fromt_store_product </select> </mapper>
第二步:接下去主要针对这个分页拦截器进行深入分析学习,主要有以下几个类和其对应接口
(1)BaseInterceptor拦截器基础类
(2)PaginationInterceptor我们要使用的分页插件类,继承上面基础类
(3)SQLHelper主要是用来提前执行count语句,还有就是获取整个完整的分页语句
(4)Dialect,MysqlDialect,主要用来数据库是否支持limit语句,然后封装完整limit语句
以下是这几个类的分享展示
BaseInterceptor.java
packagecom.store.base.secondmodel.base.pageinterceptor; importjava.io.Serializable; importjava.util.Properties; importorg.apache.ibatis.logging.Log; importorg.apache.ibatis.logging.LogFactory; importorg.apache.ibatis.plugin.Interceptor; importcom.store.base.secondmodel.base.Global; importcom.store.base.secondmodel.base.Page; importcom.store.base.secondmodel.base.dialect.Dialect; importcom.store.base.secondmodel.base.dialect.MySQLDialect; importcom.store.base.util.Reflections; /** *Mybatis分页拦截器基类 *@authoryiyong_wu * */ publicabstractclassBaseInterceptorimplementsInterceptor,Serializable{ privatestaticfinallongserialVersionUID=1L; protectedstaticfinalStringPAGE="page"; protectedstaticfinalStringDELEGATE="delegate"; protectedstaticfinalStringMAPPED_STATEMENT="mappedStatement"; protectedLoglog=LogFactory.getLog(this.getClass()); protectedDialectDIALECT; /** *对参数进行转换和检查 *@paramparameterObject参数对象 *@parampage分页对象 *@return分页对象 *@throwsNoSuchFieldException无法找到参数 */ @SuppressWarnings("unchecked") protectedstaticPage<Object>convertParameter(ObjectparameterObject,Page<Object>page){ try{ if(parameterObjectinstanceofPage){ return(Page<Object>)parameterObject; }else{ return(Page<Object>)Reflections.getFieldValue(parameterObject,PAGE); } }catch(Exceptione){ returnnull; } } /** *设置属性,支持自定义方言类和制定数据库的方式 *<code>dialectClass</code>,自定义方言类。可以不配置这项 *<ode>dbms</ode>数据库类型,插件支持的数据库 *<code>sqlPattern</code>需要拦截的SQLID *@paramp属性 */ protectedvoidinitProperties(Propertiesp){ Dialectdialect=null; StringdbType=Global.getConfig("jdbc.type"); if("mysql".equals(dbType)){ dialect=newMySQLDialect(); } if(dialect==null){ thrownewRuntimeException("mybatisdialecterror."); } DIALECT=dialect; } }
PaginationInterceptor.java
packagecom.store.base.secondmodel.base.pageinterceptor; importjava.util.Properties; importorg.apache.ibatis.executor.Executor; importorg.apache.ibatis.mapping.BoundSql; importorg.apache.ibatis.mapping.MappedStatement; importorg.apache.ibatis.mapping.SqlSource; importorg.apache.ibatis.plugin.Intercepts; importorg.apache.ibatis.plugin.Invocation; importorg.apache.ibatis.plugin.Plugin; importorg.apache.ibatis.plugin.Signature; importorg.apache.ibatis.reflection.MetaObject; importorg.apache.ibatis.session.ResultHandler; importorg.apache.ibatis.session.RowBounds; importcom.store.base.secondmodel.base.Page; importcom.store.base.secondmodel.base.util.StringUtils; importcom.store.base.util.Reflections; /** *数据库分页插件,只拦截查询语句. *@authoryiyong_wu * */ @Intercepts({@Signature(type=Executor.class,method="query",args={ MappedStatement.class,Object.class,RowBounds.class, ResultHandler.class})}) publicclassPaginationInterceptorextendsBaseInterceptor{ privatestaticfinallongserialVersionUID=1L; @Override publicObjectintercept(Invocationinvocation)throwsThrowable{ finalMappedStatementmappedStatement=(MappedStatement)invocation.getArgs()[0]; Objectparameter=invocation.getArgs()[1]; BoundSqlboundSql=mappedStatement.getBoundSql(parameter); ObjectparameterObject=boundSql.getParameterObject(); //获取分页参数对象 Page<Object>page=null; if(parameterObject!=null){ page=convertParameter(parameterObject,page); } //如果设置了分页对象,则进行分页 if(page!=null&&page.getPageSize()!=-1){ if(StringUtils.isBlank(boundSql.getSql())){ returnnull; } StringoriginalSql=boundSql.getSql().trim(); //得到总记录数 page.setCount(SQLHelper.getCount(originalSql,null,mappedStatement,parameterObject,boundSql,log)); //分页查询本地化对象修改数据库注意修改实现 StringpageSql=SQLHelper.generatePageSql(originalSql,page,DIALECT); invocation.getArgs()[2]=newRowBounds(RowBounds.NO_ROW_OFFSET,RowBounds.NO_ROW_LIMIT); BoundSqlnewBoundSql=newBoundSql( mappedStatement.getConfiguration(),pageSql, boundSql.getParameterMappings(), boundSql.getParameterObject()); //解决MyBatis分页foreach参数失效start if(Reflections.getFieldValue(boundSql,"metaParameters")!=null){ MetaObjectmo=(MetaObject)Reflections.getFieldValue( boundSql,"metaParameters"); Reflections.setFieldValue(newBoundSql,"metaParameters",mo); } //解决MyBatis分页foreach参数失效end MappedStatementnewMs=copyFromMappedStatement(mappedStatement,newBoundSqlSqlSource(newBoundSql)); invocation.getArgs()[0]=newMs; } returninvocation.proceed(); } @Override publicObjectplugin(Objecttarget){ returnPlugin.wrap(target,this); } @Override publicvoidsetProperties(Propertiesproperties){ super.initProperties(properties); } privateMappedStatementcopyFromMappedStatement(MappedStatementms, SqlSourcenewSqlSource){ MappedStatement.Builderbuilder=newMappedStatement.Builder( ms.getConfiguration(),ms.getId(),newSqlSource, ms.getSqlCommandType()); builder.resource(ms.getResource()); builder.fetchSize(ms.getFetchSize()); builder.statementType(ms.getStatementType()); builder.keyGenerator(ms.getKeyGenerator()); if(ms.getKeyProperties()!=null){ for(StringkeyProperty:ms.getKeyProperties()){ builder.keyProperty(keyProperty); } } builder.timeout(ms.getTimeout()); builder.parameterMap(ms.getParameterMap()); builder.resultMaps(ms.getResultMaps()); builder.cache(ms.getCache()); returnbuilder.build(); } publicstaticclassBoundSqlSqlSourceimplementsSqlSource{ BoundSqlboundSql; publicBoundSqlSqlSource(BoundSqlboundSql){ this.boundSql=boundSql; } @Override publicBoundSqlgetBoundSql(ObjectparameterObject){ returnboundSql; } } }
SQLHelper.java
packagecom.store.base.secondmodel.base.pageinterceptor; importjava.sql.Connection; importjava.sql.PreparedStatement; importjava.sql.ResultSet; importjava.sql.SQLException; importjava.util.List; importjava.util.regex.Matcher; importjava.util.regex.Pattern; importorg.apache.ibatis.executor.ErrorContext; importorg.apache.ibatis.executor.ExecutorException; importorg.apache.ibatis.logging.Log; importorg.apache.ibatis.mapping.BoundSql; importorg.apache.ibatis.mapping.MappedStatement; importorg.apache.ibatis.mapping.ParameterMapping; importorg.apache.ibatis.mapping.ParameterMode; importorg.apache.ibatis.reflection.MetaObject; importorg.apache.ibatis.reflection.property.PropertyTokenizer; importorg.apache.ibatis.scripting.xmltags.ForEachSqlNode; importorg.apache.ibatis.session.Configuration; importorg.apache.ibatis.type.TypeHandler; importorg.apache.ibatis.type.TypeHandlerRegistry; importcom.store.base.secondmodel.base.Global; importcom.store.base.secondmodel.base.Page; importcom.store.base.secondmodel.base.dialect.Dialect; importcom.store.base.secondmodel.base.util.StringUtils; importcom.store.base.util.Reflections; /** *SQL工具类 *@authoryiyong_wu * */ publicclassSQLHelper{ /** *默认私有构造函数 */ privateSQLHelper(){ } /** *对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.DefaultParameterHandler * *@paramps表示预编译的SQL语句的对象。 *@parammappedStatementMappedStatement *@paramboundSqlSQL *@paramparameterObject参数对象 *@throwsjava.sql.SQLException数据库异常 */ @SuppressWarnings("unchecked") publicstaticvoidsetParameters(PreparedStatementps,MappedStatementmappedStatement,BoundSqlboundSql,ObjectparameterObject)throwsSQLException{ ErrorContext.instance().activity("settingparameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping>parameterMappings=boundSql.getParameterMappings(); if(parameterMappings!=null){ Configurationconfiguration=mappedStatement.getConfiguration(); TypeHandlerRegistrytypeHandlerRegistry=configuration.getTypeHandlerRegistry(); MetaObjectmetaObject=parameterObject==null?null: configuration.newMetaObject(parameterObject); for(inti=0;i<parameterMappings.size();i++){ ParameterMappingparameterMapping=parameterMappings.get(i); if(parameterMapping.getMode()!=ParameterMode.OUT){ Objectvalue; StringpropertyName=parameterMapping.getProperty(); PropertyTokenizerprop=newPropertyTokenizer(propertyName); if(parameterObject==null){ value=null; }elseif(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())){ value=parameterObject; }elseif(boundSql.hasAdditionalParameter(propertyName)){ value=boundSql.getAdditionalParameter(propertyName); }elseif(propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)&&boundSql.hasAdditionalParameter(prop.getName())){ value=boundSql.getAdditionalParameter(prop.getName()); if(value!=null){ value=configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length())); } }else{ value=metaObject==null?null:metaObject.getValue(propertyName); } @SuppressWarnings("rawtypes") TypeHandlertypeHandler=parameterMapping.getTypeHandler(); if(typeHandler==null){ thrownewExecutorException("TherewasnoTypeHandlerfoundforparameter"+propertyName+"ofstatement"+mappedStatement.getId()); } typeHandler.setParameter(ps,i+1,value,parameterMapping.getJdbcType()); } } } } /** *查询总纪录数 *@paramsqlSQL语句 *@paramconnection数据库连接 *@parammappedStatementmapped *@paramparameterObject参数 *@paramboundSqlboundSql *@return总记录数 *@throwsSQLExceptionsql查询错误 */ publicstaticintgetCount(finalStringsql,finalConnectionconnection, finalMappedStatementmappedStatement,finalObjectparameterObject, finalBoundSqlboundSql,Loglog)throwsSQLException{ StringdbName=Global.getConfig("jdbc.type"); finalStringcountSql; if("oracle".equals(dbName)){ countSql="selectcount(1)from("+sql+")tmp_count"; }else{ countSql="selectcount(1)from("+removeOrders(sql)+")tmp_count"; } Connectionconn=connection; PreparedStatementps=null; ResultSetrs=null; try{ if(log.isDebugEnabled()){ log.debug("COUNTSQL:"+StringUtils.replaceEach(countSql,newString[]{"\n","\t"},newString[]{"",""})); } if(conn==null){ conn=mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection(); } ps=conn.prepareStatement(countSql); BoundSqlcountBS=newBoundSql(mappedStatement.getConfiguration(),countSql, boundSql.getParameterMappings(),parameterObject); //解决MyBatis分页foreach参数失效start if(Reflections.getFieldValue(boundSql,"metaParameters")!=null){ MetaObjectmo=(MetaObject)Reflections.getFieldValue(boundSql,"metaParameters"); Reflections.setFieldValue(countBS,"metaParameters",mo); } //解决MyBatis分页foreach参数失效end SQLHelper.setParameters(ps,mappedStatement,countBS,parameterObject); rs=ps.executeQuery(); intcount=0; if(rs.next()){ count=rs.getInt(1); } returncount; }finally{ if(rs!=null){ rs.close(); } if(ps!=null){ ps.close(); } if(conn!=null){ conn.close(); } } } /** *根据数据库方言,生成特定的分页sql *@paramsqlMapper中的Sql语句 *@parampage分页对象 *@paramdialect方言类型 *@return分页SQL */ publicstaticStringgeneratePageSql(Stringsql,Page<Object>page,Dialectdialect){ if(dialect.supportsLimit()){ returndialect.getLimitString(sql,page.getFirstResult(),page.getMaxResults()); }else{ returnsql; } } /** *去除qlString的select子句。 *@paramhql *@return */ @SuppressWarnings("unused") privatestaticStringremoveSelect(StringqlString){ intbeginPos=qlString.toLowerCase().indexOf("from"); returnqlString.substring(beginPos); } /** *去除hql的orderBy子句。 *@paramhql *@return */ privatestaticStringremoveOrders(StringqlString){ Patternp=Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*",Pattern.CASE_INSENSITIVE); Matcherm=p.matcher(qlString); StringBuffersb=newStringBuffer(); while(m.find()){ m.appendReplacement(sb,""); } m.appendTail(sb); returnsb.toString(); } }
Dialect.java接口
packagecom.store.base.secondmodel.base.dialect; /** *类似hibernate的Dialect,但只精简出分页部分 *@authoryiyong_wu * */ publicinterfaceDialect{ /** *数据库本身是否支持分页当前的分页查询方式 *如果数据库不支持的话,则不进行数据库分页 * *@returntrue:支持当前的分页查询方式 */ publicbooleansupportsLimit(); /** *将sql转换为分页SQL,分别调用分页sql * *@paramsqlSQL语句 *@paramoffset开始条数 *@paramlimit每页显示多少纪录条数 *@return分页查询的sql */ publicStringgetLimitString(Stringsql,intoffset,intlimit); }
MySQLDialect.java
packagecom.store.base.secondmodel.base.dialect; /** *Mysql方言的实现 *@authoryiyong_wu * */ publicclassMySQLDialectimplementsDialect{ @Override publicbooleansupportsLimit(){ returntrue; } @Override publicStringgetLimitString(Stringsql,intoffset,intlimit){ returngetLimitString(sql,offset,Integer.toString(offset),Integer.toString(limit)); } /** *将sql变成分页sql语句,提供将offset及limit使用占位符号(placeholder)替换. *<pre> *如mysql *dialect.getLimitString("select*fromuser",12,":offset",0,":limit")将返回 *select*fromuserlimit:offset,:limit *</pre> * *@paramsql实际SQL语句 *@paramoffset分页开始纪录条数 *@paramoffsetPlaceholder分页开始纪录条数-占位符号 *@paramlimitPlaceholder分页纪录条数占位符号 *@return包含占位符的分页sql */ publicStringgetLimitString(Stringsql,intoffset,StringoffsetPlaceholder,StringlimitPlaceholder){ StringBuilderstringBuilder=newStringBuilder(sql); stringBuilder.append("limit"); if(offset>0){ stringBuilder.append(offsetPlaceholder).append(",").append(limitPlaceholder); }else{ stringBuilder.append(limitPlaceholder); } returnstringBuilder.toString(); } }
差不多到这边已经把整块分页怎么实现的给分享完了,但是我们还有更重要的任务,想要整个东西跑起来,肯定还要有基础工作要做,接下去我们分析整套Page对象以及它所依据的三层架构,还是用product作为实体进行分析。一整套三层架构讲下来,收获肯定又满满的。我们依次从entity->dao->service的顺序讲下来。
首先,针对我们的实体得继承两个抽象实体类BaseEntity与DataEntity
BaseEntity.java主要放置Page成员变量,继承它后就可以每个实体都拥有这个成员变量
packagecom.store.base.secondmodel.base; importjava.io.Serializable; importjava.util.Map; importjavax.xml.bind.annotation.XmlTransient; importorg.apache.commons.lang3.StringUtils; importorg.apache.commons.lang3.builder.ReflectionToStringBuilder; importcom.fasterxml.jackson.annotation.JsonIgnore; importcom.google.common.collect.Maps; importcom.store.base.model.StoreUser; /** *最顶层的Entity *@authoryiyong_wu * *@param<T> */ publicabstractclassBaseEntity<T>implementsSerializable{ privatestaticfinallongserialVersionUID=1L; /** *删除标记(0:正常;1:删除;2:审核;) */ publicstaticfinalStringDEL_FLAG_NORMAL="0"; publicstaticfinalStringDEL_FLAG_DELETE="1"; publicstaticfinalStringDEL_FLAG_AUDIT="2"; /** *实体编号(唯一标识) */ protectedStringid; /** *当前用户 */ protectedStoreUsercurrentUser; /** *当前实体分页对象 */ protectedPage<T>page; /** *自定义SQL(SQL标识,SQL内容) */ privateMap<String,String>sqlMap; publicBaseEntity(){ } publicBaseEntity(Stringid){ this(); this.id=id; } publicStringgetId(){ returnid; } publicvoidsetId(Stringid){ this.id=id; } /** *这个主要针对shiro执行插入更新的时候会调用,获取当前的用户 *@return */ @JsonIgnore @XmlTransient publicStoreUsergetCurrentUser(){ if(currentUser==null){ //currentUser=UserUtils.getUser(); } returncurrentUser; } publicvoidsetCurrentUser(StoreUsercurrentUser){ this.currentUser=currentUser; } @JsonIgnore @XmlTransient publicPage<T>getPage(){ if(page==null){ page=newPage<>(); } returnpage; } publicPage<T>setPage(Page<T>page){ this.page=page; returnpage; } @JsonIgnore @XmlTransient publicMap<String,String>getSqlMap(){ if(sqlMap==null){ sqlMap=Maps.newHashMap(); } returnsqlMap; } publicvoidsetSqlMap(Map<String,String>sqlMap){ this.sqlMap=sqlMap; } /** *插入之前执行方法,子类实现 */ publicabstractvoidpreInsert(); /** *更新之前执行方法,子类实现 */ publicabstractvoidpreUpdate(); /** *是否是新记录(默认:false),调用setIsNewRecord()设置新记录,使用自定义ID。 *设置为true后强制执行插入语句,ID不会自动生成,需从手动传入。 *@return */ publicbooleangetIsNewRecord(){ returnStringUtils.isBlank(getId()); } /** *全局变量对象 */ @JsonIgnore publicGlobalgetGlobal(){ returnGlobal.getInstance(); } /** *获取数据库名称 */ @JsonIgnore publicStringgetDbName(){ returnGlobal.getConfig("jdbc.type"); } @Override publicStringtoString(){ returnReflectionToStringBuilder.toString(this); } }
DataEntity.java,主要存储更新删除时间,创建用户,更新用户,逻辑删除标志等
packagecom.store.base.secondmodel.base; importjava.util.Date; importorg.hibernate.validator.constraints.Length; importcom.fasterxml.jackson.annotation.JsonFormat; importcom.fasterxml.jackson.annotation.JsonIgnore; importcom.store.base.model.StoreUser; /** *数据Entity *@authoryiyong_wu * *@param<T> */ publicabstractclassDataEntity<T>extendsBaseEntity<T>{ privatestaticfinallongserialVersionUID=1L; protectedStoreUsercreateBy;//创建者 protectedDatecreateDate;//创建日期 protectedStoreUserupdateBy;//更新者 protectedDateupdateDate;//更新日期 protectedStringdelFlag;//删除标记(0:正常;1:删除;2:审核) publicDataEntity(){ super(); this.delFlag=DEL_FLAG_NORMAL; } publicDataEntity(Stringid){ super(id); } /** *插入之前执行方法,需要手动调用 */ @Override publicvoidpreInsert(){ //不限制ID为UUID,调用setIsNewRecord()使用自定义ID //Useruser=UserUtils.getUser(); //if(StringUtils.isNotBlank(user.getId())){ //this.updateBy=user; //this.createBy=user; //} this.updateDate=newDate(); this.createDate=this.updateDate; } /** *更新之前执行方法,需要手动调用 */ @Override publicvoidpreUpdate(){ //Useruser=UserUtils.getUser(); //if(StringUtils.isNotBlank(user.getId())){ //this.updateBy=user; //} this.updateDate=newDate(); } //@JsonIgnore publicStoreUsergetCreateBy(){ returncreateBy; } publicvoidsetCreateBy(StoreUsercreateBy){ this.createBy=createBy; } @JsonFormat(pattern="yyyy-MM-ddHH:mm:ss") publicDategetCreateDate(){ returncreateDate; } publicvoidsetCreateDate(DatecreateDate){ this.createDate=createDate; } //@JsonIgnore publicStoreUsergetUpdateBy(){ returnupdateBy; } publicvoidsetUpdateBy(StoreUserupdateBy){ this.updateBy=updateBy; } @JsonFormat(pattern="yyyy-MM-ddHH:mm:ss") publicDategetUpdateDate(){ returnupdateDate; } publicvoidsetUpdateDate(DateupdateDate){ this.updateDate=updateDate; } @JsonIgnore @Length(min=1,max=1) publicStringgetDelFlag(){ returndelFlag; } publicvoidsetDelFlag(StringdelFlag){ this.delFlag=delFlag; } }
Product.java产品类
packagecom.store.base.secondmodel.pratice.model; importcom.store.base.secondmodel.base.DataEntity; /** *产品基础类 *2016年10月11日 *yiyong_wu */ publicclassProductextendsDataEntity<Product>{ privatestaticfinallongserialVersionUID=1L; privateStringproductName; privatefloatprice; privateStringproductNo; publicStringgetProductName(){ returnproductName; } publicvoidsetProductName(StringproductName){ this.productName=productName; } publicfloatgetPrice(){ returnprice; } publicvoidsetPrice(floatprice){ this.price=price; } publicStringgetProductNo(){ returnproductNo; } publicvoidsetProductNo(StringproductNo){ this.productNo=productNo; } }
怎么样,是不是看到很复杂的一个实体继承连关系,不过这有什么,越复杂就会越完整。接下来我就看看dao层,同样是三层,准备好接受洗礼吧
BaseDao.java预留接口
packagecom.store.base.secondmodel.base; /** *最顶层的DAO接口 *@authoryiyong_wu * */ publicinterfaceBaseDao{ } CrudDao.java针对增删改查的一个dao接口层 [java]viewplaincopyprint?在CODE上查看代码片派生到我的代码片 packagecom.store.base.secondmodel.base; importjava.util.List; /** *定义增删改查的DAO接口 *@authoryiyong_wu * *@param<T> */ publicinterfaceCrudDao<T>extendsBaseDao{ /** *获取单条数据 *@paramid *@return */ publicTget(Stringid); /** *获取单条数据 *@paramentity *@return */ publicTget(Tentity); /** *查询数据列表,如果需要分页,请设置分页对象,如:entity.setPage(newPage<T>()); *@paramentity *@return */ publicList<T>findList(Tentity); /** *查询所有数据列表 *@paramentity *@return */ publicList<T>findAllList(Tentity); /** *查询所有数据列表 *@seepublicList<T>findAllList(Tentity) *@return publicList<T>findAllList(); */ /** *插入数据 *@paramentity *@return */ publicintinsert(Tentity); /** *更新数据 *@paramentity *@return */ publicintupdate(Tentity); /** *删除数据(一般为逻辑删除,更新del_flag字段为1) *@paramid *@seepublicintdelete(Tentity) *@return */ publicintdelete(Stringid); /** *删除数据(一般为逻辑删除,更新del_flag字段为1) *@paramentity *@return */ publicintdelete(Tentity); }
ProductDao.javamybatis对应的接口mapper,同时也是dao实现,这边需要自定一个注解@MyBatisRepository
packagecom.store.base.secondmodel.pratice.dao; importcom.store.base.secondmodel.base.CrudDao; importcom.store.base.secondmodel.base.MyBatisRepository; importcom.store.base.secondmodel.pratice.model.Product; /** *TODO *2016年10月11日 *yiyong_wu */ @MyBatisRepository publicinterfaceProductDaoextendsCrudDao<Product>{ }
自定义注解MyBatisRepository.java,跟自定义注解相关,这里就不做过多的解读,网上资料一堆
packagecom.store.base.secondmodel.base; importjava.lang.annotation.Documented; importjava.lang.annotation.Retention; importjava.lang.annotation.Target; importjava.lang.annotation.RetentionPolicy; importjava.lang.annotation.ElementType; importorg.springframework.stereotype.Component; /** *标识MyBatis的DAO,方便{@linkorg.mybatis.spring.mapper.MapperScannerConfigurer}的扫描。 * *请注意要在spring的配置文件中配置扫描该注解类的配置 * *<beanid="mapperScannerConfigurer"class="org.mybatis.spring.mapper.MapperScannerConfigurer"> *<propertyname="sqlSessionFactoryBeanName"value="sqlSessionFactory"/> *<propertyname="basePackage"value="com.store.base.secondmodel"/> *<propertyname="annotationClass"value="com.store.base.secondmodel.base.MyBatisRepository"/> *</bean> *@authoryiyong_wu * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Component public@interfaceMyBatisRepository{ Stringvalue()default""; }
注意:跟ProductDao.java联系比较大的是ProductMapper.xml文件,大家可以看到上面那个配置文件的namespace是指向这个dao的路径的。
接下来我们就进入最后的service分析了,一样还是三层继承
BaseService.java
packagecom.store.base.secondmodel.base; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.transaction.annotation.Transactional; /** *Service的最顶层父类 *@authoryiyong_wu * */ @Transactional(readOnly=true) publicabstractclassBaseService{ //日志记录用的 protectedLoggerlogger=LoggerFactory.getLogger(getClass()); }
CrudService.java增删改查相关的业务接口实现
packagecom.store.base.secondmodel.base; importjava.util.List; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.transaction.annotation.Transactional; /** *增删改查Service基类 *@authoryiyong_wu * *@param<D> *@param<T> */ publicabstractclassCrudService<DextendsCrudDao<T>,TextendsDataEntity<T>> extendsBaseService{ /** *持久层对象 */ @Autowired protectedDdao; /** *获取单条数据 *@paramid *@return */ publicTget(Stringid){ returndao.get(id); } /** *获取单条数据 *@paramentity *@return */ publicTget(Tentity){ returndao.get(entity); } /** *查询列表数据 *@paramentity *@return */ publicList<T>findList(Tentity){ returndao.findList(entity); } /** *查询分页数据 *@parampage分页对象 *@paramentity *@return */ publicPage<T>findPage(Page<T>page,Tentity){ entity.setPage(page); page.setList(dao.findList(entity)); returnpage; } /** *保存数据(插入或更新) *@paramentity */ @Transactional(readOnly=false) publicvoidsave(Tentity){ if(entity.getIsNewRecord()){ entity.preInsert(); dao.insert(entity); }else{ entity.preUpdate(); dao.update(entity); } } /** *删除数据 *@paramentity */ @Transactional(readOnly=false) publicvoiddelete(Tentity){ dao.delete(entity); } }
ProductService.java,去继承CrudService接口,注意起注入dao和实体类型的一种模式
packagecom.store.base.secondmodel.pratice.service; importorg.springframework.stereotype.Service; importorg.springframework.transaction.annotation.Transactional; importcom.store.base.secondmodel.base.CrudService; importcom.store.base.secondmodel.pratice.dao.ProductDao; importcom.store.base.secondmodel.pratice.model.Product; /** *TODO *2016年10月11日 *yiyong_wu */ @Service @Transactional(readOnly=true) publicclassProductServiceextendsCrudService<ProductDao,Product>{ }
我想看到这里的同志已经很不耐烦了。但是如果你错过接下去的一段,基本上刚才看的就快等于白看了,革命的胜利就在后半段,因为整个分页功能围绕的就是一个Page对象,重磅内容终于要出来了,当你把Page对象填充到刚才那个BaseEntity上的时候,你会发现一切就完整起来了,废话不多说,Page对象如下
packagecom.store.base.secondmodel.base; importjava.io.Serializable; importjava.util.ArrayList; importjava.util.List; importjava.util.regex.Pattern; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; importcom.fasterxml.jackson.annotation.JsonIgnore; importcom.store.base.secondmodel.base.util.CookieUtils; importcom.store.base.secondmodel.base.util.StringUtils; /** *分页类 *@authoryiyong_wu * *@param<T> */ publicclassPage<T>implementsSerializable{ privatestaticfinallongserialVersionUID=1L; privateintpageNo=1;//当前页码 privateintpageSize=Integer.parseInt(Global.getConfig("page.pageSize"));//页面大小,设置为“-1”表示不进行分页(分页无效) privatelongcount;//总记录数,设置为“-1”表示不查询总数 privateintfirst;//首页索引 privateintlast;//尾页索引 privateintprev;//上一页索引 privateintnext;//下一页索引 privatebooleanfirstPage;//是否是第一页 privatebooleanlastPage;//是否是最后一页 privateintlength=6;//显示页面长度 privateintslider=1;//前后显示页面长度 privateList<T>list=newArrayList<>(); privateStringorderBy="";//标准查询有效,实例:updatedatedesc,nameasc privateStringfuncName="page";//设置点击页码调用的js函数名称,默认为page,在一页有多个分页对象时使用。 privateStringfuncParam="";//函数的附加参数,第三个参数值。 privateStringmessage="";//设置提示消息,显示在“共n条”之后 publicPage(){ this.pageSize=-1; } /** *构造方法 *@paramrequest传递repage参数,来记住页码 *@paramresponse用于设置Cookie,记住页码 */ publicPage(HttpServletRequestrequest,HttpServletResponseresponse){ this(request,response,-2); } /** *构造方法 *@paramrequest传递repage参数,来记住页码 *@paramresponse用于设置Cookie,记住页码 *@paramdefaultPageSize默认分页大小,如果传递-1则为不分页,返回所有数据 */ publicPage(HttpServletRequestrequest,HttpServletResponseresponse,intdefaultPageSize){ //设置页码参数(传递repage参数,来记住页码) Stringno=request.getParameter("pageNo"); if(StringUtils.isNumeric(no)){ CookieUtils.setCookie(response,"pageNo",no); this.setPageNo(Integer.parseInt(no)); }elseif(request.getParameter("repage")!=null){ no=CookieUtils.getCookie(request,"pageNo"); if(StringUtils.isNumeric(no)){ this.setPageNo(Integer.parseInt(no)); } } //设置页面大小参数(传递repage参数,来记住页码大小) Stringsize=request.getParameter("pageSize"); if(StringUtils.isNumeric(size)){ CookieUtils.setCookie(response,"pageSize",size); this.setPageSize(Integer.parseInt(size)); }elseif(request.getParameter("repage")!=null){ no=CookieUtils.getCookie(request,"pageSize"); if(StringUtils.isNumeric(size)){ this.setPageSize(Integer.parseInt(size)); } }elseif(defaultPageSize!=-2){ this.pageSize=defaultPageSize; } //设置排序参数 StringorderBy=request.getParameter("orderBy"); if(StringUtils.isNotBlank(orderBy)){ this.setOrderBy(orderBy); } } /** *构造方法 *@parampageNo当前页码 *@parampageSize分页大小 */ publicPage(intpageNo,intpageSize){ this(pageNo,pageSize,0); } /** *构造方法 *@parampageNo当前页码 *@parampageSize分页大小 *@paramcount数据条数 */ publicPage(intpageNo,intpageSize,longcount){ this(pageNo,pageSize,count,newArrayList<T>()); } /** *构造方法 *@parampageNo当前页码 *@parampageSize分页大小 *@paramcount数据条数 *@paramlist本页数据对象列表 */ publicPage(intpageNo,intpageSize,longcount,List<T>list){ this.setCount(count); this.setPageNo(pageNo); this.pageSize=pageSize; this.list=list; } /** *初始化参数 */ publicvoidinitialize(){ //1 this.first=1; this.last=(int)(count/(this.pageSize<1?20:this.pageSize)+first-1); if(this.count%this.pageSize!=0||this.last==0){ this.last++; } if(this.last<this.first){ this.last=this.first; } if(this.pageNo<=1){ this.pageNo=this.first; this.firstPage=true; } if(this.pageNo>=this.last){ this.pageNo=this.last; this.lastPage=true; } if(this.pageNo<this.last-1){ this.next=this.pageNo+1; }else{ this.next=this.last; } if(this.pageNo>1){ this.prev=this.pageNo-1; }else{ this.prev=this.first; } //2 if(this.pageNo<this.first){//如果当前页小于首页 this.pageNo=this.first; } if(this.pageNo>this.last){//如果当前页大于尾页 this.pageNo=this.last; } } /** *默认输出当前分页标签 *<divclass="page">${page}</div> */ @Override publicStringtoString(){ StringBuildersb=newStringBuilder(); if(pageNo==first){//如果是首页 sb.append("<liclass=\"disabled\"><ahref=\"javascript:\">«上一页</a></li>\n"); }else{ sb.append("<li><ahref=\"javascript:\"onclick=\""+funcName+"("+prev+","+pageSize+",'"+funcParam+"');\">«上一页</a></li>\n"); } intbegin=pageNo-(length/2); if(begin<first){ begin=first; } intend=begin+length-1; if(end>=last){ end=last; begin=end-length+1; if(begin<first){ begin=first; } } if(begin>first){ inti=0; for(i=first;i<first+slider&&i<begin;i++){ sb.append("<li><ahref=\"javascript:\"onclick=\""+funcName+"("+i+","+pageSize+",'"+funcParam+"');\">" +(i+1-first)+"</a></li>\n"); } if(i<begin){ sb.append("<liclass=\"disabled\"><ahref=\"javascript:\">...</a></li>\n"); } } for(inti=begin;i<=end;i++){ if(i==pageNo){ sb.append("<liclass=\"active\"><ahref=\"javascript:\">"+(i+1-first) +"</a></li>\n"); }else{ sb.append("<li><ahref=\"javascript:\"onclick=\""+funcName+"("+i+","+pageSize+",'"+funcParam+"');\">" +(i+1-first)+"</a></li>\n"); } } if(last-end>slider){ sb.append("<liclass=\"disabled\"><ahref=\"javascript:\">...</a></li>\n"); end=last-slider; } for(inti=end+1;i<=last;i++){ sb.append("<li><ahref=\"javascript:\"onclick=\""+funcName+"("+i+","+pageSize+",'"+funcParam+"');\">" +(i+1-first)+"</a></li>\n"); } if(pageNo==last){ sb.append("<liclass=\"disabled\"><ahref=\"javascript:\">下一页»</a></li>\n"); }else{ sb.append("<li><ahref=\"javascript:\"onclick=\""+funcName+"("+next+","+pageSize+",'"+funcParam+"');\">" +"下一页»</a></li>\n"); } returnsb.toString(); } /** *获取分页HTML代码 *@return */ publicStringgetHtml(){ returntoString(); } /** *获取设置总数 *@return */ publiclonggetCount(){ returncount; } /** *设置数据总数 *@paramcount */ publicvoidsetCount(longcount){ this.count=count; if(pageSize>=count){ pageNo=1; } } /** *获取当前页码 *@return */ publicintgetPageNo(){ returnpageNo; } /** *设置当前页码 *@parampageNo */ publicvoidsetPageNo(intpageNo){ this.pageNo=pageNo; } /** *获取页面大小 *@return */ publicintgetPageSize(){ returnpageSize; } /** *设置页面大小(最大500)//>500?500:pageSize; *@parampageSize */ publicvoidsetPageSize(intpageSize){ this.pageSize=pageSize<=0?10:pageSize; } /** *首页索引 *@return */ @JsonIgnore publicintgetFirst(){ returnfirst; } /** *尾页索引 *@return */ @JsonIgnore publicintgetLast(){ returnlast; } /** *获取页面总数 *@returngetLast(); */ @JsonIgnore publicintgetTotalPage(){ returngetLast(); } /** *是否为第一页 *@return */ @JsonIgnore publicbooleanisFirstPage(){ returnfirstPage; } /** *是否为最后一页 *@return */ @JsonIgnore publicbooleanisLastPage(){ returnlastPage; } /** *上一页索引值 *@return */ @JsonIgnore publicintgetPrev(){ if(isFirstPage()){ returnpageNo; }else{ returnpageNo-1; } } /** *下一页索引值 *@return */ @JsonIgnore publicintgetNext(){ if(isLastPage()){ returnpageNo; }else{ returnpageNo+1; } } /** *获取本页数据对象列表 *@returnList<T> */ publicList<T>getList(){ returnlist; } /** *设置本页数据对象列表 *@paramlist */ publicPage<T>setList(List<T>list){ this.list=list; initialize(); returnthis; } /** *获取查询排序字符串 *@return */ @JsonIgnore publicStringgetOrderBy(){ //SQL过滤,防止注入 Stringreg="(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|" +"(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)"; PatternsqlPattern=Pattern.compile(reg,Pattern.CASE_INSENSITIVE); if(sqlPattern.matcher(orderBy).find()){ return""; } returnorderBy; } /** *设置查询排序,标准查询有效,实例:updatedatedesc,nameasc */ publicvoidsetOrderBy(StringorderBy){ this.orderBy=orderBy; } /** *获取点击页码调用的js函数名称 *function${page.funcName}(pageNo){location="${ctx}/list-${category.id}${urlSuffix}?pageNo="+i;} *@return */ @JsonIgnore publicStringgetFuncName(){ returnfuncName; } /** *设置点击页码调用的js函数名称,默认为page,在一页有多个分页对象时使用。 *@paramfuncName默认为page */ publicvoidsetFuncName(StringfuncName){ this.funcName=funcName; } /** *获取分页函数的附加参数 *@return */ @JsonIgnore publicStringgetFuncParam(){ returnfuncParam; } /** *设置分页函数的附加参数 *@return */ publicvoidsetFuncParam(StringfuncParam){ this.funcParam=funcParam; } /** *设置提示消息,显示在“共n条”之后 *@parammessage */ publicvoidsetMessage(Stringmessage){ this.message=message; } /** *分页是否有效 *@returnthis.pageSize==-1 */ @JsonIgnore publicbooleanisDisabled(){ returnthis.pageSize==-1; } /** *是否进行总数统计 *@returnthis.count==-1 */ @JsonIgnore publicbooleanisNotCount(){ returnthis.count==-1; } /** *获取HibernateFirstResult */ publicintgetFirstResult(){ intfirstResult=(getPageNo()-1)*getPageSize(); if(firstResult>=getCount()){ firstResult=0; } returnfirstResult; } /** *获取HibernateMaxResults */ publicintgetMaxResults(){ returngetPageSize(); } }
看完这个Page对象应该稍微有点感觉了吧,然后我在胡乱贴一些相关用到的工具类吧,工具类的话我只稍微提一下,具体大家可以弄到自己的代码上好好解读。
PropertiesLoader.java用来获取resource文件夹下的常量配置文件
packagecom.store.base.secondmodel.base.util; importjava.io.IOException; importjava.io.InputStream; importjava.util.NoSuchElementException; importjava.util.Properties; importorg.apache.commons.io.IOUtils; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.core.io.DefaultResourceLoader; importorg.springframework.core.io.Resource; importorg.springframework.core.io.ResourceLoader; /** *Properties文件载入工具类.可载入多个properties文件, *相同的属性在最后载入的文件中的值将会覆盖之前的值,但以System的Property优先. *@authoryiyong_wu * */ publicclassPropertiesLoader{ privatestaticLoggerlogger=LoggerFactory.getLogger(PropertiesLoader.class); privatestaticResourceLoaderresourceLoader=newDefaultResourceLoader(); privatefinalPropertiesproperties; publicPropertiesLoader(String...resourcesPaths){ properties=loadProperties(resourcesPaths); } publicPropertiesgetProperties(){ returnproperties; } /** *取出Property,但以System的Property优先,取不到返回空字符串. */ privateStringgetValue(Stringkey){ StringsystemProperty=System.getProperty(key); if(systemProperty!=null){ returnsystemProperty; } if(properties.containsKey(key)){ returnproperties.getProperty(key); } return""; } /** *取出String类型的Property,但以System的Property优先,如果都为Null则抛出异常. */ publicStringgetProperty(Stringkey){ Stringvalue=getValue(key); if(value==null){ thrownewNoSuchElementException(); } returnvalue; } /** *取出String类型的Property,但以System的Property优先.如果都为Null则返回Default值. */ publicStringgetProperty(Stringkey,StringdefaultValue){ Stringvalue=getValue(key); returnvalue!=null?value:defaultValue; } /** *取出Integer类型的Property,但以System的Property优先.如果都为Null或内容错误则抛出异常. */ publicIntegergetInteger(Stringkey){ Stringvalue=getValue(key); if(value==null){ thrownewNoSuchElementException(); } returnInteger.valueOf(value); } /** *取出Integer类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容错误则抛出异常 */ publicIntegergetInteger(Stringkey,IntegerdefaultValue){ Stringvalue=getValue(key); returnvalue!=null?Integer.valueOf(value):defaultValue; } /** *取出Double类型的Property,但以System的Property优先.如果都为Null或内容错误则抛出异常. */ publicDoublegetDouble(Stringkey){ Stringvalue=getValue(key); if(value==null){ thrownewNoSuchElementException(); } returnDouble.valueOf(value); } /** *取出Double类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容错误则抛出异常 */ publicDoublegetDouble(Stringkey,IntegerdefaultValue){ Stringvalue=getValue(key); returnvalue!=null?Double.valueOf(value):defaultValue.doubleValue(); } /** *取出Boolean类型的Property,但以System的Property优先.如果都为Null抛出异常,如果内容不是true/false则返回false. */ publicBooleangetBoolean(Stringkey){ Stringvalue=getValue(key); if(value==null){ thrownewNoSuchElementException(); } returnBoolean.valueOf(value); } /** *取出Boolean类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容不为true/false则返回false. */ publicBooleangetBoolean(Stringkey,booleandefaultValue){ Stringvalue=getValue(key); returnvalue!=null?Boolean.valueOf(value):defaultValue; } /** *载入多个文件,文件路径使用SpringResource格式. */ privatePropertiesloadProperties(String...resourcesPaths){ Propertiesprops=newProperties(); for(Stringlocation:resourcesPaths){ InputStreamis=null; try{ Resourceresource=resourceLoader.getResource(location); is=resource.getInputStream(); props.load(is); }catch(IOExceptionex){ logger.error("Couldnotloadpropertiesfrompath:"+location,ex); }finally{ IOUtils.closeQuietly(is); } } returnprops; } }
Global.java用来获取全局的一些常量,可以是从配置文件中读取的常量,也可以是定义成finalstatic的常量,获取配置文件的话是调用上面那个类进行获取的。
packagecom.store.base.secondmodel.base; importjava.io.File; importjava.io.IOException; importjava.util.Map; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.core.io.DefaultResourceLoader; importcom.google.common.collect.Maps; importcom.store.base.secondmodel.base.util.PropertiesLoader; importcom.store.base.secondmodel.base.util.StringUtils; /** *全局配置类 *@authoryiyong_wu * */ publicclassGlobal{ privatestaticfinalLoggerlogger=LoggerFactory.getLogger(Global.class); /** *当前对象实例 */ privatestaticGlobalglobal=newGlobal(); /** *保存全局属性值 */ privatestaticMap<String,String>map=Maps.newHashMap(); /** *属性文件加载对象 */ privatestaticPropertiesLoaderloader=newPropertiesLoader("application.properties"); /** *显示/隐藏 publicstaticfinalStringSHOW="1"; publicstaticfinalStringHIDE="0"; /** *是/否 */ publicstaticfinalStringYES="1"; publicstaticfinalStringNO="0"; /** *状态上/下app专用 */ publicstaticfinalStringUPSHVELF="1"; publicstaticfinalStringDOWNSHVELF="2"; publicstaticfinalStringSEPARATOR="/"; /** *对/错 */ publicstaticfinalStringTRUE="true"; publicstaticfinalStringFALSE="false"; /** *上传文件基础虚拟路径 */ publicstaticfinalStringUSERFILES_BASE_URL="/userfiles/"; /** *针对富文本编辑器,结尾会产生的空div */ publicstaticfinalStringENDS="<p><br></p>"; /** *默认空的私有构造函数 */ publicGlobal(){ //donothinginthismethod,justempty } /** *获取当前对象实例 */ publicstaticGlobalgetInstance(){ returnglobal; } /** *获取配置 */ publicstaticStringgetConfig(Stringkey){ Stringvalue=map.get(key); if(value==null){ value=loader.getProperty(key); map.put(key,value!=null?value:StringUtils.EMPTY); } returnvalue; } /** *获取URL后缀 */ publicstaticStringgetUrlSuffix(){ returngetConfig("urlSuffix"); } /** *页面获取常量 *@see${fns:getConst('YES')} */ publicstaticObjectgetConst(Stringfield){ try{ returnGlobal.class.getField(field).get(null); }catch(Exceptione){ logger.error("获取常量出错",e); } returnnull; } /** *获取工程路径 *@return */ publicstaticStringgetProjectPath(){ //如果配置了工程路径,则直接返回,否则自动获取。 StringprojectPath=Global.getConfig("projectPath"); if(StringUtils.isNotBlank(projectPath)){ returnprojectPath; } try{ Filefile=newDefaultResourceLoader().getResource("").getFile(); if(file!=null){ while(true){ Filef=newFile(file.getPath()+File.separator+"src"+File.separator+"main"); if(f==null||f.exists()){ break; } if(file.getParentFile()!=null){ file=file.getParentFile(); }else{ break; } } projectPath=file.toString(); } }catch(IOExceptione){ logger.error("加载配置文件失败",e); } returnprojectPath; } }
CookieUtil.java从名称就知道是针对获取和存储cookie的一个工具类
packagecom.store.base.secondmodel.base.util; importjava.io.UnsupportedEncodingException; importjava.net.URLDecoder; importjava.net.URLEncoder; importjavax.servlet.http.Cookie; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; /** *Cookie工具类 *@authoryiyong_wu * */ publicclassCookieUtils{ privatestaticfinalLoggerlogger=LoggerFactory.getLogger(CookieUtils.class); /** *私有构造函数 */ privateCookieUtils(){ } /** *设置Cookie(生成时间为1年) *@paramname名称 *@paramvalue值 */ publicstaticvoidsetCookie(HttpServletResponseresponse,Stringname,Stringvalue){ setCookie(response,name,value,60*60*24*365); } /** *设置Cookie *@paramname名称 *@paramvalue值 *@parammaxAge生存时间(单位秒) *@paramuri路径 */ publicstaticvoidsetCookie(HttpServletResponseresponse,Stringname,Stringvalue,Stringpath){ setCookie(response,name,value,path,60*60*24*365); } /** *设置Cookie *@paramname名称 *@paramvalue值 *@parammaxAge生存时间(单位秒) *@paramuri路径 */ publicstaticvoidsetCookie(HttpServletResponseresponse,Stringname,Stringvalue,intmaxAge){ setCookie(response,name,value,"/",maxAge); } /** *设置Cookie *@paramname名称 *@paramvalue值 *@parammaxAge生存时间(单位秒) *@paramuri路径 */ publicstaticvoidsetCookie(HttpServletResponseresponse,Stringname,Stringvalue,Stringpath,intmaxAge){ Cookiecookie=newCookie(name,null); cookie.setPath(path); cookie.setMaxAge(maxAge); try{ cookie.setValue(URLEncoder.encode(value,"utf-8")); }catch(UnsupportedEncodingExceptione){ logger.error("不支持的编码",e); } response.addCookie(cookie); } /** *获得指定Cookie的值 *@paramname名称 *@return值 */ publicstaticStringgetCookie(HttpServletRequestrequest,Stringname){ returngetCookie(request,null,name,false); } /** *获得指定Cookie的值,并删除。 *@paramname名称 *@return值 */ publicstaticStringgetCookie(HttpServletRequestrequest,HttpServletResponseresponse,Stringname){ returngetCookie(request,response,name,true); } /** *获得指定Cookie的值 *@paramrequest请求对象 *@paramresponse响应对象 *@paramname名字 *@paramisRemove是否移除 *@return值 */ publicstaticStringgetCookie(HttpServletRequestrequest,HttpServletResponseresponse,Stringname,booleanisRemove){ Stringvalue=null; Cookie[]cookies=request.getCookies(); if(cookies==null){ returnvalue; } for(Cookiecookie:cookies){ if(cookie.getName().equals(name)){ try{ value=URLDecoder.decode(cookie.getValue(),"utf-8"); }catch(UnsupportedEncodingExceptione){ logger.error("不支持的编码",e); } if(isRemove){ cookie.setMaxAge(0); response.addCookie(cookie); } } } returnvalue; } }
SpringContextHolder.java主要是用来在java代码中获取当前的ApplicationContext,需要在spring配置文件中配置这个bean并且懒加载设置成false;
packagecom.store.base.secondmodel.base.util; importorg.apache.commons.lang3.Validate; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.beans.factory.DisposableBean; importorg.springframework.context.ApplicationContext; importorg.springframework.context.ApplicationContextAware; importorg.springframework.context.annotation.Lazy; importorg.springframework.stereotype.Service; @Service @Lazy(false) publicclassSpringContextHolderimplementsApplicationContextAware, DisposableBean{ privatestaticLoggerlogger=LoggerFactory.getLogger(SpringContextHolder.class); privatestaticApplicationContextapplicationContext=null; /** *取得存储在静态变量中的ApplicationContext. */ publicstaticApplicationContextgetApplicationContext(){ assertContextInjected(); returnapplicationContext; } /** *从静态变量applicationContext中取得Bean,自动转型为所赋值对象的类型. */ @SuppressWarnings("unchecked") publicstatic<T>TgetBean(Stringname){ assertContextInjected(); return(T)applicationContext.getBean(name); } /** *从静态变量applicationContext中取得Bean,自动转型为所赋值对象的类型. */ publicstatic<T>TgetBean(Class<T>requiredType){ assertContextInjected(); returnapplicationContext.getBean(requiredType); } @Override publicvoiddestroy()throwsException{ SpringContextHolder.clearHolder(); } /** *实现ApplicationContextAware接口,注入Context到静态变量中. */ @Override publicvoidsetApplicationContext(ApplicationContextapplicationContext){ logger.debug("注入ApplicationContext到SpringContextHolder:{}",applicationContext); SpringContextHolder.applicationContext=applicationContext; if(SpringContextHolder.applicationContext!=null){ logger.info("SpringContextHolder中的ApplicationContext被覆盖,原有ApplicationContext为:"+SpringContextHolder.applicationContext); } } /** *清除SpringContextHolder中的ApplicationContext为Null. */ publicstaticvoidclearHolder(){ if(logger.isDebugEnabled()){ logger.debug("清除SpringContextHolder中的ApplicationContext:"+applicationContext); } applicationContext=null; } /** *检查ApplicationContext不为空. */ privatestaticvoidassertContextInjected(){ Validate.validState(applicationContext!=null,"applicaitonContext属性未注入,请在applicationContext.xml中定义SpringContextHolder."); } }
StringUtils.java字符串相关的一个工具类
packagecom.store.base.secondmodel.base.util; importjava.io.UnsupportedEncodingException; importjava.util.Locale; importjava.util.regex.Matcher; importjava.util.regex.Pattern; importjavax.servlet.http.HttpServletRequest; importorg.apache.commons.lang3.StringEscapeUtils; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.web.context.request.RequestContextHolder; importorg.springframework.web.context.request.ServletRequestAttributes; importorg.springframework.web.servlet.LocaleResolver; importcom.store.base.util.Encodes; /** *字符串帮助类 *@authoryiyong_wu * */ publicclassStringUtilsextendsorg.apache.commons.lang3.StringUtils{ privatestaticfinalcharSEPARATOR='_'; privatestaticfinalStringCHARSET_NAME="UTF-8"; privatestaticfinalLoggerlogger=LoggerFactory.getLogger(StringUtils.class); /** *转换为字节数组 *@paramstr *@return */ publicstaticbyte[]getBytes(Stringstr){ if(str!=null){ try{ returnstr.getBytes(CHARSET_NAME); }catch(UnsupportedEncodingExceptione){ logger.error("",e); returnnewbyte[0]; } }else{ returnnewbyte[0]; } } /** *转换为字节数组 *@paramstr *@return */ publicstaticStringtoString(byte[]bytes){ try{ returnnewString(bytes,CHARSET_NAME); }catch(UnsupportedEncodingExceptione){ logger.error("",e); returnEMPTY; } } /** *是否包含字符串 *@paramstr验证字符串 *@paramstrs字符串组 *@return包含返回true */ publicstaticbooleaninString(Stringstr,String...strs){ if(str!=null){ for(Strings:strs){ if(str.equals(trim(s))){ returntrue; } } } returnfalse; } /** *替换掉HTML标签方法 */ publicstaticStringreplaceHtml(Stringhtml){ if(isBlank(html)){ return""; } StringregEx="<.+?>"; Patternp=Pattern.compile(regEx); Matcherm=p.matcher(html); returnm.replaceAll(""); } /** *替换为手机识别的HTML,去掉样式及属性,保留回车。 *@paramhtml *@return */ publicstaticStringreplaceMobileHtml(Stringhtml){ if(html==null){ return""; } returnhtml.replaceAll("<([a-z]+?)\\s+?.*?>","<$1>"); } /** *替换为手机识别的HTML,去掉样式及属性,保留回车。 *@paramtxt *@return */ publicstaticStringtoHtml(Stringtxt){ if(txt==null){ return""; } returnreplace(replace(Encodes.escapeHtml(txt),"\n","<br/>"),"\t",""); } /** *缩略字符串(不区分中英文字符) *@paramstr目标字符串 *@paramlength截取长度 *@return */ publicstaticStringabbr(Stringstr,intlength){ if(str==null){ return""; } try{ StringBuildersb=newStringBuilder(); intcurrentLength=0; for(charc:replaceHtml(StringEscapeUtils.unescapeHtml4(str)).toCharArray()){ currentLength+=String.valueOf(c).getBytes("GBK").length; if(currentLength<=length-3){ sb.append(c); }else{ sb.append("..."); break; } } returnsb.toString(); }catch(UnsupportedEncodingExceptione){ logger.error("",e); } return""; } /** *转换为Double类型 */ publicstaticDoubletoDouble(Objectval){ if(val==null){ return0D; } try{ returnDouble.valueOf(trim(val.toString())); }catch(Exceptione){ logger.error("",e); return0D; } } /** *转换为Float类型 */ publicstaticFloattoFloat(Objectval){ returntoDouble(val).floatValue(); } /** *转换为Long类型 */ publicstaticLongtoLong(Objectval){ returntoDouble(val).longValue(); } /** *转换为Integer类型 */ publicstaticIntegertoInteger(Objectval){ returntoLong(val).intValue(); } /** *获得i18n字符串 */ publicstaticStringgetMessage(Stringcode,Object[]args){ LocaleResolverlocalLocaleResolver=SpringContextHolder.getBean(LocaleResolver.class); HttpServletRequestrequest=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); LocalelocalLocale=localLocaleResolver.resolveLocale(request); returnSpringContextHolder.getApplicationContext().getMessage(code,args,localLocale); } /** *获得用户远程地址 */ publicstaticStringgetRemoteAddr(HttpServletRequestrequest){ StringremoteAddr=request.getHeader("X-Real-IP"); if(isNotBlank(remoteAddr)){ remoteAddr=request.getHeader("X-Forwarded-For"); } if(isNotBlank(remoteAddr)){ remoteAddr=request.getHeader("Proxy-Client-IP"); } if(isNotBlank(remoteAddr)){ remoteAddr=request.getHeader("WL-Proxy-Client-IP"); } returnremoteAddr!=null?remoteAddr:request.getRemoteAddr(); } /** *驼峰命名法工具 *@return *toCamelCase("hello_world")=="helloWorl