欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-03-12 12:18:02
在未分享整个查询分页的执行代码之前,先了解一下执行流程。 1.总体上是利用mybatis的插件拦截器,在sql执行之前拦截,为查询语句加上limit x x 2.用一个...

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

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常用分页插件实现快速分页处理技巧,希望对大家有所帮助