Mybatis操作Sybase資料庫遊標分頁
阿新 • • 發佈:2019-01-21
宣告
歡迎轉載請註明原文地址,謝謝。
簡介
sybase資料庫本身是不支援分頁sql語句,想用mybatis操作sybase分頁很麻煩,在網上也看了不少使用資料庫儲存過程解決的。
但總覺得侷限性太大出現很多問題,所以本文介紹結合mybatis進行遊標分頁處理,不影響mybatis本身寫法的限制,
只需遵循分頁介面規範即可(可支援多資料庫分頁,不過效率沒有資料庫本身分頁效率高)。
本文原始碼地址:https://github.com/ssp1523/sybase-mybatis-pagination,最好先下載原始碼結合文字效果更佳。
第一步:建立maven web專案
maven pom檔案配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId >com.smp</groupId>
<artifactId>sybase-mybatis-pagination</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<description>Mybatis 基於sybase資料庫遊標分頁例子</description>
<build>
<plugins>
<plugin >
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<javax-activation.version>1.1</javax-activation.version>
<spring.version>4.0.4.RELEASE</spring.version>
<mysql-connector-java.version>5.1.34</mysql-connector-java.version>
<java.version>1.7</java.version>
</properties>
<dependencies>
<!-- spring start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-dao</artifactId>
<version>2.0.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-mock</artifactId>
<version>2.0.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring end -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
</project>
web.xml檔案配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:log4j.properties</param-value>
</context-param>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/ApplicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
resources/spring/ApplicationContext.xml spring xml檔案配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 啟用註解 -->
<context:annotation-config/>
<context:component-scan base-package="com.smp"/>
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:dbconfig.properties" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
<!-- 配置mybatis -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- mapper掃描 -->
<property name="mapperLocations" value="classpath:com/smp/mapper/*.xml"/>
</bean>
<!--mapper掃描-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.smp.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<bean id="sqlSessionTemplate"
class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"/>
</bean>
</beans>
第二步:建立分頁相關配置和java類
資料庫連線配置檔案 resources/dbconfig.properties,由於是演示專案所以配置較簡單
url=你的資料庫url地址
driverClassName=net.sourceforge.jtds.jdbc.Driver
username=你的資料庫使用者名稱
password=你的資料庫密碼
分頁核心類 SybasePagination
package com.smp;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.jdbc.core.*;
import org.springframework.jdbc.datasource.AbstractDataSource;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
/**
* sybase 分頁
* Created by ssp on 2017/3/17.
*/
@Component
public class SybasePagination {
@Resource(name = "sqlSessionFactory")
private SqlSessionFactory sqlSessionFactory;
private BoundSql getBoundSql(String mapperId, Object parameter) {
return getMappedStatement(mapperId).getBoundSql(parameter);
}
private MappedStatement getMappedStatement(String mapperId) {
return sqlSessionFactory.getConfiguration().getMappedStatement(mapperId);
}
/**
* mybatis遊標分頁方法
* @param mapperId mybatis 對映id 如:com.smp.mapper.CustomMapper.findByListPage
* @param params 分頁引數 暫時只支援Page型別引數
* @return 分頁結果
*/
public List<PageData> listPage(String mapperId, Object params) throws SQLException, NoSuchFieldException, IllegalAccessException {
return queryForListPage(sqlSessionFactory.openSession().getConnection(), getBoundSql(mapperId, params), getMappedStatement(mapperId), (Page) params);
}
/**
* 分頁核心方法,處理遊標分頁操作
* 由於是使用jdbc遊標分頁,所以此處使用JdbcTemplate簡化操作。
* @param connection 資料庫連線
* @param boundSql 分頁BoundSql
* @param mappedStatement 分頁 MappedStatement
* @param page 分頁Page類
* @return 分頁結果
*/
@SuppressWarnings("unchecked")
private List<PageData> queryForListPage(final Connection connection, final BoundSql boundSql, final MappedStatement mappedStatement, final Page page) throws SQLException, NoSuchFieldException, IllegalAccessException {
//分頁count
PagePlugin.pageCount(connection, mappedStatement, boundSql, page);
return (List) new JdbcTemplate(new JdbcTemplateDataSource(connection))
.query(new MyPreparedStatementCreator(boundSql, mappedStatement, page)
, new RowMapperResultSetExtractor(new ColumnPageDataRowMapper(page)) {
@Override
public List extractData(ResultSet rs) throws SQLException {
if (page.getCurrentResult() != 0) {
//將遊標移動到第一條記錄
rs.first();
// 遊標移動到要輸出的第一條記錄
rs.relative(page.getCurrentResult() - 1);
}
return super.extractData(rs);
}
});
}
private class JdbcTemplateDataSource extends AbstractDataSource {
private Connection connection;
JdbcTemplateDataSource(Connection connection) {
this.connection = connection;
}
public Connection getConnection() throws SQLException {
return connection;
}
public Connection getConnection(String username, String password) throws SQLException {
return connection;
}
}
private class MyPreparedStatementCreator implements PreparedStatementCreator {
private BoundSql boundSql;
private MappedStatement mappedStatement;
private Page page;
MyPreparedStatementCreator(BoundSql boundSql, MappedStatement mappedStatement, Page page) {
this.boundSql = boundSql;
this.mappedStatement = mappedStatement;
this.page = page;
}
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement pstat = con.prepareStatement(boundSql.getSql(), ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
PagePlugin.setParameters(pstat, mappedStatement, boundSql, boundSql.getParameterObject());
pstat.setMaxRows(page.getCurrentResult() + page.getShowCount());
return pstat;
}
}
private class ColumnPageDataRowMapper implements RowMapper<PageData> {
private Page page;
ColumnMapRowMapper columnMapRowMapper = new ColumnMapRowMapper() {
@Override
protected Map<String, Object> createColumnMap(int columnCount) {
return new PageData();
}
@Override
protected String getColumnKey(String columnName) {
if (page.getCamelName())
return StringUtil.camelName(columnName);
else
return super.getColumnKey(columnName);
}
};
private ColumnPageDataRowMapper(Page page) {
this.page = page;
}
public PageData mapRow(ResultSet rs, int rowNum) throws SQLException {
return (PageData) columnMapRowMapper.mapRow(rs, rowNum);
}
}
}
Page分頁vo類,儲存分頁相關資訊,作為分頁引數使用
package com.smp;
public class Page {
private int showCount; //每頁顯示記錄數
private int totalPage; //總頁數
private int totalResult; //總記錄數
private int currentPage; //當前頁
private int currentResult; //當前記錄起始索引
private boolean entityOrField; //true:需要分頁的地方,傳入的引數就是Page實體;false:需要分頁的地方,傳入的引數所代表的實體擁有Page屬性
private PageData pd = new PageData();
private Boolean camelName = false;
public Page() {
}
public int getTotalPage() {
if (totalResult % showCount == 0)
totalPage = totalResult / showCount;
else
totalPage = totalResult / showCount + 1;
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getTotalResult() {
return totalResult;
}
public void setTotalResult(int totalResult) {
this.totalResult = totalResult;
}
public int getCurrentPage() {
if (currentPage <= 0)
currentPage = 1;
if (currentPage > getTotalPage())
currentPage = getTotalPage();
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getShowCount() {
return showCount;
}
public void setShowCount(int showCount) {
this.showCount = showCount;
}
public int getCurrentResult() {
currentResult = (getCurrentPage() - 1) * getShowCount();
if (currentResult < 0)
currentResult = 0;
return currentResult;
}
public void setCurrentResult(int currentResult) {
this.currentResult = currentResult;
}
public boolean isEntityOrField() {
return entityOrField;
}
public void setEntityOrField(boolean entityOrField) {
this.entityOrField = entityOrField;
}
public PageData getPd() {
return pd;
}
public void setPd(PageData pd) {
this.pd = pd;
}
public Boolean getCamelName() {
return camelName;
}
public void setCamelName(Boolean camelName) {
this.camelName = camelName;
}
}
PageData類,分頁查詢的方法結果一條記錄為一個PageData
package com.smp;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class PageData extends HashMap<String, Object> implements Map<String, Object> {
private static final long serialVersionUID = 1L;
private Map<String, Object> map = null;
public PageData() {
map = new HashMap<>();
}
@Override
public Object get(Object key) {
return map.get(key);
}
public String getString(Object key) {
return (String) get(key);
}
@SuppressWarnings("unchecked")
@Override
public Object put(String key, Object value) {
return map.put(key, value);
}
@Override
public Object remove(Object key) {
return map.remove(key);
}
public void clear() {
map.clear();
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public Set<Map.Entry<String, Object>> entrySet() {
return map.entrySet();
}
public boolean isEmpty() {
return map.isEmpty();
}
public Set<String> keySet() {
return map.keySet();
}
@SuppressWarnings("unchecked")
public void putAll(Map t) {
map.putAll(t);
}
public int size() {
return map.size();
}
public Collection<Object> values() {
return map.values();
}
}
PagePlugin類,分頁工具類,處理總條數和引數設定。
package com.smp;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.ExecutorException;
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 java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
class PagePlugin {
static void pageCount(Connection connection, MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject) throws NoSuchFieldException, IllegalAccessException, SQLException {
String countSql = "select count(0) from (" + sqlProcess(boundSql.getSql()) + ") tmp_count"; //記錄統計 == oracle 加 as 報錯(SQL command not properly ended)
PreparedStatement countStmt = connection.prepareStatement(countSql);
BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings(), parameterObject);
setParameters(countStmt, mappedStatement, countBS, parameterObject);
ResultSet rs = countStmt.executeQuery();
int count = 0;
if (rs.next()) {
count = rs.getInt(1);
}
rs.close();
countStmt.close();
Page page;
if (parameterObject instanceof Page) { //引數就是Page實體
page = (Page) parameterObject;
page.setEntityOrField(true);
page.setTotalResult(count);
} else { //引數為某個實體,該實體擁有Page屬性
Field pageField = ReflectHelper.getFieldByFieldName(parameterObject, "page");
if (pageField != null) {
page = (Page) ReflectHelper.getValueByFieldName(parameterObject, "page");
if (page == null)
page = new Page();
page.setEntityOrField(false);
page.setTotalResult(count);
ReflectHelper.setValueByFieldName(parameterObject, "page", page); //通過反射,對實體物件設定分頁物件
} else {
throw new NoSuchFieldException(parameterObject.getClass().getName() + "不存在 page 屬性!");
}
}
}
/**
* sql處理
*
* @param sql 被處理的sql
* @return 處理後的sql語句
*/
private static String sqlProcess(String sql) {
//去掉sql語句 order by 語句
int orderIndex;
if ((orderIndex = sql.toLowerCase().lastIndexOf("order")) != -1) {
return sql.substring(0, orderIndex);
}
return sql;
}
/**
* 對SQL引數(?)設值,參考org.apache.ibatis.executor.parameter.DefaultParameterHandler
*/
@SuppressWarnings("unchecked")
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);
}
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());
}
}
}
}
}
ReflectHelper類,反射工具類
package com.smp;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.ExecutorException;
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 java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
class PagePlugin {
static void pageCount(Connection connection, MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject) throws NoSuchFieldException, IllegalAccessException, SQLException {
String countSql = "select count(0) from (" + sqlProcess(boundSql.getSql()) + ") tmp_count"; //記錄統計 == oracle 加 as 報錯(SQL command not properly ended)
PreparedStatement countStmt = connection.prepareStatement(countSql);
BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings(), parameterObject);
setParameters(countStmt, mappedStatement, countBS, parameterObject);
ResultSet rs = countStmt.executeQuery();
int count = 0;
if (rs.next()) {
count = rs.getInt(1);
}
rs.close();
countStmt.close();
Page page;
if (parameterObject instanceof Page) { //引數就是Page實體
page = (Page) parameterObject;
page.setEntityOrField(true);
page.setTotalResult(count);
} else { //引數為某個實體,該實體擁有Page屬性
Field pageField = ReflectHelper.getFieldByFieldName(parameterObject, "page");
if (pageField != null) {
page = (Page) ReflectHelper.getValueByFieldName(parameterObject, "page");
if (page == null)
page = new Page();
page.setEntityOrField(false);
page.setTotalResult(count);
ReflectHelper.setValueByFieldName(parameterObject, "page", page); //通過反射,對實體物件設定分頁物件