当前位置: 移动技术网 > IT编程>开发语言>Java > Mybatis常用分页插件实现快速分页处理技巧

Mybatis常用分页插件实现快速分页处理技巧

2019年07月22日  | 移动技术网IT编程  | 我要评论

在未分享整个查询分页的执行代码之前,先了解一下执行流程。

1.总体上是利用mybatis的插件拦截器,在sql执行之前拦截,为查询语句加上limit x x

2.用一个page对象,贯穿整个执行流程,这个page对象需要用java编写前端分页组件

3.用一套比较完整的三层entity,dao,service支持这个分页架构

4.这个分页用到的一些辅助类

注:分享的内容较多,这边的话我就不把需要的jar一一列举,大家使用这个分页功能的时候缺少什么就去晚上找什么jar包即可,尽可能用maven包导入因为maven能减少版本冲突等比较好的优势。

我只能说尽可能让大家快速使用这个比较好用的分页功能,如果讲得不明白,欢迎加我qq一起探讨1063150576,。莫喷哈!还有就是文章篇幅可能会比较大,不过花点时间,把它看完并实践一下一定会收获良多。

第一步:既然主题是围绕怎么进行分页的,我们就从mybatis入手,首先,我们把mybatis相关的两个比较重要的配置文件拿出来做简要的理解,一个是mybatis-config.xml,另外一个是实体所对应的mapper配置文件,我会在配置文件上写好注释,大家一看就会明白。

mybatis-config.xml

<!doctype configuration 
public "-//mybatis.org//dtd config 3.0//en" 
"http://mybatis.org/dtd/mybatis-3-config.dtd"> 
<configuration> 
<!-- 全局参数 --> 
<settings> 
<!-- 使全局的映射器启用或禁用缓存。 --> 
<setting name="cacheenabled" value="false"/> 
<!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 --> 
<setting name="lazyloadingenabled" value="true"/> 
<!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 --> 
<setting name="aggressivelazyloading" value="true"/> 
<!-- 是否允许单条sql 返回多个数据集 (取决于驱动的兼容性) default:true --> 
<setting name="multipleresultsetsenabled" value="true"/> 
<!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true --> 
<setting name="usecolumnlabel" value="true"/> 
<!-- 允许jdbc 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。 default:false --> 
<setting name="usegeneratedkeys" value="false"/> 
<!-- 指定 mybatis 如何自动映射 数据基表的列 none:不隐射 partial:部分 full:全部 --> 
<setting name="automappingbehavior" value="partial"/> 
<!-- 这是默认的执行类型 (simple: 简单; reuse: 执行器可能重复使用prepared statements语句;batch: 执行器可以重复执行语句和批量更新) --> 
<setting name="defaultexecutortype" value="simple"/> 
<!-- 使用驼峰命名法转换字段。 --> 
<setting name="mapunderscoretocamelcase" value="true"/> 
<!-- 设置本地缓存范围 session:就会有数据的共享 statement:语句范围 (这样就不会有数据的共享 ) defalut:session --> 
<setting name="localcachescope" value="session"/> 
<!-- 设置但jdbc类型为空时,某些驱动程序 要指定值,default:other,插入空值时不需要指定类型 --> 
<setting name="jdbctypefornull" value="null"/> 
<setting name="logprefix" value="dao."/> 
</settings> 
<!--别名是一个较短的java 类型的名称 --> 
<typealiases> 
<typealias type="com.store.base.model.storeuser" 
alias="user"></typealias> 
<typealias type="com.store.base.secondmodel.pratice.model.product" 
alias="product"></typealias> 
<typealias type="com.store.base.secondmodel.base.page" 
alias="page"></typealias> 
</typealiases> 
<!-- 插件配置,这边为mybatis配置分页拦截器,这个分页拦截器需要我们自己实现 --> 
<plugins> 
<plugin interceptor="com.store.base.secondmodel.base.pageinterceptor.paginationinterceptor" /> 
</plugins> 
</configuration>

一个productmapper.xml作为测试对象,这个mapper文件就简单配置一个需要用到的查询语句

<?xml version="1.0" encoding="utf-8" ?> 
<!doctype mapper public "-//mybatis.org//dtd mapper 3.0//en" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > 
<mapper namespace="com.store.base.secondmodel.pratice.dao.productdao" > 
<sql id="basecolumns" > 
id, product_name as productname, product_no as productno, price as price 
</sql> 
<select id="findlist" resulttype="com.store.base.secondmodel.pratice.model.product"> 
select <include refid="basecolumns"/> from t_store_product 
</select> 
</mapper>

第二步:接下去主要针对这个分页拦截器进行深入分析学习,主要有以下几个类和其对应接口

(1)baseinterceptor 拦截器基础类

(2)paginationinterceptor 我们要使用的分页插件类,继承上面基础类

(3)sqlhelper 主要是用来提前执行count语句,还有就是获取整个完整的分页语句

(4)dialect,mysqldialect,主要用来数据库是否支持limit语句,然后封装完整limit语句

以下是这几个类的分享展示

baseinterceptor.java

package com.store.base.secondmodel.base.pageinterceptor; 
import java.io.serializable; 
import java.util.properties; 
import org.apache.ibatis.logging.log; 
import org.apache.ibatis.logging.logfactory; 
import org.apache.ibatis.plugin.interceptor; 
import com.store.base.secondmodel.base.global; 
import com.store.base.secondmodel.base.page; 
import com.store.base.secondmodel.base.dialect.dialect; 
import com.store.base.secondmodel.base.dialect.mysqldialect; 
import com.store.base.util.reflections; 
/** 
* mybatis分页拦截器基类 
* @author yiyong_wu 
* 
*/ 
public abstract class baseinterceptor implements interceptor, serializable { 
private static final long serialversionuid = 1l; 
protected static final string page = "page"; 
protected static final string delegate = "delegate"; 
protected static final string mapped_statement = "mappedstatement"; 
protected log log = logfactory.getlog(this.getclass()); 
protected dialect dialect; 
/** 
* 对参数进行转换和检查 
* @param parameterobject 参数对象 
* @param page 分页对象 
* @return 分页对象 
* @throws nosuchfieldexception 无法找到参数 
*/ 
@suppresswarnings("unchecked") 
protected static page<object> convertparameter(object parameterobject, page<object> page) { 
try{ 
if (parameterobject instanceof page) { 
return (page<object>) parameterobject; 
} else { 
return (page<object>)reflections.getfieldvalue(parameterobject, page); 
} 
}catch (exception e) { 
return null; 
} 
} 
/** 
* 设置属性,支持自定义方言类和制定数据库的方式 
* <code>dialectclass</code>,自定义方言类。可以不配置这项 
* <ode>dbms</ode> 数据库类型,插件支持的数据库 
* <code>sqlpattern</code> 需要拦截的sql id 
* @param p 属性 
*/ 
protected void initproperties(properties p) { 
dialect dialect = null; 
string dbtype = global.getconfig("jdbc.type"); 
if("mysql".equals(dbtype)){ 
dialect = new mysqldialect(); 
} 
if (dialect == null) { 
throw new runtimeexception("mybatis dialect error."); 
} 
dialect = dialect; 
} 
}

paginationinterceptor.java

package com.store.base.secondmodel.base.pageinterceptor; 
import java.util.properties; 
import org.apache.ibatis.executor.executor; 
import org.apache.ibatis.mapping.boundsql; 
import org.apache.ibatis.mapping.mappedstatement; 
import org.apache.ibatis.mapping.sqlsource; 
import org.apache.ibatis.plugin.intercepts; 
import org.apache.ibatis.plugin.invocation; 
import org.apache.ibatis.plugin.plugin; 
import org.apache.ibatis.plugin.signature; 
import org.apache.ibatis.reflection.metaobject; 
import org.apache.ibatis.session.resulthandler; 
import org.apache.ibatis.session.rowbounds; 
import com.store.base.secondmodel.base.page; 
import com.store.base.secondmodel.base.util.stringutils; 
import com.store.base.util.reflections; 
/** 
* 数据库分页插件,只拦截查询语句. 
* @author yiyong_wu 
* 
*/ 
@intercepts({ @signature(type = executor.class, method = "query", args = { 
mappedstatement.class, object.class, rowbounds.class, 
resulthandler.class }) }) 
public class paginationinterceptor extends baseinterceptor { 
private static final long serialversionuid = 1l; 
@override 
public object intercept(invocation invocation) throws throwable { 
final mappedstatement mappedstatement = (mappedstatement) invocation.getargs()[0]; 
object parameter = invocation.getargs()[1]; 
boundsql boundsql = mappedstatement.getboundsql(parameter); 
object parameterobject = boundsql.getparameterobject(); 
// 获取分页参数对象 
page<object> page = null; 
if (parameterobject != null) { 
page = convertparameter(parameterobject, page); 
} 
// 如果设置了分页对象,则进行分页 
if (page != null && page.getpagesize() != -1) { 
if (stringutils.isblank(boundsql.getsql())) { 
return null; 
} 
string originalsql = boundsql.getsql().trim(); 
// 得到总记录数 
page.setcount(sqlhelper.getcount(originalsql, null,mappedstatement, parameterobject, boundsql, log)); 
// 分页查询 本地化对象 修改数据库注意修改实现 
string pagesql = sqlhelper.generatepagesql(originalsql, page,dialect); 
invocation.getargs()[2] = new rowbounds(rowbounds.no_row_offset,rowbounds.no_row_limit); 
boundsql newboundsql = new boundsql( 
mappedstatement.getconfiguration(), pagesql, 
boundsql.getparametermappings(), 
boundsql.getparameterobject()); 
// 解决mybatis 分页foreach 参数失效 start 
if (reflections.getfieldvalue(boundsql, "metaparameters") != null) { 
metaobject mo = (metaobject) reflections.getfieldvalue( 
boundsql, "metaparameters"); 
reflections.setfieldvalue(newboundsql, "metaparameters", mo); 
} 
// 解决mybatis 分页foreach 参数失效 end 
mappedstatement newms = copyfrommappedstatement(mappedstatement,new boundsqlsqlsource(newboundsql)); 
invocation.getargs()[0] = newms; 
} 
return invocation.proceed(); 
} 
@override 
public object plugin(object target) { 
return plugin.wrap(target, this); 
} 
@override 
public void setproperties(properties properties) { 
super.initproperties(properties); 
} 
private mappedstatement copyfrommappedstatement(mappedstatement ms, 
sqlsource newsqlsource) { 
mappedstatement.builder builder = new mappedstatement.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 (string keyproperty : ms.getkeyproperties()) { 
builder.keyproperty(keyproperty); 
} 
} 
builder.timeout(ms.gettimeout()); 
builder.parametermap(ms.getparametermap()); 
builder.resultmaps(ms.getresultmaps()); 
builder.cache(ms.getcache()); 
return builder.build(); 
} 
public static class boundsqlsqlsource implements sqlsource { 
boundsql boundsql; 
public boundsqlsqlsource(boundsql boundsql) { 
this.boundsql = boundsql; 
} 
@override 
public boundsql getboundsql(object parameterobject) { 
return boundsql; 
} 
} 
}

sqlhelper.java

package com.store.base.secondmodel.base.pageinterceptor; 
import java.sql.connection; 
import java.sql.preparedstatement; 
import java.sql.resultset; 
import java.sql.sqlexception; 
import java.util.list; 
import java.util.regex.matcher; 
import java.util.regex.pattern; 
import org.apache.ibatis.executor.errorcontext; 
import org.apache.ibatis.executor.executorexception; 
import org.apache.ibatis.logging.log; 
import org.apache.ibatis.mapping.boundsql; 
import org.apache.ibatis.mapping.mappedstatement; 
import org.apache.ibatis.mapping.parametermapping; 
import org.apache.ibatis.mapping.parametermode; 
import org.apache.ibatis.reflection.metaobject; 
import org.apache.ibatis.reflection.property.propertytokenizer; 
import org.apache.ibatis.scripting.xmltags.foreachsqlnode; 
import org.apache.ibatis.session.configuration; 
import org.apache.ibatis.type.typehandler; 
import org.apache.ibatis.type.typehandlerregistry; 
import com.store.base.secondmodel.base.global; 
import com.store.base.secondmodel.base.page; 
import com.store.base.secondmodel.base.dialect.dialect; 
import com.store.base.secondmodel.base.util.stringutils; 
import com.store.base.util.reflections; 
/** 
* sql工具类 
* @author yiyong_wu 
* 
*/ 
public class sqlhelper { 
/** 
* 默认私有构造函数 
*/ 
private sqlhelper() { 
} 
/** 
* 对sql参数(?)设值,参考org.apache.ibatis.executor.parameter.defaultparameterhandler 
* 
* @param ps 表示预编译的 sql 语句的对象。 
* @param mappedstatement mappedstatement 
* @param boundsql sql 
* @param parameterobject 参数对象 
* @throws java.sql.sqlexception 数据库异常 
*/ 
@suppresswarnings("unchecked") 
public static void setparameters(preparedstatement ps, mappedstatement mappedstatement, boundsql boundsql, object parameterobject) throws sqlexception { 
errorcontext.instance().activity("setting parameters").object(mappedstatement.getparametermap().getid()); 
list<parametermapping> parametermappings = boundsql.getparametermappings(); 
if (parametermappings != null) { 
configuration configuration = mappedstatement.getconfiguration(); 
typehandlerregistry typehandlerregistry = configuration.gettypehandlerregistry(); 
metaobject metaobject = parameterobject == null ? null : 
configuration.newmetaobject(parameterobject); 
for (int i = 0; i < parametermappings.size(); i++) { 
parametermapping parametermapping = parametermappings.get(i); 
if (parametermapping.getmode() != parametermode.out) { 
object value; 
string propertyname = parametermapping.getproperty(); 
propertytokenizer prop = new propertytokenizer(propertyname); 
if (parameterobject == null) { 
value = null; 
} else if (typehandlerregistry.hastypehandler(parameterobject.getclass())) { 
value = parameterobject; 
} else if (boundsql.hasadditionalparameter(propertyname)) { 
value = boundsql.getadditionalparameter(propertyname); 
} else if (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") 
typehandler typehandler = parametermapping.gettypehandler(); 
if (typehandler == null) { 
throw new executorexception("there was no typehandler found for parameter " + propertyname + " of statement " + mappedstatement.getid()); 
} 
typehandler.setparameter(ps, i + 1, value, parametermapping.getjdbctype()); 
} 
} 
} 
} 
/** 
* 查询总纪录数 
* @param sql sql语句 
* @param connection 数据库连接 
* @param mappedstatement mapped 
* @param parameterobject 参数 
* @param boundsql boundsql 
* @return 总记录数 
* @throws sqlexception sql查询错误 
*/ 
public static int getcount(final string sql, final connection connection, 
final mappedstatement mappedstatement, final object parameterobject, 
final boundsql boundsql, log log) throws sqlexception { 
string dbname = global.getconfig("jdbc.type"); 
final string countsql; 
if("oracle".equals(dbname)){ 
countsql = "select count(1) from (" + sql + ") tmp_count"; 
}else{ 
countsql = "select count(1) from (" + removeorders(sql) + ") tmp_count"; 
} 
connection conn = connection; 
preparedstatement ps = null; 
resultset rs = null; 
try { 
if (log.isdebugenabled()) { 
log.debug("count sql: " + stringutils.replaceeach(countsql, new string[]{"\n","\t"}, new string[]{" "," "})); 
} 
if (conn == null){ 
conn = mappedstatement.getconfiguration().getenvironment().getdatasource().getconnection(); 
} 
ps = conn.preparestatement(countsql); 
boundsql countbs = new boundsql(mappedstatement.getconfiguration(), countsql, 
boundsql.getparametermappings(), parameterobject); 
//解决mybatis 分页foreach 参数失效 start 
if (reflections.getfieldvalue(boundsql, "metaparameters") != null) { 
metaobject mo = (metaobject) reflections.getfieldvalue(boundsql, "metaparameters"); 
reflections.setfieldvalue(countbs, "metaparameters", mo); 
} 
//解决mybatis 分页foreach 参数失效 end 
sqlhelper.setparameters(ps, mappedstatement, countbs, parameterobject); 
rs = ps.executequery(); 
int count = 0; 
if (rs.next()) { 
count = rs.getint(1); 
} 
return count; 
} finally { 
if (rs != null) { 
rs.close(); 
} 
if (ps != null) { 
ps.close(); 
} 
if (conn != null) { 
conn.close(); 
} 
} 
} 
/** 
* 根据数据库方言,生成特定的分页sql 
* @param sql mapper中的sql语句 
* @param page 分页对象 
* @param dialect 方言类型 
* @return 分页sql 
*/ 
public static string generatepagesql(string sql, page<object> page, dialect dialect) { 
if (dialect.supportslimit()) { 
return dialect.getlimitstring(sql, page.getfirstresult(), page.getmaxresults()); 
} else { 
return sql; 
} 
} 
/** 
* 去除qlstring的select子句。 
* @param hql 
* @return 
*/ 
@suppresswarnings("unused") 
private static string removeselect(string qlstring){ 
int beginpos = qlstring.tolowercase().indexof("from"); 
return qlstring.substring(beginpos); 
} 
/** 
* 去除hql的orderby子句。 
* @param hql 
* @return 
*/ 
private static string removeorders(string qlstring) { 
pattern p = pattern.compile("order\\s*by[\\w|\\w|\\s|\\s]*", pattern.case_insensitive); 
matcher m = p.matcher(qlstring); 
stringbuffer sb = new stringbuffer(); 
while (m.find()) { 
m.appendreplacement(sb, ""); 
} 
m.appendtail(sb); 
return sb.tostring(); 
} 
}

dialect.java 接口

package com.store.base.secondmodel.base.dialect; 
/** 
* 类似hibernate的dialect,但只精简出分页部分 
* @author yiyong_wu 
* 
*/ 
public interface dialect { 
/** 
* 数据库本身是否支持分页当前的分页查询方式 
* 如果数据库不支持的话,则不进行数据库分页 
* 
* @return true:支持当前的分页查询方式 
*/ 
public boolean supportslimit(); 
/** 
* 将sql转换为分页sql,分别调用分页sql 
* 
* @param sql sql语句 
* @param offset 开始条数 
* @param limit 每页显示多少纪录条数 
* @return 分页查询的sql 
*/ 
public string getlimitstring(string sql, int offset, int limit); 
}

mysqldialect.java

package com.store.base.secondmodel.base.dialect; 
/** 
* mysql方言的实现 
* @author yiyong_wu 
* 
*/ 
public class mysqldialect implements dialect { 
@override 
public boolean supportslimit() { 
return true; 
} 
@override 
public string getlimitstring(string sql, int offset, int limit) { 
return getlimitstring(sql, offset, integer.tostring(offset),integer.tostring(limit)); 
} 
/** 
* 将sql变成分页sql语句,提供将offset及limit使用占位符号(placeholder)替换. 
* <pre> 
* 如mysql 
* dialect.getlimitstring("select * from user", 12, ":offset",0,":limit") 将返回 
* select * from user limit :offset,:limit 
* </pre> 
* 
* @param sql 实际sql语句 
* @param offset 分页开始纪录条数 
* @param offsetplaceholder 分页开始纪录条数-占位符号 
* @param limitplaceholder 分页纪录条数占位符号 
* @return 包含占位符的分页sql 
*/ 
public string getlimitstring(string sql, int offset, string offsetplaceholder, string limitplaceholder) { 
stringbuilder stringbuilder = new stringbuilder(sql); 
stringbuilder.append(" limit "); 
if (offset > 0) { 
stringbuilder.append(offsetplaceholder).append(",").append(limitplaceholder); 
} else { 
stringbuilder.append(limitplaceholder); 
} 
return stringbuilder.tostring(); 
} 
}

差不多到这边已经把整块分页怎么实现的给分享完了,但是我们还有更重要的任务,想要整个东西跑起来,肯定还要有基础工作要做,接下去我们分析整套page对象以及它所依据的三层架构,还是用product作为实体进行分析。一整套三层架构讲下来,收获肯定又满满的。我们依次从entity->dao->service的顺序讲下来。

首先,针对我们的实体得继承两个抽象实体类baseentity 与 dataentity

baseentity.java 主要放置page成员变量,继承它后就可以每个实体都拥有这个成员变量

package com.store.base.secondmodel.base; 
import java.io.serializable; 
import java.util.map; 
import javax.xml.bind.annotation.xmltransient; 
import org.apache.commons.lang3.stringutils; 
import org.apache.commons.lang3.builder.reflectiontostringbuilder; 
import com.fasterxml.jackson.annotation.jsonignore; 
import com.google.common.collect.maps; 
import com.store.base.model.storeuser; 
/** 
* 最顶层的entity 
* @author yiyong_wu 
* 
* @param <t> 
*/ 
public abstract class baseentity<t> implements serializable { 
private static final long serialversionuid = 1l; 
/** 
* 删除标记(0:正常;1:删除;2:审核;) 
*/ 
public static final string del_flag_normal = "0"; 
public static final string del_flag_delete = "1"; 
public static final string del_flag_audit = "2"; 
/** 
* 实体编号(唯一标识) 
*/ 
protected string id; 
/** 
* 当前用户 
*/ 
protected storeuser currentuser; 
/** 
* 当前实体分页对象 
*/ 
protected page<t> page; 
/** 
* 自定义sql(sql标识,sql内容) 
*/ 
private map<string, string> sqlmap; 
public baseentity() { 
} 
public baseentity(string id) { 
this(); 
this.id = id; 
} 
public string getid() { 
return id; 
} 
public void setid(string id) { 
this.id = id; 
} 
/** 
* 这个主要针对shiro执行插入更新的时候会调用,获取当前的用户 
* @return 
*/ 
@jsonignore 
@xmltransient 
public storeuser getcurrentuser() { 
if(currentuser == null){ 
// currentuser = userutils.getuser(); 
} 
return currentuser; 
} 
public void setcurrentuser(storeuser currentuser) { 
this.currentuser = currentuser; 
} 
@jsonignore 
@xmltransient 
public page<t> getpage() { 
if (page == null){ 
page = new page<>(); 
} 
return page; 
} 
public page<t> setpage(page<t> page) { 
this.page = page; 
return page; 
} 
@jsonignore 
@xmltransient 
public map<string, string> getsqlmap() { 
if (sqlmap == null){ 
sqlmap = maps.newhashmap(); 
} 
return sqlmap; 
} 
public void setsqlmap(map<string, string> sqlmap) { 
this.sqlmap = sqlmap; 
} 
/** 
* 插入之前执行方法,子类实现 
*/ 
public abstract void preinsert(); 
/** 
* 更新之前执行方法,子类实现 
*/ 
public abstract void preupdate(); 
/** 
* 是否是新记录(默认:false),调用setisnewrecord()设置新记录,使用自定义id。 
* 设置为true后强制执行插入语句,id不会自动生成,需从手动传入。 
* @return 
*/ 
public boolean getisnewrecord() { 
return stringutils.isblank(getid()); 
} 
/** 
* 全局变量对象 
*/ 
@jsonignore 
public global getglobal() { 
return global.getinstance(); 
} 
/** 
* 获取数据库名称 
*/ 
@jsonignore 
public string getdbname(){ 
return global.getconfig("jdbc.type"); 
} 
@override 
public string tostring() { 
return reflectiontostringbuilder.tostring(this); 
} 
}

dataentity.java,主要存储更新删除时间,创建用户,更新用户,逻辑删除标志等

package com.store.base.secondmodel.base; 
import java.util.date; 
import org.hibernate.validator.constraints.length; 
import com.fasterxml.jackson.annotation.jsonformat; 
import com.fasterxml.jackson.annotation.jsonignore; 
import com.store.base.model.storeuser; 
/** 
* 数据entity 
* @author yiyong_wu 
* 
* @param <t> 
*/ 
public abstract class dataentity<t> extends baseentity<t> { 
private static final long serialversionuid = 1l; 
protected storeuser createby; // 创建者 
protected date createdate; // 创建日期 
protected storeuser updateby; // 更新者 
protected date updatedate; // 更新日期 
protected string delflag; // 删除标记(0:正常;1:删除;2:审核) 
public dataentity() { 
super(); 
this.delflag = del_flag_normal; 
} 
public dataentity(string id) { 
super(id); 
} 
/** 
* 插入之前执行方法,需要手动调用 
*/ 
@override 
public void preinsert() { 
// 不限制id为uuid,调用setisnewrecord()使用自定义id 
// user user = userutils.getuser(); 
// if (stringutils.isnotblank(user.getid())) { 
// this.updateby = user; 
// this.createby = user; 
// } 
this.updatedate = new date(); 
this.createdate = this.updatedate; 
} 
/** 
* 更新之前执行方法,需要手动调用 
*/ 
@override 
public void preupdate() { 
// user user = userutils.getuser(); 
// if (stringutils.isnotblank(user.getid())) { 
// this.updateby = user; 
// } 
this.updatedate = new date(); 
} 
// @jsonignore 
public storeuser getcreateby() { 
return createby; 
} 
public void setcreateby(storeuser createby) { 
this.createby = createby; 
} 
@jsonformat(pattern = "yyyy-mm-dd hh:mm:ss") 
public date getcreatedate() { 
return createdate; 
} 
public void setcreatedate(date createdate) { 
this.createdate = createdate; 
} 
// @jsonignore 
public storeuser getupdateby() { 
return updateby; 
} 
public void setupdateby(storeuser updateby) { 
this.updateby = updateby; 
} 
@jsonformat(pattern = "yyyy-mm-dd hh:mm:ss") 
public date getupdatedate() { 
return updatedate; 
} 
public void setupdatedate(date updatedate) { 
this.updatedate = updatedate; 
} 
@jsonignore 
@length(min = 1, max = 1) 
public string getdelflag() { 
return delflag; 
} 
public void setdelflag(string delflag) { 
this.delflag = delflag; 
} 
}

product.java 产品类

package com.store.base.secondmodel.pratice.model; 
import com.store.base.secondmodel.base.dataentity; 
/** 
*产品基础类 
*2016年10月11日 
*yiyong_wu 
*/ 
public class product extends dataentity<product>{ 
private static final long serialversionuid = 1l; 
private string productname; 
private float price; 
private string productno; 
public string getproductname() { 
return productname; 
} 
public void setproductname(string productname) { 
this.productname = productname; 
} 
public float getprice() { 
return price; 
} 
public void setprice(float price) { 
this.price = price; 
} 
public string getproductno() { 
return productno; 
} 
public void setproductno(string productno) { 
this.productno = productno; 
} 
}

怎么样,是不是看到很复杂的一个实体继承连关系,不过这有什么,越复杂就会越完整。接下来我就看看dao层,同样是三层,准备好接受洗礼吧

basedao.java 预留接口

package com.store.base.secondmodel.base; 
/** 
* 最顶层的dao接口 
* @author yiyong_wu 
* 
*/ 
public interface basedao { 
} 
cruddao.java 针对增删改查的一个dao接口层
[java] view plain copy print?在code上查看代码片派生到我的代码片
package com.store.base.secondmodel.base; 
import java.util.list; 
/** 
* 定义增删改查的dao接口 
* @author yiyong_wu 
* 
* @param <t> 
*/ 
public interface cruddao<t> extends basedao { 
/** 
* 获取单条数据 
* @param id 
* @return 
*/ 
public t get(string id); 
/** 
* 获取单条数据 
* @param entity 
* @return 
*/ 
public t get(t entity); 
/** 
* 查询数据列表,如果需要分页,请设置分页对象,如:entity.setpage(new page<t>()); 
* @param entity 
* @return 
*/ 
public list<t> findlist(t entity); 
/** 
* 查询所有数据列表 
* @param entity 
* @return 
*/ 
public list<t> findalllist(t entity); 
/** 
* 查询所有数据列表 
* @see public list<t> findalllist(t entity) 
* @return 
public list<t> findalllist(); 
*/ 
/** 
* 插入数据 
* @param entity 
* @return 
*/ 
public int insert(t entity); 
/** 
* 更新数据 
* @param entity 
* @return 
*/ 
public int update(t entity); 
/** 
* 删除数据(一般为逻辑删除,更新del_flag字段为1) 
* @param id 
* @see public int delete(t entity) 
* @return 
*/ 
public int delete(string id); 
/** 
* 删除数据(一般为逻辑删除,更新del_flag字段为1) 
* @param entity 
* @return 
*/ 
public int delete(t entity); 
}

productdao.java mybatis对应的接口mapper,同时也是dao实现,这边需要自定一个注解@mybatisrepository

package com.store.base.secondmodel.pratice.dao; 
import com.store.base.secondmodel.base.cruddao; 
import com.store.base.secondmodel.base.mybatisrepository; 
import com.store.base.secondmodel.pratice.model.product; 
/** 
*todo 
*2016年10月11日 
*yiyong_wu 
*/ 
@mybatisrepository 
public interface productdao extends cruddao<product>{ 
}

自定义注解mybatisrepository.java,跟自定义注解相关,这里就不做过多的解读,网上资料一堆

package com.store.base.secondmodel.base; 
import java.lang.annotation.documented; 
import java.lang.annotation.retention; 
import java.lang.annotation.target; 
import java.lang.annotation.retentionpolicy; 
import java.lang.annotation.elementtype; 
import org.springframework.stereotype.component; 
/** 
* 标识mybatis的dao,方便{@link org.mybatis.spring.mapper.mapperscannerconfigurer}的扫描。 
* 
* 请注意要在spring的配置文件中配置扫描该注解类的配置 
* 
*<bean id="mapperscannerconfigurer" class="org.mybatis.spring.mapper.mapperscannerconfigurer"> 
*<property name="sqlsessionfactorybeanname" value="sqlsessionfactory" /> 
*<property name="basepackage" value="com.store.base.secondmodel" /> 
*<property name="annotationclass" value="com.store.base.secondmodel.base.mybatisrepository" /> 
*</bean> 
* @author yiyong_wu 
* 
*/ 
@retention(retentionpolicy.runtime) 
@target(elementtype.type) 
@documented 
@component 
public @interface mybatisrepository { 
string value() default ""; 
}

注意:跟productdao.java联系比较大的是productmapper.xml文件,大家可以看到上面那个配置文件的namespace是指向这个dao的路径的。

接下来我们就进入最后的service分析了,一样还是三层继承

baseservice.java

package com.store.base.secondmodel.base; 
import org.slf4j.logger; 
import org.slf4j.loggerfactory; 
import org.springframework.transaction.annotation.transactional; 
/** 
* service的最顶层父类 
* @author yiyong_wu 
* 
*/ 
@transactional(readonly = true) 
public abstract class baseservice { 
//日志记录用的 
protected logger logger = loggerfactory.getlogger(getclass()); 
}

crudservice.java 增删改查相关的业务接口实现

package com.store.base.secondmodel.base; 
import java.util.list; 
import org.springframework.beans.factory.annotation.autowired; 
import org.springframework.transaction.annotation.transactional; 
/** 
* 增删改查service基类 
* @author yiyong_wu 
* 
* @param <d> 
* @param <t> 
*/ 
public abstract class crudservice<d extends cruddao<t>, t extends dataentity<t>> 
extends baseservice { 
/** 
* 持久层对象 
*/ 
@autowired 
protected d dao; 
/** 
* 获取单条数据 
* @param id 
* @return 
*/ 
public t get(string id) { 
return dao.get(id); 
} 
/** 
* 获取单条数据 
* @param entity 
* @return 
*/ 
public t get(t entity) { 
return dao.get(entity); 
} 
/** 
* 查询列表数据 
* @param entity 
* @return 
*/ 
public list<t> findlist(t entity) { 
return dao.findlist(entity); 
} 
/** 
* 查询分页数据 
* @param page 分页对象 
* @param entity 
* @return 
*/ 
public page<t> findpage(page<t> page, t entity) { 
entity.setpage(page); 
page.setlist(dao.findlist(entity)); 
return page; 
} 
/** 
* 保存数据(插入或更新) 
* @param entity 
*/ 
@transactional(readonly = false) 
public void save(t entity) { 
if (entity.getisnewrecord()){ 
entity.preinsert(); 
dao.insert(entity); 
}else{ 
entity.preupdate(); 
dao.update(entity); 
} 
} 
/** 
* 删除数据 
* @param entity 
*/ 
@transactional(readonly = false) 
public void delete(t entity) { 
dao.delete(entity); 
} 
}

productservice.java,去继承crudservice接口,注意起注入dao和实体类型的一种模式

package com.store.base.secondmodel.pratice.service; 
import org.springframework.stereotype.service; 
import org.springframework.transaction.annotation.transactional; 
import com.store.base.secondmodel.base.crudservice; 
import com.store.base.secondmodel.pratice.dao.productdao; 
import com.store.base.secondmodel.pratice.model.product; 
/** 
*todo 
*2016年10月11日 
*yiyong_wu 
*/ 
@service 
@transactional(readonly = true) 
public class productservice extends crudservice<productdao,product>{ 
}

我想看到这里的同志已经很不耐烦了。但是如果你错过接下去的一段,基本上刚才看的就快等于白看了,革命的胜利就在后半段,因为整个分页功能围绕的就是一个page对象,重磅内容终于要出来了,当你把page对象填充到刚才那个baseentity上的时候,你会发现一切就完整起来了,废话不多说,page对象如下

package com.store.base.secondmodel.base; 
import java.io.serializable; 
import java.util.arraylist; 
import java.util.list; 
import java.util.regex.pattern; 
import javax.servlet.http.httpservletrequest; 
import javax.servlet.http.httpservletresponse; 
import com.fasterxml.jackson.annotation.jsonignore; 
import com.store.base.secondmodel.base.util.cookieutils; 
import com.store.base.secondmodel.base.util.stringutils; 
/** 
* 分页类 
* @author yiyong_wu 
* 
* @param <t> 
*/ 
public class page<t> implements serializable{ 
private static final long serialversionuid = 1l; 
private int pageno = 1; // 当前页码 
private int pagesize = integer.parseint(global.getconfig("page.pagesize")); // 页面大小,设置为“-1”表示不进行分页(分页无效) 
private long count;// 总记录数,设置为“-1”表示不查询总数 
private int first;// 首页索引 
private int last;// 尾页索引 
private int prev;// 上一页索引 
private int next;// 下一页索引 
private boolean firstpage;//是否是第一页 
private boolean lastpage;//是否是最后一页 
private int length = 6;// 显示页面长度 
private int slider = 1;// 前后显示页面长度 
private list<t> list = new arraylist<>(); 
private string orderby = ""; // 标准查询有效, 实例: updatedate desc, name asc 
private string funcname = "page"; // 设置点击页码调用的js函数名称,默认为page,在一页有多个分页对象时使用。 
private string funcparam = ""; // 函数的附加参数,第三个参数值。 
private string message = ""; // 设置提示消息,显示在“共n条”之后 
public page() { 
this.pagesize = -1; 
} 
/** 
* 构造方法 
* @param request 传递 repage 参数,来记住页码 
* @param response 用于设置 cookie,记住页码 
*/ 
public page(httpservletrequest request, httpservletresponse response){ 
this(request, response, -2); 
} 
/** 
* 构造方法 
* @param request 传递 repage 参数,来记住页码 
* @param response 用于设置 cookie,记住页码 
* @param defaultpagesize 默认分页大小,如果传递 -1 则为不分页,返回所有数据 
*/ 
public page(httpservletrequest request, httpservletresponse response, int defaultpagesize){ 
// 设置页码参数(传递repage参数,来记住页码) 
string no = request.getparameter("pageno"); 
if (stringutils.isnumeric(no)){ 
cookieutils.setcookie(response, "pageno", no); 
this.setpageno(integer.parseint(no)); 
}else if (request.getparameter("repage")!=null){ 
no = cookieutils.getcookie(request, "pageno"); 
if (stringutils.isnumeric(no)){ 
this.setpageno(integer.parseint(no)); 
} 
} 
// 设置页面大小参数(传递repage参数,来记住页码大小) 
string size = request.getparameter("pagesize"); 
if (stringutils.isnumeric(size)){ 
cookieutils.setcookie(response, "pagesize", size); 
this.setpagesize(integer.parseint(size)); 
}else if (request.getparameter("repage")!=null){ 
no = cookieutils.getcookie(request, "pagesize"); 
if (stringutils.isnumeric(size)){ 
this.setpagesize(integer.parseint(size)); 
} 
}else if (defaultpagesize != -2){ 
this.pagesize = defaultpagesize; 
} 
// 设置排序参数 
string orderby = request.getparameter("orderby"); 
if (stringutils.isnotblank(orderby)){ 
this.setorderby(orderby); 
} 
} 
/** 
* 构造方法 
* @param pageno 当前页码 
* @param pagesize 分页大小 
*/ 
public page(int pageno, int pagesize) { 
this(pageno, pagesize, 0); 
} 
/** 
* 构造方法 
* @param pageno 当前页码 
* @param pagesize 分页大小 
* @param count 数据条数 
*/ 
public page(int pageno, int pagesize, long count) { 
this(pageno, pagesize, count, new arraylist<t>()); 
} 
/** 
* 构造方法 
* @param pageno 当前页码 
* @param pagesize 分页大小 
* @param count 数据条数 
* @param list 本页数据对象列表 
*/ 
public page(int pageno, int pagesize, long count, list<t> list) { 
this.setcount(count); 
this.setpageno(pageno); 
this.pagesize = pagesize; 
this.list = list; 
} 
/** 
* 初始化参数 
*/ 
public void initialize(){ 
//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; 
} 
} 
/** 
* 默认输出当前分页标签 
* <div class="page">${page}</div> 
*/ 
@override 
public string tostring() { 
stringbuilder sb = new stringbuilder(); 
if (pageno == first) {// 如果是首页 
sb.append("<li class=\"disabled\"><a href=\"javascript:\">« 上一页</a></li>\n"); 
} else { 
sb.append("<li><a href=\"javascript:\" onclick=\""+funcname+"("+prev+","+pagesize+",'"+funcparam+"');\">« 上一页</a></li>\n"); 
} 
int begin = pageno - (length / 2); 
if (begin < first) { 
begin = first; 
} 
int end = begin + length - 1; 
if (end >= last) { 
end = last; 
begin = end - length + 1; 
if (begin < first) { 
begin = first; 
} 
} 
if (begin > first) { 
int i = 0; 
for (i = first; i < first + slider && i < begin; i++) { 
sb.append("<li><a href=\"javascript:\" onclick=\""+funcname+"("+i+","+pagesize+",'"+funcparam+"');\">" 
+ (i + 1 - first) + "</a></li>\n"); 
} 
if (i < begin) { 
sb.append("<li class=\"disabled\"><a href=\"javascript:\">...</a></li>\n"); 
} 
} 
for (int i = begin; i <= end; i++) { 
if (i == pageno) { 
sb.append("<li class=\"active\"><a href=\"javascript:\">" + (i + 1 - first) 
+ "</a></li>\n"); 
} else { 
sb.append("<li><a href=\"javascript:\" onclick=\""+funcname+"("+i+","+pagesize+",'"+funcparam+"');\">" 
+ (i + 1 - first) + "</a></li>\n"); 
} 
} 
if (last - end > slider) { 
sb.append("<li class=\"disabled\"><a href=\"javascript:\">...</a></li>\n"); 
end = last - slider; 
} 
for (int i = end + 1; i <= last; i++) { 
sb.append("<li><a href=\"javascript:\" onclick=\""+funcname+"("+i+","+pagesize+",'"+funcparam+"');\">" 
+ (i + 1 - first) + "</a></li>\n"); 
} 
if (pageno == last) { 
sb.append("<li class=\"disabled\"><a href=\"javascript:\">下一页 »</a></li>\n"); 
} else { 
sb.append("<li><a href=\"javascript:\" onclick=\""+funcname+"("+next+","+pagesize+",'"+funcparam+"');\">" 
+ "下一页 »</a></li>\n"); 
} 
return sb.tostring(); 
} 
/** 
* 获取分页html代码 
* @return 
*/ 
public string gethtml(){ 
return tostring(); 
} 
/** 
* 获取设置总数 
* @return 
*/ 
public long getcount() { 
return count; 
} 
/** 
* 设置数据总数 
* @param count 
*/ 
public void setcount(long count) { 
this.count = count; 
if (pagesize >= count){ 
pageno = 1; 
} 
} 
/** 
* 获取当前页码 
* @return 
*/ 
public int getpageno() { 
return pageno; 
} 
/** 
* 设置当前页码 
* @param pageno 
*/ 
public void setpageno(int pageno) { 
this.pageno = pageno; 
} 
/** 
* 获取页面大小 
* @return 
*/ 
public int getpagesize() { 
return pagesize; 
} 
/** 
* 设置页面大小(最大500)// > 500 ? 500 : pagesize; 
* @param pagesize 
*/ 
public void setpagesize(int pagesize) { 
this.pagesize = pagesize <= 0 ? 10 : pagesize; 
} 
/** 
* 首页索引 
* @return 
*/ 
@jsonignore 
public int getfirst() { 
return first; 
} 
/** 
* 尾页索引 
* @return 
*/ 
@jsonignore 
public int getlast() { 
return last; 
} 
/** 
* 获取页面总数 
* @return getlast(); 
*/ 
@jsonignore 
public int gettotalpage() { 
return getlast(); 
} 
/** 
* 是否为第一页 
* @return 
*/ 
@jsonignore 
public boolean isfirstpage() { 
return firstpage; 
} 
/** 
* 是否为最后一页 
* @return 
*/ 
@jsonignore 
public boolean islastpage() { 
return lastpage; 
} 
/** 
* 上一页索引值 
* @return 
*/ 
@jsonignore 
public int getprev() { 
if (isfirstpage()) { 
return pageno; 
} else { 
return pageno - 1; 
} 
} 
/** 
* 下一页索引值 
* @return 
*/ 
@jsonignore 
public int getnext() { 
if (islastpage()) { 
return pageno; 
} else { 
return pageno + 1; 
} 
} 
/** 
* 获取本页数据对象列表 
* @return list<t> 
*/ 
public list<t> getlist() { 
return list; 
} 
/** 
* 设置本页数据对象列表 
* @param list 
*/ 
public page<t> setlist(list<t> list) { 
this.list = list; 
initialize(); 
return this; 
} 
/** 
* 获取查询排序字符串 
* @return 
*/ 
@jsonignore 
public string getorderby() { 
// sql过滤,防止注入 
string reg = "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|" 
+ "(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)"; 
pattern sqlpattern = pattern.compile(reg, pattern.case_insensitive); 
if (sqlpattern.matcher(orderby).find()) { 
return ""; 
} 
return orderby; 
} 
/** 
* 设置查询排序,标准查询有效, 实例: updatedate desc, name asc 
*/ 
public void setorderby(string orderby) { 
this.orderby = orderby; 
} 
/** 
* 获取点击页码调用的js函数名称 
* function ${page.funcname}(pageno){location="${ctx}/list-${category.id}${urlsuffix}?pageno="+i;} 
* @return 
*/ 
@jsonignore 
public string getfuncname() { 
return funcname; 
} 
/** 
* 设置点击页码调用的js函数名称,默认为page,在一页有多个分页对象时使用。 
* @param funcname 默认为page 
*/ 
public void setfuncname(string funcname) { 
this.funcname = funcname; 
} 
/** 
* 获取分页函数的附加参数 
* @return 
*/ 
@jsonignore 
public string getfuncparam() { 
return funcparam; 
} 
/** 
* 设置分页函数的附加参数 
* @return 
*/ 
public void setfuncparam(string funcparam) { 
this.funcparam = funcparam; 
} 
/** 
* 设置提示消息,显示在“共n条”之后 
* @param message 
*/ 
public void setmessage(string message) { 
this.message = message; 
} 
/** 
* 分页是否有效 
* @return this.pagesize==-1 
*/ 
@jsonignore 
public boolean isdisabled() { 
return this.pagesize==-1; 
} 
/** 
* 是否进行总数统计 
* @return this.count==-1 
*/ 
@jsonignore 
public boolean isnotcount() { 
return this.count==-1; 
} 
/** 
* 获取 hibernate firstresult 
*/ 
public int getfirstresult(){ 
int firstresult = (getpageno() - 1) * getpagesize(); 
if (firstresult >= getcount()) { 
firstresult = 0; 
} 
return firstresult; 
} 
/** 
* 获取 hibernate maxresults 
*/ 
public int getmaxresults(){ 
return getpagesize(); 
} 
}

看完这个page对象应该稍微有点感觉了吧,然后我在胡乱贴一些相关用到的工具类吧,工具类的话我只稍微提一下,具体大家可以弄到自己的代码上好好解读。

propertiesloader.java 用来获取resource文件夹下的常量配置文件

package com.store.base.secondmodel.base.util; 
import java.io.ioexception; 
import java.io.inputstream; 
import java.util.nosuchelementexception; 
import java.util.properties; 
import org.apache.commons.io.ioutils; 
import org.slf4j.logger; 
import org.slf4j.loggerfactory; 
import org.springframework.core.io.defaultresourceloader; 
import org.springframework.core.io.resource; 
import org.springframework.core.io.resourceloader; 
/** 
* properties文件载入工具类. 可载入多个properties文件, 
* 相同的属性在最后载入的文件中的值将会覆盖之前的值,但以system的property优先. 
* @author yiyong_wu 
* 
*/ 
public class propertiesloader { 
private static logger logger = loggerfactory.getlogger(propertiesloader.class); 
private static resourceloader resourceloader = new defaultresourceloader(); 
private final properties properties; 
public propertiesloader(string... resourcespaths) { 
properties = loadproperties(resourcespaths); 
} 
public properties getproperties() { 
return properties; 
} 
/** 
* 取出property,但以system的property优先,取不到返回空字符串. 
*/ 
private string getvalue(string key) { 
string systemproperty = system.getproperty(key); 
if (systemproperty != null) { 
return systemproperty; 
} 
if (properties.containskey(key)) { 
return properties.getproperty(key); 
} 
return ""; 
} 
/** 
* 取出string类型的property,但以system的property优先,如果都为null则抛出异常. 
*/ 
public string getproperty(string key) { 
string value = getvalue(key); 
if (value == null) { 
throw new nosuchelementexception(); 
} 
return value; 
} 
/** 
* 取出string类型的property,但以system的property优先.如果都为null则返回default值. 
*/ 
public string getproperty(string key, string defaultvalue) { 
string value = getvalue(key); 
return value != null ? value : defaultvalue; 
} 
/** 
* 取出integer类型的property,但以system的property优先.如果都为null或内容错误则抛出异常. 
*/ 
public integer getinteger(string key) { 
string value = getvalue(key); 
if (value == null) { 
throw new nosuchelementexception(); 
} 
return integer.valueof(value); 
} 
/** 
* 取出integer类型的property,但以system的property优先.如果都为null则返回default值,如果内容错误则抛出异常 
*/ 
public integer getinteger(string key, integer defaultvalue) { 
string value = getvalue(key); 
return value != null ? integer.valueof(value) : defaultvalue; 
} 
/** 
* 取出double类型的property,但以system的property优先.如果都为null或内容错误则抛出异常. 
*/ 
public double getdouble(string key) { 
string value = getvalue(key); 
if (value == null) { 
throw new nosuchelementexception(); 
} 
return double.valueof(value); 
} 
/** 
* 取出double类型的property,但以system的property优先.如果都为null则返回default值,如果内容错误则抛出异常 
*/ 
public double getdouble(string key, integer defaultvalue) { 
string value = getvalue(key); 
return value != null ? double.valueof(value) : defaultvalue.doublevalue(); 
} 
/** 
* 取出boolean类型的property,但以system的property优先.如果都为null抛出异常,如果内容不是true/false则返回false. 
*/ 
public boolean getboolean(string key) { 
string value = getvalue(key); 
if (value == null) { 
throw new nosuchelementexception(); 
} 
return boolean.valueof(value); 
} 
/** 
* 取出boolean类型的property,但以system的property优先.如果都为null则返回default值,如果内容不为true/false则返回false. 
*/ 
public boolean getboolean(string key, boolean defaultvalue) { 
string value = getvalue(key); 
return value != null ? boolean.valueof(value) : defaultvalue; 
} 
/** 
* 载入多个文件, 文件路径使用spring resource格式. 
*/ 
private properties loadproperties(string... resourcespaths) { 
properties props = new properties(); 
for (string location : resourcespaths) { 
inputstream is = null; 
try { 
resource resource = resourceloader.getresource(location); 
is = resource.getinputstream(); 
props.load(is); 
} catch (ioexception ex) { 
logger.error("could not load properties from path:" + location , ex); 
} finally { 
ioutils.closequietly(is); 
} 
} 
return props; 
} 
}

global.java 用来获取全局的一些常量,可以是从配置文件中读取的常量,也可以是定义成final static的常量,获取配置文件的话是调用上面那个类进行获取的。

package com.store.base.secondmodel.base; 
import java.io.file; 
import java.io.ioexception; 
import java.util.map; 
import org.slf4j.logger; 
import org.slf4j.loggerfactory; 
import org.springframework.core.io.defaultresourceloader; 
import com.google.common.collect.maps; 
import com.store.base.secondmodel.base.util.propertiesloader; 
import com.store.base.secondmodel.base.util.stringutils; 
/** 
* 全局配置类 
* @author yiyong_wu 
* 
*/ 
public class global { 
private static final logger logger = loggerfactory.getlogger(global.class); 
/** 
* 当前对象实例 
*/ 
private static global global = new global(); 
/** 
* 保存全局属性值 
*/ 
private static map<string, string> map = maps.newhashmap(); 
/** 
* 属性文件加载对象 
*/ 
private static propertiesloader loader = new propertiesloader("application.properties"); 
/** 
* 显示/隐藏 
public static final string show = "1"; 
public static final string hide = "0"; 
/** 
* 是/否 
*/ 
public static final string yes = "1"; 
public static final string no = "0"; 
/** 
* 状态 上/下 app专用 
*/ 
public static final string upshvelf = "1"; 
public static final string downshvelf = "2"; 
public static final string separator = "/"; 
/** 
* 对/错 
*/ 
public static final string true = "true"; 
public static final string false = "false"; 
/** 
* 上传文件基础虚拟路径 
*/ 
public static final string userfiles_base_url = "/userfiles/"; 
/** 
* 针对富文本编辑器,结尾会产生的空div 
*/ 
public static final string ends = "<p><br></p>"; 
/** 
* 默认空的私有构造函数 
*/ 
public global() { 
//do nothing in this method,just empty 
} 
/** 
* 获取当前对象实例 
*/ 
public static global getinstance() { 
return global; 
} 
/** 
* 获取配置 
*/ 
public static string getconfig(string key) { 
string value = map.get(key); 
if (value == null){ 
value = loader.getproperty(key); 
map.put(key, value != null ? value : stringutils.empty); 
} 
return value; 
} 
/** 
* 获取url后缀 
*/ 
public static string geturlsuffix() { 
return getconfig("urlsuffix"); 
} 
/** 
* 页面获取常量 
* @see ${fns:getconst('yes')} 
*/ 
public static object getconst(string field) { 
try { 
return global.class.getfield(field).get(null); 
} catch (exception e) { 
logger.error("获取常量出错", e); 
} 
return null; 
} 
/** 
* 获取工程路径 
* @return 
*/ 
public static string getprojectpath(){ 
// 如果配置了工程路径,则直接返回,否则自动获取。 
string projectpath = global.getconfig("projectpath"); 
if (stringutils.isnotblank(projectpath)){ 
return projectpath; 
} 
try { 
file file = new defaultresourceloader().getresource("").getfile(); 
if (file != null){ 
while(true){ 
file f = new file(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 (ioexception e) { 
logger.error("加载配置文件失败", e); 
} 
return projectpath; 
} 
}

cookieutil.java 从名称就知道是针对获取和存储cookie的一个工具类

package com.store.base.secondmodel.base.util; 
import java.io.unsupportedencodingexception; 
import java.net.urldecoder; 
import java.net.urlencoder; 
import javax.servlet.http.cookie; 
import javax.servlet.http.httpservletrequest; 
import javax.servlet.http.httpservletresponse; 
import org.slf4j.logger; 
import org.slf4j.loggerfactory; 
/** 
* cookie工具类 
* @author yiyong_wu 
* 
*/ 
public class cookieutils { 
private static final logger logger = loggerfactory.getlogger(cookieutils.class); 
/** 
* 私有构造函数 
*/ 
private cookieutils() { 
} 
/** 
* 设置 cookie(生成时间为1年) 
* @param name 名称 
* @param value 值 
*/ 
public static void setcookie(httpservletresponse response, string name, string value) { 
setcookie(response, name, value, 60*60*24*365); 
} 
/** 
* 设置 cookie 
* @param name 名称 
* @param value 值 
* @param maxage 生存时间(单位秒) 
* @param uri 路径 
*/ 
public static void setcookie(httpservletresponse response, string name, string value, string path) { 
setcookie(response, name, value, path, 60*60*24*365); 
} 
/** 
* 设置 cookie 
* @param name 名称 
* @param value 值 
* @param maxage 生存时间(单位秒) 
* @param uri 路径 
*/ 
public static void setcookie(httpservletresponse response, string name, string value, int maxage) { 
setcookie(response, name, value, "/", maxage); 
} 
/** 
* 设置 cookie 
* @param name 名称 
* @param value 值 
* @param maxage 生存时间(单位秒) 
* @param uri 路径 
*/ 
public static void setcookie(httpservletresponse response, string name, string value, string path, int maxage) { 
cookie cookie = new cookie(name, null); 
cookie.setpath(path); 
cookie.setmaxage(maxage); 
try { 
cookie.setvalue(urlencoder.encode(value, "utf-8")); 
} catch (unsupportedencodingexception e) { 
logger.error("不支持的编码", e); 
} 
response.addcookie(cookie); 
} 
/** 
* 获得指定cookie的值 
* @param name 名称 
* @return 值 
*/ 
public static string getcookie(httpservletrequest request, string name) { 
return getcookie(request, null, name, false); 
} 
/** 
* 获得指定cookie的值,并删除。 
* @param name 名称 
* @return 值 
*/ 
public static string getcookie(httpservletrequest request, httpservletresponse response, string name) { 
return getcookie(request, response, name, true); 
} 
/** 
* 获得指定cookie的值 
* @param request 请求对象 
* @param response 响应对象 
* @param name 名字 
* @param isremove 是否移除 
* @return 值 
*/ 
public static string getcookie(httpservletrequest request, httpservletresponse response, string name, boolean isremove) { 
string value = null; 
cookie[] cookies = request.getcookies(); 
if(cookies == null) { 
return value; 
} 
for (cookie cookie : cookies) { 
if (cookie.getname().equals(name)) { 
try { 
value = urldecoder.decode(cookie.getvalue(), "utf-8"); 
} catch (unsupportedencodingexception e) { 
logger.error("不支持的编码", e); 
} 
if (isremove) { 
cookie.setmaxage(0); 
response.addcookie(cookie); 
} 
} 
} 
return value; 
} 
}

springcontextholder.java 主要是用来在java代码中获取当前的applicationcontext,需要在spring配置文件中配置这个bean并且懒加载设置成false;

package com.store.base.secondmodel.base.util; 
import org.apache.commons.lang3.validate; 
import org.slf4j.logger; 
import org.slf4j.loggerfactory; 
import org.springframework.beans.factory.disposablebean; 
import org.springframework.context.applicationcontext; 
import org.springframework.context.applicationcontextaware; 
import org.springframework.context.annotation.lazy; 
import org.springframework.stereotype.service; 
@service 
@lazy(false) 
public class springcontextholder implements applicationcontextaware, 
disposablebean { 
private static logger logger = loggerfactory.getlogger(springcontextholder.class); 
private static applicationcontext applicationcontext = null; 
/** 
* 取得存储在静态变量中的applicationcontext. 
*/ 
public static applicationcontext getapplicationcontext() { 
assertcontextinjected(); 
return applicationcontext; 
} 
/** 
* 从静态变量applicationcontext中取得bean, 自动转型为所赋值对象的类型. 
*/ 
@suppresswarnings("unchecked") 
public static <t> t getbean(string name) { 
assertcontextinjected(); 
return (t) applicationcontext.getbean(name); 
} 
/** 
* 从静态变量applicationcontext中取得bean, 自动转型为所赋值对象的类型. 
*/ 
public static <t> t getbean(class<t> requiredtype) { 
assertcontextinjected(); 
return applicationcontext.getbean(requiredtype); 
} 
@override 
public void destroy() throws exception { 
springcontextholder.clearholder(); 
} 
/** 
* 实现applicationcontextaware接口, 注入context到静态变量中. 
*/ 
@override 
public void setapplicationcontext(applicationcontext applicationcontext) { 
logger.debug("注入applicationcontext到springcontextholder:{}", applicationcontext); 
springcontextholder.applicationcontext = applicationcontext; 
if (springcontextholder.applicationcontext != null) { 
logger.info("springcontextholder中的applicationcontext被覆盖, 原有applicationcontext为:" + springcontextholder.applicationcontext); 
} 
} 
/** 
* 清除springcontextholder中的applicationcontext为null. 
*/ 
public static void clearholder() { 
if (logger.isdebugenabled()){ 
logger.debug("清除springcontextholder中的applicationcontext:" + applicationcontext); 
} 
applicationcontext = null; 
} 
/** 
* 检查applicationcontext不为空. 
*/ 
private static void assertcontextinjected() { 
validate.validstate(applicationcontext != null, "applicaitoncontext属性未注入, 请在applicationcontext.xml中定义springcontextholder."); 
} 
}

stringutils.java字符串相关的一个工具类

package com.store.base.secondmodel.base.util; 
import java.io.unsupportedencodingexception; 
import java.util.locale; 
import java.util.regex.matcher; 
import java.util.regex.pattern; 
import javax.servlet.http.httpservletrequest; 
import org.apache.commons.lang3.stringescapeutils; 
import org.slf4j.logger; 
import org.slf4j.loggerfactory; 
import org.springframework.web.context.request.requestcontextholder; 
import org.springframework.web.context.request.servletrequestattributes; 
import org.springframework.web.servlet.localeresolver; 
import com.store.base.util.encodes; 
/** 
* 字符串帮助类 
* @author yiyong_wu 
* 
*/ 
public class stringutils extends org.apache.commons.lang3.stringutils { 
private static final char separator = '_'; 
private static final string charset_name = "utf-8"; 
private static final logger logger = loggerfactory.getlogger(stringutils.class); 
/** 
* 转换为字节数组 
* @param str 
* @return 
*/ 
public static byte[] getbytes(string str){ 
if (str != null){ 
try { 
return str.getbytes(charset_name); 
} catch (unsupportedencodingexception e) { 
logger.error("", e); 
return new byte[0]; 
} 
}else{ 
return new byte[0]; 
} 
} 
/** 
* 转换为字节数组 
* @param str 
* @return 
*/ 
public static string tostring(byte[] bytes){ 
try { 
return new string(bytes, charset_name); 
} catch (unsupportedencodingexception e) { 
logger.error("", e); 
return empty; 
} 
} 
/** 
* 是否包含字符串 
* @param str 验证字符串 
* @param strs 字符串组 
* @return 包含返回true 
*/ 
public static boolean instring(string str, string... strs){ 
if (str != null){ 
for (string s : strs){ 
if (str.equals(trim(s))){ 
return true; 
} 
} 
} 
return false; 
} 
/** 
* 替换掉html标签方法 
*/ 
public static string replacehtml(string html) { 
if (isblank(html)){ 
return ""; 
} 
string regex = "<.+?>"; 
pattern p = pattern.compile(regex); 
matcher m = p.matcher(html); 
return m.replaceall(""); 
} 
/** 
* 替换为手机识别的html,去掉样式及属性,保留回车。 
* @param html 
* @return 
*/ 
public static string replacemobilehtml(string html){ 
if (html == null){ 
return ""; 
} 
return html.replaceall("<([a-z]+?)\\s+?.*?>", "<$1>"); 
} 
/** 
* 替换为手机识别的html,去掉样式及属性,保留回车。 
* @param txt 
* @return 
*/ 
public static string tohtml(string txt){ 
if (txt == null){ 
return ""; 
} 
return replace(replace(encodes.escapehtml(txt), "\n", "<br/>"), "\t", " "); 
} 
/** 
* 缩略字符串(不区分中英文字符) 
* @param str 目标字符串 
* @param length 截取长度 
* @return 
*/ 
public static string abbr(string str, int length) { 
if (str == null) { 
return ""; 
} 
try { 
stringbuilder sb = new stringbuilder(); 
int currentlength = 0; 
for (char c : replacehtml(stringescapeutils.unescapehtml4(str)).tochararray()) { 
currentlength += string.valueof(c).getbytes("gbk").length; 
if (currentlength <= length - 3) { 
sb.append(c); 
} else { 
sb.append("..."); 
break; 
} 
} 
return sb.tostring(); 
} catch (unsupportedencodingexception e) { 
logger.error("", e); 
} 
return ""; 
} 
/** 
* 转换为double类型 
*/ 
public static double todouble(object val){ 
if (val == null){ 
return 0d; 
} 
try { 
return double.valueof(trim(val.tostring())); 
} catch (exception e) { 
logger.error("", e); 
return 0d; 
} 
} 
/** 
* 转换为float类型 
*/ 
public static float tofloat(object val){ 
return todouble(val).floatvalue(); 
} 
/** 
* 转换为long类型 
*/ 
public static long tolong(object val){ 
return todouble(val).longvalue(); 
} 
/** 
* 转换为integer类型 
*/ 
public static integer tointeger(object val){ 
return tolong(val).intvalue(); 
} 
/** 
* 获得i18n字符串 
*/ 
public static string getmessage(string code, object[] args) { 
localeresolver locallocaleresolver = springcontextholder.getbean(localeresolver.class); 
httpservletrequest request = ((servletrequestattributes)requestcontextholder.getrequestattributes()).getrequest(); 
locale locallocale = locallocaleresolver.resolvelocale(request); 
return springcontextholder.getapplicationcontext().getmessage(code, args, locallocale); 
} 
/** 
* 获得用户远程地址 
*/ 
public static string getremoteaddr(httpservletrequest request){ 
string remoteaddr = 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"); 
} 
return remoteaddr != null ? remoteaddr : request.getremoteaddr(); 
} 
/** 
* 驼峰命名法工具 
* @return 
* tocamelcase("hello_world") == "helloworld" 
* tocapitalizecamelcase("hello_world") == "helloworld" 
* tounderscorecase("helloworld") = "hello_world" 
*/ 
public static string tocamelcase(string s) { 
string s1 =s; 
if (s1 == null) { 
return null; 
} 
s1 = s.tolowercase(); 
stringbuilder sb = new stringbuilder(s1.length()); 
boolean uppercase = false; 
for (int i = 0; i < s1.length(); i++) { 
char c = s1.charat(i); 
if (c == separator) { 
uppercase = true; 
} else if (uppercase) { 
sb.append(character.touppercase(c)); 
uppercase = false; 
} else { 
sb.append(c); 
} 
} 
return sb.tostring(); 
} 
/** 
* 驼峰命名法工具 
* @return 
* tocamelcase("hello_world") == "helloworld" 
* tocapitalizecamelcase("hello_world") == "helloworld" 
* tounderscorecase("helloworld") = "hello_world" 
*/ 
public static string tocapitalizecamelcase(string s) { 
string s1 = s; 
if (s1 == null) { 
return null; 
} 
s1 = tocamelcase(s1); 
return s1.substring(0, 1).touppercase() + s1.substring(1); 
} 
/** 
* 驼峰命名法工具 
* @return 
* tocamelcase("hello_world") == "helloworld" 
* tocapitalizecamelcase("hello_world") == "helloworld" 
* tounderscorecase("helloworld") = "hello_world" 
*/ 
public static string tounderscorecase(string s) { 
if (s == null) { 
return null; 
} 
stringbuilder sb = new stringbuilder(); 
boolean uppercase = false; 
for (int i = 0; i < s.length(); i++) { 
char c = s.charat(i); 
boolean nextuppercase = true; 
if (i < (s.length() - 1)) { 
nextuppercase = character.isuppercase(s.charat(i + 1)); 
} 
if ((i > 0) && character.isuppercase(c)) { 
if (!uppercase || !nextuppercase) { 
sb.append(separator); 
} 
uppercase = true; 
} else { 
uppercase = false; 
} 
sb.append(character.tolowercase(c)); 
} 
return sb.tostring(); 
} 
/** 
* 转换为js获取对象值,生成三目运算返回结果 
* @param objectstring 对象串 
* 例如:row.user.id 
* 返回:!row?'':!row.user?'':!row.user.id?'':row.user.id 
*/ 
public static string jsgetval(string objectstring){ 
stringbuilder result = new stringbuilder(); 
stringbuilder val = new stringbuilder(); 
string[] vals = split(objectstring, "."); 
for (int i=0; i<vals.length; i++){ 
val.append("." + vals[i]); 
result.append("!"+(val.substring(1))+"?'':"); 
} 
result.append(val.substring(1)); 
return result.tostring(); 
} 
}

有了上面这些基础的东西,只需要在写一个控制层接口,就可以看到每次返回一个page对象,然后里面封装好了查询对象的列表,并且是按分页得出列表。

package com.store.controller; 
import javax.servlet.http.httpservletrequest; 
import javax.servlet.http.httpservletresponse; 
import org.springframework.beans.factory.annotation.autowired; 
import org.springframework.web.bind.annotation.requestmapping; 
import org.springframework.web.bind.annotation.responsebody; 
import org.springframework.web.bind.annotation.restcontroller; 
import com.store.base.secondmodel.base.page; 
import com.store.base.secondmodel.pratice.model.product; 
import com.store.base.secondmodel.pratice.service.productservice; 
/** 
*todo 
*2016年10月11日 
*yiyong_wu 
*/ 
@restcontroller 
@requestmapping("/product") 
public class productcontroller { 
@autowired 
private productservice productservice; 
@responsebody 
@requestmapping(value="/getpageproduct") 
public page<product> getpageproduct(httpservletrequest request,httpservletresponse response){ 
page<product> page = productservice.findpage(new page<product>(request,response), new product()); 
return page; 
} 
}

最后在看一下页面怎么使用这个page对象,这样我们就完整地介绍了这个一个分页功能,代码很多,但很完整。

<%@ page contenttype="text/html;charset=utf-8"%> 
<%@ include file="/web-inf/views/include/taglib.jsp"%> 
<html> 
<head> 
<title></title> 
<meta name="decorator" content="default" /> 
function page(n, s) { 
if (n) 
$("#pageno").val(n); 
if (s) 
$("#pagesize").val(s); 
$("#searchform").attr("action", "${ctx}/app/bank/list"); 
$("#searchform").submit(); 
return false; 
} 
</script> 
</head> 
<body> 
<form:form id="searchform" modelattribute="xxxx" action="${ctx}/xxx" method="post" class="breadcrumb form-search "> 
<input id="pageno" name="pageno" type="hidden" value="${page.pageno}" /> 
<input id="pagesize" name="pagesize" type="hidden" value="${page.pagesize}" /> 
<ul class="ul-form"> 
<li> 
<label>是否上架:</label> 
<form:select id="status" path="status" class="input-medium"> 
<form:option value="" label=""/> 
<form:options items="${fns:getdictlist('yes_no_app')}" itemlabel="label" itemvalue="value" htmlescape="false"/> 
</form:select> 
</li> 
<li class="btns"><input id="btnsubmit" class="btn btn-primary" type="submit" value="查询"/> 
<li class="clearfix"></li> 
</ul> 
</form:form> 
<sys:message content="${message}" /> 
<sys:message content="${message}" /> 
<table id="contenttable" 
class="table table-striped table-bordered table-condensed"> 
<thead> 
<tr> 
<th>xxxx</th> 
<th>xxxx</th> 
<th>xxxx</th> 
<th>xxxx</th> 
<th>xxxx</th> 
<th>xxxx</th> 
<th>xxxx</th> 
<th>xxxx</th> 
</tr> 
</thead> 
<tbody> 
<c:foreach items="${page.list}" var="xxxx"> 
<tr> 
<td>${xxxx.name}</td> 
<td><a href="${ctx}/app/bank/form?id=${xxxx.id}">${xxxx.}</a></td> 
<td>${xxxx.}</td> 
<td>${xxxx.}</td> 
<td>${xxxx.}</td> 
<td>${fns:getdictlabel(xxxx.ishot, 'yes_no_app', '无')}</td> 
<td>${xxxx.}</td> 
<td><c:if test="${xxxx.status==1 }">上架</c:if> 
<c:if test="${xxxx.status==2 }">下架</c:if> 
</td> 
</tr> 
</c:foreach> 
</tbody> 
</table> 
<div class="pagination">${page} <li style="padding-top: 6px;padding-left: 12px;float: left;">共${page.count}条</li></div> 
</body> 
</html>

到这里就基本上把整个分页功能描述得比较清楚了,希望可以帮助到你们快速解决分页这个问题,当然要在前端显示分页漂亮的话要针对li做一些css样式啥的,最后祝福你可以快速掌握这个分页功能!

以上所述是小编给大家介绍的mybatis常用分页插件实现快速分页处理技巧,希望对大家有所帮助

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网