package page; public interface Dialect { public boolean supportsLimit(); public String getLimitString(String sql, boolean hasOffset); public String getLimitString(String sql, int offset, int limit); public boolean supportsLimitOffset(); }
package page; public class MySQLDialect implements Dialect { protected static final String SQL_END_DELIMITER = ";"; public String getLimitString(String sql, boolean hasOffset) { return new StringBuffer(sql.length() + 20).append(trim(sql)) .append(hasOffset ? " limit ?,?" : " limit ?") .append(SQL_END_DELIMITER).toString(); } public String getLimitString(String sql, int offset, int limit) { sql = trim(sql); StringBuffer sb = new StringBuffer(sql.length() + 20); sb.append(sql); if (offset > 0) { sb.append(" limit ").append(offset).append(',').append(limit) .append(SQL_END_DELIMITER); } else { sb.append(" limit ").append(limit).append(SQL_END_DELIMITER); } return sb.toString(); } public boolean supportsLimit() { return true; } private String trim(String sql) { sql = sql.trim(); if (sql.endsWith(SQL_END_DELIMITER)) { sql = sql.substring(0, sql.length() - 1 - SQL_END_DELIMITER.length()); } return sql; } public boolean supportsLimitOffset() { // TODO Auto-generated method stub return true; } }
package page; 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.MappedStatement.Builder; import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.plugin.Interceptor; 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.session.ResultHandler; import org.apache.ibatis.session.RowBounds; @Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) }) public class OffsetLimitInterceptor implements Interceptor { static int MAPPED_STATEMENT_INDEX = 0; static int PARAMETER_INDEX = 1; static int ROWBOUNDS_INDEX = 2; static int RESULT_HANDLER_INDEX = 3; Dialect dialect; public Object intercept(Invocation invocation) throws Throwable { processIntercept(invocation.getArgs()); return invocation.proceed(); } void processIntercept(final Object[] queryArgs) { // queryArgs = query(MappedStatement ms, Object parameter, RowBounds // rowBounds, ResultHandler resultHandler) MappedStatement ms = (MappedStatement) queryArgs[MAPPED_STATEMENT_INDEX]; Object parameter = queryArgs[PARAMETER_INDEX]; final RowBounds rowBounds = (RowBounds) queryArgs[ROWBOUNDS_INDEX]; int offset = rowBounds.getOffset(); int limit = rowBounds.getLimit(); if (dialect.supportsLimit() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) { BoundSql boundSql = ms.getBoundSql(parameter); String sql = boundSql.getSql().trim(); if (dialect.supportsLimitOffset()) { sql = dialect.getLimitString(sql, offset, limit); offset = RowBounds.NO_ROW_OFFSET; } else { sql = dialect.getLimitString(sql, 0, limit); } limit = RowBounds.NO_ROW_LIMIT; queryArgs[ROWBOUNDS_INDEX] = new RowBounds(offset, limit); BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(), boundSql.getParameterObject()); MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql)); queryArgs[MAPPED_STATEMENT_INDEX] = newMs; } } private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) { 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()); builder.keyProperty(ms.getKeyProperty()); builder.timeout(ms.getTimeout()); builder.parameterMap(ms.getParameterMap()); builder.resultMaps(ms.getResultMaps()); builder.cache(ms.getCache()); MappedStatement newMs = builder.build(); return newMs; } public Object plugin(Object target) { return Plugin.wrap(target, this); } public void setProperties(Properties properties) { String dialectClass = new PropertiesHelper(properties) .getRequiredString("dialectClass"); try { dialect = (Dialect) Class.forName(dialectClass).newInstance(); } catch (Exception e) { throw new RuntimeException( "cannot create dialect instance by dialectClass:" + dialectClass, e); } System.out.println(OffsetLimitInterceptor.class.getSimpleName() + ".dialect=" + dialectClass); } public static class BoundSqlSqlSource implements SqlSource { BoundSql boundSql; public BoundSqlSqlSource(BoundSql boundSql) { this.boundSql = boundSql; } public BoundSql getBoundSql(Object parameterObject) { return boundSql; } } }
package page; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.util.Collection; import java.util.Enumeration; import java.util.InvalidPropertiesFormatException; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; public class PropertiesHelper { /** Never check system properties. */ public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0; /** * * Check system properties if not resolvable in the specified properties. * * This is the default. */ public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1; /** * * Check system properties first, before trying the specified properties. * * This allows system properties to override any other property source. */ public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2; Properties p; private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_NEVER; public PropertiesHelper(Properties p) { setProperties(p); } public PropertiesHelper(Properties p, int systemPropertiesMode) { setProperties(p); if (systemPropertiesMode != SYSTEM_PROPERTIES_MODE_NEVER && systemPropertiesMode != SYSTEM_PROPERTIES_MODE_FALLBACK && systemPropertiesMode != SYSTEM_PROPERTIES_MODE_OVERRIDE) { throw new IllegalArgumentException( "error systemPropertiesMode mode:" + systemPropertiesMode); } this.systemPropertiesMode = systemPropertiesMode; } public Properties getProperties() { return p; } public void setProperties(Properties props) { if (props == null) throw new IllegalArgumentException("properties must be not null"); this.p = props; } public String getRequiredString(String key) { String value = getProperty(key); if (isBlankString(value)) { throw new IllegalStateException( "required property is blank by key=" + key); } return value; } public String getNullIfBlank(String key) { String value = getProperty(key); if (isBlankString(value)) { return null; } return value; } public String getNullIfEmpty(String key) { String value = getProperty(key); if (value == null || "".equals(value)) { return null; } return value; } /** * 尝试从System.getProperty(key)及System.getenv(key)得到值 * @return */ public String getAndTryFromSystem(String key) { String value = getProperty(key); if (isBlankString(value)) { value = getSystemProperty(key); } return value; } private String getSystemProperty(String key) { String value; value = System.getProperty(key); if (isBlankString(value)) { value = System.getenv(key); } return value; } public Integer getInteger(String key) { String v = getProperty(key); if (v == null) { return null; } return Integer.parseInt(v); } public int getInt(String key, int defaultValue) { if (getProperty(key) == null) { return defaultValue; } return Integer.parseInt(getRequiredString(key)); } public int getRequiredInt(String key) { return Integer.parseInt(getRequiredString(key)); } public Long getLong(String key) { if (getProperty(key) == null) { return null; } return Long.parseLong(getRequiredString(key)); } public long getLong(String key, long defaultValue) { if (getProperty(key) == null) { return defaultValue; } return Long.parseLong(getRequiredString(key)); } public Long getRequiredLong(String key) { return Long.parseLong(getRequiredString(key)); } public Boolean getBoolean(String key) { if (getProperty(key) == null) { return null; } return Boolean.parseBoolean(getRequiredString(key)); } public boolean getBoolean(String key, boolean defaultValue) { if (getProperty(key) == null) { return defaultValue; } return Boolean.parseBoolean(getRequiredString(key)); } public boolean getRequiredBoolean(String key) { return Boolean.parseBoolean(getRequiredString(key)); } public Float getFloat(String key) { if (getProperty(key) == null) { return null; } return Float.parseFloat(getRequiredString(key)); } public float getFloat(String key, float defaultValue) { if (getProperty(key) == null) { return defaultValue; } return Float.parseFloat(getRequiredString(key)); } public Float getRequiredFloat(String key) { return Float.parseFloat(getRequiredString(key)); } public Double getDouble(String key) { if (getProperty(key) == null) { return null; } return Double.parseDouble(getRequiredString(key)); } public double getDouble(String key, double defaultValue) { if (getProperty(key) == null) { return defaultValue; } return Double.parseDouble(getRequiredString(key)); } public Double getRequiredDouble(String key) { return Double.parseDouble(getRequiredString(key)); } /** setProperty(String key,int value) ... start */ public Object setProperty(String key, int value) { return setProperty(key, String.valueOf(value)); } public Object setProperty(String key, long value) { return setProperty(key, String.valueOf(value)); } public Object setProperty(String key, float value) { return setProperty(key, String.valueOf(value)); } public Object setProperty(String key, double value) { return setProperty(key, String.valueOf(value)); } public Object setProperty(String key, boolean value) { return setProperty(key, String.valueOf(value)); } /** delegate method start */ public String getProperty(String key, String defaultValue) { return p.getProperty(key, defaultValue); } public String getProperty(String key) { String propVal = null; if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) { propVal = getSystemProperty(key); } if (propVal == null) { propVal = p.getProperty(key); } if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) { propVal = getSystemProperty(key); } return propVal; } public Object setProperty(String key, String value) { return p.setProperty(key, value); } public void clear() { p.clear(); } public Set<Entry<Object, Object>> entrySet() { return p.entrySet(); } public Enumeration<?> propertyNames() { return p.propertyNames(); } public boolean contains(Object value) { return p.contains(value); } public boolean containsKey(Object key) { return p.containsKey(key); } public boolean containsValue(Object value) { return p.containsValue(value); } public Enumeration<Object> elements() { return p.elements(); } public Object get(Object key) { return p.get(key); } public boolean isEmpty() { return p.isEmpty(); } public Enumeration<Object> keys() { return p.keys(); } public Set<Object> keySet() { return p.keySet(); } public void list(PrintStream out) { p.list(out); } public void list(PrintWriter out) { p.list(out); } public void load(InputStream inStream) throws IOException { p.load(inStream); } public void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException { p.loadFromXML(in); } public Object put(Object key, Object value) { return p.put(key, value); } public void putAll(Map<? extends Object, ? extends Object> t) { p.putAll(t); } public Object remove(Object key) { return p.remove(key); } /** @deprecated */ public void save(OutputStream out, String comments) { p.save(out, comments); } public int size() { return p.size(); } public void store(OutputStream out, String comments) throws IOException { p.store(out, comments); } public void storeToXML(OutputStream os, String comment, String encoding) throws IOException { p.storeToXML(os, comment, encoding); } public void storeToXML(OutputStream os, String comment) throws IOException { p.storeToXML(os, comment); } public Collection<Object> values() { return p.values(); } public String toString() { return p.toString(); } private static boolean isBlankString(String value) { return value == null || "".equals(value.trim()); } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN" "http://ibatis.apache.org/dtd/ibatis-3-config.dtd"> <configuration> <plugins> <plugin interceptor="page.OffsetLimitInterceptor"> <property name="dialectClass" value="page.MySQLDialect" /> </plugin> </plugins> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8" /> <property name="username" value="root" /> <property name="password" value="sa" /> </dataSource> </environment> </environments> <mappers> <mapper resource="com/company/project/model/mapper/UserInfoMapper.xml" /> </mappers> </configuration>
import java.io.Reader; import java.util.List; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.company.project.model.UserInfo; /** * @author badqiu */ public class Ibatis3DemoMain { public static void main(String[] args) throws Exception { Reader reader = Resources.getResourceAsReader("Configuration.xml"); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession session = sessionFactory.openSession(); testCrud(session); } private static void testCrud(SqlSession session) { // test select // Long count = (Long)session.selectOne("UserInfo.count",user); // assertTrue(1 == count); List<UserInfo> list = session.selectList("UserInfo.pageSelect",user,new RowBounds(0, 2)); for (UserInfo info : list) { System.out.println(info.toString()); } // SqlSession.selectList(statement, parameter, new RowBounds(offset,limit))即可使用物理分页 // fromDb = (UserInfo)list.get(0); } }
由于版本问题,BoundSql类在ibatis3的版本中构造函数新增了一个参数,
public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) { this.sql = sql; this.parameterMappings = parameterMappings; this.parameterObject = parameterObject; this.additionalParameters = new HashMap<String, Object>(); this.metaParameters = configuration.newMetaObject(additionalParameters); }
所以,必须要先获得configuration,configuration可以通过MappedStatement获得。