Mybatis常用分页插件实现快速分页处理技巧
在未分享整个查询分页的执行代码之前,先了解一下执行流程。
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常用分页插件实现快速分页处理技巧,希望对大家有所帮助
下一篇: 牛客网华为机试题目:IP 地址分类