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