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获得。