Before sharing the execution code for the entire query page, understand the execution process first.
1. Generally, it uses mybatis plug-in interceptor to intercept before SQL execution, and add limit XX to the query statement
2. Use a Page object to run through the entire execution process. This Page object requires writing front-end pagination components in Java.
3. Use a relatively complete set of three-layer entity, dao, and service to support this pagination architecture
4. Some auxiliary classes used in this page
Note: There is a lot of content shared. I won’t list the required jars one by one. When you use this pagination function, just look for any jar packages at night. Use the maven package to import as much as possible because maven can reduce version conflicts and other advantages.
I can only say that you can use this easier paging function as quickly as possible. If you don’t understand it, please add me to QQ and discuss it together. Don't swear! Also, the article may be relatively large, but taking some time, reading it and practicing it will definitely gain a lot.
Step 1: Since the topic revolves around how to paginate, let’s start with mybatis. First, we will take out two more important configuration files related to mybatis for a brief understanding. One is mybatis-config.xml, and the other is the mapper configuration file corresponding to the entity. I will write comments on the configuration file, and everyone will understand it at a glance.
mybatis-config.xml
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- Global Parameters--> <settings> <!-- Make the global mapper enable or disable cache. --> <setting name="cacheEnabled" value="false"/> <!-- Enable or disable lazy loading globally. When disabled, all associated objects are loaded instantly. --> <setting name="lazyLoadingEnabled" value="true"/> <!-- When enabled, an object with a lazy loading property will fully load any property when it is called. Otherwise, each property will be loaded as needed. --> <setting name="aggressiveLazyLoading" value="true"/> <!-- Whether to allow a single sql to return multiple datasets (depending on the compatibility of the driver) default:true --> <setting name="multipleResultSetsEnabled" value="true"/> <!-- Whether alias for columns can be used (depending on the compatibility of the driver) default:true --> <setting name="useColumnLabel" value="true"/> <!-- Allow JDBC to generate primary keys. Drive support is required. If set to true, this setting will force the generated primary key, some drives are incompatible but can still be executed. default:false --> <setting name="useGeneratedKeys" value="false"/> <!-- Specify how MyBatis automatically maps columns of data base table NONE: not obscuring PARTIAL: part FULL: all--> <setting name="autoMappingBehavior" value="PARTIAL"/> <!-- This is the default execution type (SIMPLE: simple; REUSE: The executor may reuse prepared statements statements; BATCH: The executor can repeat statements and batch updates) --> <setting name="defaultExecutorType" value="SIMPLE"/> <!-- Convert fields using camel nomenclature. --> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- Setting local cache range session: There will be data sharing statement: statement scope (this will not be data sharing) defalut:session --> <setting name="localCacheScope" value="SESSION"/> <!-- Setting but the JDBC type is empty, some drivers need to specify the value, default:OTHER, and there is no need to specify the type when inserting a null value --> <setting name="jdbcTypeForNull" value="NULL"/> <setting name="logPrefix" value="dao."/> </settings> <!--Alias is the name of a shorter Java type --> <typeAlias> <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> </typeAlias> <!-- Plug-in configuration, here is a page interceptor configured for mybatis, and we need to implement this page interceptor ourselves --> <plugins> <plugin interceptor="com.store.base.secondmodel.base.pageinterceptor.PaginationInterceptor" /> </plugins> </configuration>
A ProductMapper.xml is used as a test object, and this mapper file simply configures a query statement that needs to be used.
<?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>
Step 2: Next, we will conduct in-depth analysis and learning for this pagination interceptor, mainly including the following classes and their corresponding interfaces
(1) BaseInterceptor Interceptor basic class
(2) PaginationInterceptor The pagination plugin class we want to use, inherit the above basic class
(3) SQLHelper is mainly used to execute count statements in advance, and also to obtain the entire complete pagination statement.
(4) Dialect, MysqlDialect, is mainly used to whether the database supports limit statements, and then encapsulate the complete limit statements
The following are the sharing displays of these categories
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 pagination interceptor base class* @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; /** * Convert and check parameters* @param parameterObject ParameterObject ParameterObject * @param page Pagination object* @return Pagination object* @throws NoSuchFieldException Parameter cannot be found*/ @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; } } /** * Set properties, support custom dialect classes and how to formulate databases* <code>dialectClass</code>, custom dialect classes. This can be configured without configuring * <ode>dbms</ode> database type, database supported by the plug-in * <code>sqlPattern</code> SQL ID that needs to be intercepted * @param p Properties*/ protected void initProperties(Properties p) { Dialect dialect = null; String dbType = Global.getConfig("jdbc.type"); if("mysql".equals(dbType)){ dialect = new MySQLDialect(); } if (dalect == 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; 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; /** * Database paging plugin, only intercepts query statements. * @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(); // Get the pagination parameter object Page<Object> page = null; if (parameterObject != null) { page = convertParameter(parameterObject, page); } // If the paging object is set, paging is performed if (page != null && page.getPageSize() != -1) { if (StringUtils.isBlank(boundSql.getSql()))) { return null; } String originalSql = boundSql.getSql().trim(); // Get the total number of records page.setCount(SQLHelper.getCount(originalSql, null, mappedStatement, parameterObject, boundSql, log)); // Pagination query localized objects to modify the database and pay attention to modifying the implementation 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()); // Solve MyBatis pagination foreach parameter failure start if (Reflections.getFieldValue(boundSql, "metaParameters") != null) { MetaObject mo = (MetaObject) Reflections.getFieldValue(boundSql, "metaParameters"); Reflections.setFieldValue(newBoundSql, "metaParameters", mo); } // Solve MyBatis pagination foreach parameter failure 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 tool class* @author yiyong_wu * */ public class SQLHelper { /** * Default private constructor*/ private SQLHelper() { } /** * Set the value for SQL parameters (?), refer to org.apache.ibatis.executor.parameter.DefaultParameterHandler * * @param ps represents precompiled SQL The object of the statement. * @param mappedStatement MappedStatement * @param boundSql SQL * @param parameterObject ParameterObject ParameterObject @throws java.sql.SQLException Database exception*/ @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()); } } } } /** * Query total records* @param sql SQL statement* @param connection Database connection* @param mappedStatement mapped * @param parameterObject parameter* @param boundSql boundSql * @return Total number of records* @throws SQLException sql query error*/ 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); //Solve MyBatis pagination foreach parameter failure start if (Reflections.getFieldValue(boundSql, "metaParameters") != null) { MetaObject mo = (MetaObject) Reflections.getFieldValue(boundSql, "metaParameters"); Reflections.setFieldValue(countBS, "metaParameters", mo); } //Solve MyBatis pagination foreach parameter failure 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(); } } } /** * Generate specific pages according to database dialect * @param sql Sql statement in @param sql Mapper* @param page paging object* @param dialect dialect type* @return paging 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; } } /** * Remove the select clause of qlString. * @param hql * @return */ @SuppressWarnings("unused") private static String removeSelect(String qlString){ int beginPos = qlString.toLowerCase().indexOf("from"); return qlString.substring(beginPos); } /** * Remove the orderBy clause of hql. * @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 interface
package com.store.base.secondmodel.base.dialect; /** * Similar to hibernate, but only the paging part is simplified* @author yiyong_wu * */ public interface Dialect { /** * Does the database itself support the current paging query method for paging* If the database does not support it, the database paging will not be paging* * @return true: supports the current paging query method */ public boolean supportLimit(); /** * Convert sql to pagination SQL and call paging sql separately * * @param sql SQL statement * @param offset Number of starts * @param limit How many records are displayed per page* @return sql for pagination query */ public String getLimitString(String sql, int offset, int limit); }MySQLDialect.java
package com.store.base.secondmodel.base.dialect; /** * Implementation of Mysql dialect* @author yiyong_wu * */ public class MySQLDialect implements Dialect { @Override public boolean supportLimit() { return true; } @Override public String getLimitString(String sql, int offset, int limit) { return getLimitString(sql, offset, Integer.toString(offset),Integer.toString(limit)); } /** * Turn SQL into a paged SQL statement, providing replacement of offset and limit with placeholder. * <pre> * For example, mysql * dialect.getLimitString("select * from user", 12, ":offset",0,":limit") will return * select * from user limit :offset,:limit * </pre> * * @param sql Actual SQL statement * @param offsetPlaceholder Pagination start record number * @param offsetPlaceholder Pagination start record number - placeholder Pagination record number placeholder @return Pagination sql containing placeholders */ 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(); } }Almost here we have shared how to implement the entire page, but we have more important tasks. If we want the whole thing to run, we must have basic work to do. Next, we analyze the entire set of Page objects and the three-layer architecture it is based on, and use product as an entity for analysis. After talking about a complete three-layer architecture, the rewards will definitely be full. Let's talk about it in the order of entity->dao->service in turn.
First, we must inherit two abstract entity classes BaseEntity and DataEntity for our entities.
BaseEntity.java mainly places Page member variable. After inheriting it, each entity can have this member variable.
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; /** * The top-level Entity * @author yiyong_wu * * @param <T> */ public abstract class BaseEntity<T> implements Serializable { private static final long serialVersionUID = 1L; /** * Delete the tag (0: normal; 1: delete; 2: audit;) */ public static final String DEL_FLAG_NORMAL = "0"; public static final String DEL_FLAG_DELETE = "1"; public static final String DEL_FLAG_AUDIT = "2"; /** * Entity number (unique identifier) */ protected String id; /** * Current user*/ protected StoreUser currentUser; /** * Current entity pagination object*/ protected Page<T> page; /** * Custom SQL (SQL identifier, SQL content) */ 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; } /** * This will be called when performing insert updates for shiro to get the current user* @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; } /** * Execute the method before insertion, subclass implements */ public abstract void preInsert(); /** * Execute the method before update, subclass implements */ public abstract void preUpdate(); /** * Whether it is a new record (default: false), call setIsNewRecord() to set a new record, and use a custom ID. * After setting to true, the insert statement is forced to be executed. The ID will not be automatically generated and needs to be passed in manually. * @return */ public boolean getIsNewRecord() { return StringUtils.isBlank(getId()); } /** * Global variable object*/ @JsonIgnore public Global getGlobal() { return Global.getInstance(); } /** * Get database name*/ @JsonIgnore public String getDbName(){ return Global.getConfig("jdbc.type"); } @Override public String toString() { return ReflectionToStringBuilder.toString(this); } }DataEntity.java, mainly stores update and delete time, create user, update user, logical deletion flag, etc.
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; /** * Data Entity * @author yiyong_wu * * @param <T> */ public abstract class DataEntity<T> extends BaseEntity<T> { private static final long serialVersionUID = 1L; protected StoreUser createBy; // Creator protected Date createDate; // Creation date protected StoreUser updateBy; // Updater protected Date updateDate; // Updated date protected String delFlag; // Delete tag (0: normal; 1: delete; 2: audit) public DataEntity() { super(); this.delFlag = DEL_FLAG_NORMAL; } public DataEntity(String id) { super(id); } /** * Execute the method before insertion, you need to manually call */ @Override public void preInsert() { // No restriction on the ID to UUID, call setIsNewRecord() to use a custom ID // User user = UserUtils.getUser(); // if (StringUtils.isNotBlank(user.getId())) { // this.updateBy = user; // this.createBy = user; // } this.updateDate = new Date(); this.createDate = this.updateDate; } /** * Execute the method before update, you need to manually call */ @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 Product category
package com.store.base.secondmodel.pratice.model; import com.store.base.secondmodel.base.DataEntity; /** *Product Basics Class*October 11, 2016*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; } }How about it? Do you see a very complex entity inheritance relationship? But what is there? The more complex it is, the more complete it will be. Next I will look at the layer, the same three layers, ready to be baptized
BaseDao.java reserved interface
package com.store.base.secondmodel.base; /** * Top-most DAO interface* @author yiyong_wu * */ public interface BaseDao { } CrudDao.java A dao interface layer for addition, deletion, modification and query [java] view plain copy print? View code fragments on CODE derived to my code fragment package com.store.base.secondmodel.base; import java.util.List; /** * Define DAO interface for addition, deletion, modification and query* @author yiyong_wu * * @param <T> */ public interface CrudDao<T> extends BaseDao { /** * Get a single piece of data* @param id * @return */ public T get(String id); /** * Get a single piece of data* @param entity * @return */ public T get(T entity); /** * Query the data list. If you need paging, please set the paging object, such as: entity.setPage(new Page<T>()); * @param entity * @return */ public List<T> findList(T entity); /** * Query all data list* @param entity * @return */ public List<T> findAllList(T entity); /** * Query all data list* @see public List<T> findAllList(T entity) * @return public List<T> findAllList(); */ /** * Insert data* @param entity * @return */ public int insert(T entity); /** * Update data* @param entity * @return */ public int update(T entity); /** * Delete data (usually logical deletion, update the del_flag field to 1) * @param id * @see public int delete(T entity) * @return */ public int delete(String id); /** * Delete data (usually logical deletion, update the del_flag field to 1) * @param entity * @return */ public int delete(T entity); }ProductDao.java mybatis corresponding interface mapper is also a dao implementation. You need to customize annotation @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 *October 11, 2016*yiyong_wu */ @MyBatisRepository public interface ProductDao extends CrudDao<Product>{ }Custom annotation MyBatisRepository.java is related to custom annotation. I won’t explain too much here, there is a lot of online information.
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; /** * Identify the DAO of MyBatis to facilitate scanning of {@link org.mybatis.spring.mapper.MapperScannerConfigurer}. * * Please note that you want to configure the configuration to scan the annotated class in the spring configuration file* *<bean id="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 ""; } Note: The ProductMapper.xml file that has a strong connection with ProductDao.java is the ProductMapper.xml file. You can see that the namespace of the configuration file above points to the path to this dao.
Next we enter the final service analysis, and it is also a three-layer inheritance.
BaseService.java
package com.store.base.secondmodel.base; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; /** * The top-level parent class of Service* @author yiyong_wu * */ @Transactional(readOnly = true) public abstract class BaseService { // protected Logger logger = LoggerFactory.getLogger(getClass()); }CrudService.java related business interface implementation
package com.store.base.secondmodel.base; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; /** * Add, delete, modify and check Service base class* @author yiyong_wu * * @param <D> * @param <T> */ public abstract class CrudService<D extends CrudDao<T>, T extends DataEntity<T>> extends BaseService { /** * Persistence layer object*/ @Autowired protected D dao; /** * Get a single piece of data* @param id * @return */ public T get(String id) { return dao.get(id); } /** * Get a single piece of data* @param entity * @return */ public T get(T entity) { return dao.get(entity); } /** * Query list data* @param entity * @return */ public List<T> findList(T entity) { return dao.findList(entity); } /** * Query paging data* @param page paging object* @param entity * @return */ public Page<T> findPage(Page<T> page, T entity) { entity.setPage(page); page.setList(dao.findList(entity)); return page; } /** * Save data (insert or update) * @param entity */ @Transactional(readOnly = false) public void save(T entity) { if (entity.getIsNewRecord()){ entity.preInsert(); dao.insert(entity); }else{ entity.preUpdate(); dao.update(entity); } } /** * Delete data* @param entity */ @Transactional(readOnly = false) public void delete(T entity) { dao.delete(entity); } }ProductService.java, inherits the CrudService interface, pay attention to a pattern of injecting dao and entity types.
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>${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("<([az]+?)//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"> <input id="pageNo" name="pageNo" type="hidden" value="${page.pageNo}" /> <input id="pageSize" name="pageSize" type="hidden" value="${page.pageSize}" /> <ul> <li> <label>是否上架:</label> <form:select id="status" path="status"> <form:option value="" label=""/> <form:options items="${fns:getDictList('yes_no_app')}" itemLabel="label" itemValue="value" htmlEscape="false"/> </form:select> </li> <li><input id="btnSubmit" type="submit" value="查询"/> <li></li> </ul> </form:form> <sys:message content="${message}" /> <sys:message content="${message}" /> <table id="contentTable"> <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>${page} <li style="padding-top: 6px;padding-left: 12px;float: left;">共${page.count}条</li></div> </body> </html>到这里就基本上把整个分页功能描述得比较清楚了,希望可以帮助到你们快速解决分页这个问题,当然要在前端显示分页漂亮的话要针对li做一些css样式啥的,最后祝福你可以快速掌握这个分页功能!
以上所述是小编给大家介绍的Mybatis常用分页插件实现快速分页处理技巧,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。 Thank you very much for your support to Wulin.com website!