日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一步步教你mybatis分页,mybatis分页拦截器 使用,mybatis拦截器分页

發布時間:2024/1/1 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一步步教你mybatis分页,mybatis分页拦截器 使用,mybatis拦截器分页 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

????????? mybatis 分頁詳解、mybatis分頁查詢,mybatis分頁攔截器使用、struts2下mybatis分頁

?


mybatis默認是支持分頁的,內部通過創建可滾動的ResultSet(ResultSet.TYPE_FORWARD_ONLY)對結果集指針進行跳轉以達到分頁控制的目的。實際使用,需要傳入RowBounds類型參數來告知mybatis做分頁控制,RowBounds構造器有兩個參數:

RowBounds(int offset, int limit), offset,從第offset條開始查(起始于0),limit查詢個數。如:

RowBounds(0, 11):第一頁,顯示十一條【0-10】、

RowBounds(11, 10):第二頁,顯示十一條【11-21】

。。。。

不過mybatis默認分頁存在兩個問題:1.通過ResultSet控制指針進行分頁與數據庫本身通過sql語句進行分頁相比,查詢性能欠佳。2.無法返回總記錄數,通常情況下前端表格除了要顯示第n頁數據,也需要顯示總記錄數,通過內置RowBounds顯然不能滿足需求。

有些兄弟可能不知道怎么使用,沒關系,我先做一個簡單的示例,供參考。

問題:現有區號表一張,需要支持表分頁查詢:

1.定義區號javabean AreaCode.java:

AreaCode.java

public class AreaCode implements Serializable{//javabean與數據庫字段一致private String provId;private String description;public AreaCode() {super();}public String getProvId() {return provId;}public void setProvId(String provId) {this.provId = provId;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}}

2.定義mapper接口AreaCodeMapper.java(為了簡單只定義一個查詢接口):

public interface AreaCodeMapper {//這個方法不支持分頁,是全查public List<AreaCode> list();//通過RowBounds參數告知mybatis此方法需要分頁查詢public List<AreaCode> list(RowBounds rowBounds);}

3.定義配置文件AreaCodeMapper.xml(配置僅供參考):

<?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="AreaCodeMapper"><select id="list" resultType="AreaCode">select provId, description from areacodecfg</select> </mapper>

4.定義配置文件configuration.xml(配置僅供參考):

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><environments default="development"><environment id="development"><transactionManager type="JDBC" /><dataSource type="UNPOOLED"><!-- ${driver}為實際的數據庫驅動名 --><property name="driver" value="${driver}" /><property name="url" value="${url}" /><property name="username" value="${username}" /><property name="password" value="${password}" /></dataSource></environment></environments><mappers><!-- 添加mapper文件 --><mapper resource="AreaCodeMapper.xml" /></mappers> </configuration>

5.測試:

public static void main(String[] args) throws SQLException {Reader reader = null;SqlSessionFactory sf;SqlSession session = null;try {SqlSessionFactoryBuilder build = new SqlSessionFactoryBuilder();//根據配置文件生成SqlSessionFactoryreader = Resources.getResourceAsReader("test/configuration.xml");sf = build.build(reader);session = sf.openSession();AreaCodeMapper mapper = session.getMapper(AreaCodeMapper.class);//查詢第一頁,每頁十條記錄RowBounds rowBounds = new RowBounds(0, 10);List<AreaCode> list = mapper.list(rowBounds);} catch (IOException e) {e.printStackTrace();} finally {if (reader != null) {try {reader.close();} catch (IOException e) {}}if (session != null) {session.close();}}}

分頁查詢已搞定,但以上查詢并不包含總記錄數,這個比較麻煩,如果你愿意,再添加一個總記錄數查詢的接口,不愿意接著往下看。

下面通過擴展RowBounds,使其支持數據庫分頁與總記錄數統計。

1.由于不同的數據庫,分頁sql語句略有不同,我們通過一個枚舉類型DBType.java,用于區分不同數據庫類型。假設只支持mysql和oracle兩種數據庫,要添加其他數據庫,自己擴展:

public enum DBType {MYSQL, ORACLE }

2.創建用于sql轉換的接口IDialect.java。接口功能:可將普通查詢sql,轉換成與具體數據庫相關的分頁查詢sql,IDialect.java:

public interface IDialect {//支持根據字段進行排序查詢,留給讀者實現吧String getSortSQL(String sql, List<String> sortFields, List<String> sortOrders);//根據原始查詢sql生成與數據庫相關的查詢sql//原始sql就是上文提到的:select provId, description from areacodecfgString getLimitSQL(String sql, int offset, int limit);//根據原始的sql生成查詢總記錄數的sqlString getTotalSQL(String sql); }

3.定義IDialect的抽象實現ADialect.java:

?

public abstract class ADialect implements IDialect {@Overridepublic String getTotalSQL(String sql) {//基本所有數據庫通用StringBuffer totalSql = new StringBuffer(sql.length() + 100);totalSql.append("select count(0) from ( ").append(sql).append(" ) as _tmp_count");return totalSql.toString();} }

4.定義mysql的實現:

public class MySqlDialect extends ADialect {@Overridepublic String getLimitSQL(String sql, int offset, int limit) {sql = sql.trim();StringBuffer newSql = new StringBuffer(sql.length() + 100);newSql.append("select * from (").append(sql).append(") as _tmp_query limit ").append(offset).append(",").append(limit);return newSql.toString();} }

5.定義oracle的實現:

public class OracleDialect extends ADialect {@Overridepublic String getLimitSQL(String sql, int offset, int limit) {sql = sql.trim();StringBuffer pageSelect = new StringBuffer(sql.length() + 100);pageSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");pageSelect.append(sql);pageSelect.append(" ) row_ ) where rownum_ > ").append(offset).append(" and rownum_ <= ").append(offset + limit);return pageSelect.toString();} }

5.創建IDialect的實例化工廠RoutingDialect.java:

public class RoutingDialect implements IDialect {//實際委托給delegateprivate IDialect delegate;//用于緩存實例private static Map<DBType, IDialect> dialectMap = new HashMap<DBType, IDialect>();private RoutingDialect(DBType dbType) {switch (dbType) {case MYSQL:delegate = new MySqlDialect();break;case ORACLE:delegate = new OracleDialect();break;default:delegate = new MySqlDialect();}}//工廠方法,根據數據庫類型返回相應的Dialectpublic static IDialect getDialect(DBType dbType) {IDialect dialect = null;dialect = dialectMap.get(dbType);if (dialect == null) {synchronized (dialectMap) {dialect = dialectMap.get(dbType);if (dialect == null) {dialect = new RoutingDialect(dbType);dialectMap.put(dbType, dialect);}}}return dialect;}@Overridepublic String getLimitSQL(String sql, int offset, int limit) {return delegate.getLimitSQL(sql, offset, limit);}@Overridepublic String getTotalSQL(String sql) {return delegate.getTotalSQL(sql);}}

6.創建SmartRowBounds擴展自RowBounds,支持數據庫分頁,與總記錄數統計:

public class SmartRowBounds extends RowBounds {//內部參數:當前查詢記錄的偏移private int queryOffset = -1;//內部參數:當前查詢記錄的條數private int queryLimit = -1;//內部參數:用于保存總記錄數private int totalCount;//是否使用數據庫分頁,還是使用mybatis的默認滾動分頁private boolean isDbSupport = false;//內部標識,是否阻止默認分頁,當isDbSupport=true時此標識必須設成trueprivate boolean preventDefaultRowBounds = false;public SmartRowBounds() {//無分頁參數,不分頁super(RowBounds.NO_ROW_OFFSET, RowBounds.NO_ROW_LIMIT);}public SmartRowBounds(int queryOffset, int queryLimit) {//將分頁參數傳遞給父類,這一點很重要,可用于默認分頁super(queryOffset, queryLimit);this.queryOffset = queryOffset;this.queryLimit = queryLimit;}public SmartRowBounds(int queryOffset, int queryLimit,boolean isDbSupport) {this(queryOffset, queryLimit);this.isDbSupport = isDbSupport;}//根據數據庫類型,即原始的查詢sql來生成數據庫相關的分頁sql,//委托IDialect進行轉換public String getPageSql(DBType dbType, String rawSql) {IDialect dialet = RoutingDialect.getDialect(dbType);//只有使用數據庫分頁的情況下才會對sql進行數據庫相關的轉換if (isDbSupport && queryOffset >= 0 && queryLimit >= 0) {rawSql = dialet.getLimitSQL(rawSql, queryOffset, queryLimit);//如果使用了數據庫分頁,就必須阻止mybatis默認分頁行為。//設置一個阻止默認分頁的標識preventDefaultRowBounds=tuepreventDefaultRowBounds = true;}return rawSql;}public String getTotalSQL(DBType dbType, String rawSql) {return RoutingDialect.getDialect(dbType).getTotalSQL(rawSql);}public void setTotalCount(int totalCount) {this.totalCount = totalCount;}public int getTotalCount() {return totalCount;}@Overridepublic int getLimit() {//mysql會調用RowBounds的getLimit方法計算查詢記錄條數,//如果未使用數據庫分頁,你需要告訴mybatis查詢的記錄長度queryLimitif (!preventDefaultRowBounds) {return queryLimit;}//如果你使用了數據庫分頁,那么mybatis內部就不能再使用ResultSet滾動分頁了//因此需要返回super.NO_ROW_LIMIT,告知mybatis不進行分頁相關的結果集指針跳轉return super.NO_ROW_LIMIT;}@Overridepublic int getOffset() {//mysql會調用RowBounds的getOffset方法計算查詢偏移,if (!preventDefaultRowBounds) {return queryOffset;}//如果默認使用了數據庫分頁,那么mybatis內部就不能再使用ResultSet滾動分頁了//因此返回NO_ROW_OFFSETreturn super.NO_ROW_OFFSET;}public void reset() {totalCount = 0;preventDefaultRowBounds = false;}}

7.創建mybatis分頁攔截器類:

//注意注解,這里只對Connection創建的的preparedStatement進行攔截,固定寫法,可以不深究 @Intercepts( { @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) }) public class PageStatementInterceptor implements Interceptor {private DBType dbType;public void setDBType(String dbTypeStr) {if (dbTypeStr != null && !"".equals(dbTypeStr.trim())) {try {this.dbType = DBType.valueOf(dbTypeStr.trim().toUpperCase());} catch (Exception e) {this.dbType = DBType.MYSQL;}}}//mybatis框架自動調用@Overridepublic void setProperties(Properties properties) {setDBType(properties.getProperty("DBType"));}@Overridepublic Object intercept(Invocation invocation) throws Throwable {try {StatementHandler statementHandler = (StatementHandler) invocation.getTarget();StatementHandler deleStatementHandler = (StatementHandler) getFieldValue(statementHandler, "delegate");RowBounds rowBounds = (RowBounds) getFieldValue(deleStatementHandler, "rowBounds");MappedStatement mappedStatement = (MappedStatement)getFieldValue(deleStatementHandler, "mappedStatement");//只針對select類型切分頁參數是SmartRowBounds的查詢進行sql改造if (rowBounds != null && mappedStatement != null && SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType()) && SmartRowBounds.class.isAssignableFrom(rowBounds.getClass())) {SmartRowBounds pageHandler = (SmartRowBounds) rowBounds;//獲取原始sqlBoundSql boundSql = statementHandler.getBoundSql();if (boundSql != null) {//進行總記錄數查詢countTotal(pageHandler, (Connection) invocation.getArgs()[0], statementHandler.getParameterHandler(), boundSql.getSql());//將原始sql替換為支持分頁的sqlsetFieldValue(boundSql, "sql", pageHandler.getPageSql(dbType, boundSql.getSql()));}}} catch (Exception e) {e.printStackTrace();}return invocation.proceed();}private Field getField(Object target, String fieldName) {Field field = null;for (Class<?> clazz = target.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {try {field = clazz.getDeclaredField(fieldName);break;} catch (NoSuchFieldException e) {// ignore}}if (field != null) {if (!field.isAccessible()) {try {field.setAccessible(true);} catch (Exception e) {// ignore}}}return field;}private Object getFieldValue(Object target, String fieldName) {try {Field field = getField(target, fieldName);if (field != null) {return field.get(target);}} catch (Exception e) {e.printStackTrace();}return null;}private void setFieldValue(Object target, String fieldName, Object value) {try {Field field = getField(target, fieldName);if (field != null) {field.set(target, value);}} catch (Exception e) {e.printStackTrace();}}private void countTotal(SmartRowBounds pageHandler, Connection con, ParameterHandler paramHandler, String rawSql) {ResultSet rs = null;PreparedStatement stmt = null;try {String totalSql = pageHandler.getTotalSQL(dbType, rawSql);if (totalSql != null && !totalSql.isEmpty()) {stmt = con.prepareStatement(totalSql);paramHandler.setParameters(stmt);rs = stmt.executeQuery();if (rs.next()) {pageHandler.setTotalCount(rs.getInt(1));}}} catch (SQLException e) {e.printStackTrace();} finally {try {if (rs != null) {rs.close();}} catch (SQLException e1) {// ignore}try {if (stmt != null) {stmt.close();}} catch (Exception e) {// ignore}}}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}}

8.configuration.xml添加攔截器配置:

<plugins><plugin interceptor="PageStatementInterceptor"><property name="DBType" value="mysql" /></plugin></plugins>

9.測試:

public class Test {public static void main(String[] args) throws SQLException {Reader reader = null;SqlSessionFactory sf;SqlSession session = null;try {SqlSessionFactoryBuilder build = new SqlSessionFactoryBuilder();reader = Resources.getResourceAsReader("test/configuration.xml");sf = build.build(reader);session = sf.openSession();AreaCodeMapper mapper = session.getMapper(AreaCodeMapper.class);SmartRowBounds rowBounds = new SmartRowBounds(0, 10, true);//分頁記錄與記錄總數,使用一次接口調用搞定List<AreaCode> list = mapper.list(rowBounds);int total = rowBounds.getTotalCount();} catch (IOException e) {e.printStackTrace();} finally {if (reader != null) {try {reader.close();} catch (IOException e) {}}if (session != null) {session.close();}}} }

以上介紹了攔截器分頁的基本用法,下面簡單介紹下,如果配合struts, spring進行分頁查詢

1.創建業務類AreaCodeService.java,正常情況下應該創建業務接口再創建實現類,為了簡單起見,跳過接口創建:

@Service public class AreaCodeService {//通過spring進行自動注入@Resourceprivate AreaCodeMapper mapper;public List<AreaCode> listAreaCode(RowBounds rowBounds) {//方法1return mapper.list(rowBounds));}public List<AreaCode> listAreaCode(int offset, int limit) {//方法2//直接創建SmartRowBounds//但是總記錄數如何傳遞出去?return mapper.list(new SmartRowBounds(offset, limit));} }

AreaCodeService很簡單,基本就是委托mapper查詢,唯一需要做的就是實例化RowBounds交給mapper。

上面代碼定義了兩個用于分頁查詢的方法,方法1:通過傳入RowBounds類型參數,實現分頁控制,好處是簡單,實際的rowbounds對象的創建代碼放到調用者(一般是structs的控制器對象)中,缺點是AreaCodeService與mybatis耦合太緊,如果以后使用其他orm框架如hibernate,那就需要修改service類。方法2:方法獨立性比較好,不與任何特定框架耦合,它只關心查詢偏移,與返回記錄數,但問題是,方法的返回類型是List<AreaCode>, 如何將SmartRowBounds的總記錄數返回給調用者對象?先不說,后面再談!

2.創建Struts控制器的抽象Action, AbstractAction.java:

public class AbstractAction extends ActionSupport{public static final String JSON = "json";//querOffset與queryLimit是通過前臺jsp傳過來的分頁相關參數//其實可以將這兩個參數封裝成Page對象,通過Page對象來取queryOffset與queryLimit//這里這樣操作是為了偷懶,展示用protected int queryOffset = -1;protected int queryLimit = -1;//前臺頁表格加載需要的數據對象protected Object gridDataList;//將分頁的查詢的結果存放到gridDataList//最終通過struts.xml配置文件將gridDataList轉換為json傳到前臺jsp//list為查詢的結果, total為總記錄數protected void setJsonGrid(List list, int total) {Map data = new HashMap();data.put("total", total);data.put("rows", list);this.gridDataList = data;}public int getQueryOffset() {return queryOffset;}public void setQueryOffset(int queryOffset) {this.queryOffset = queryOffset;}public int getGridDataList() {return gridDataList;}public void setGridDataList(Object gridDataList) {this.gridDataList= gridDataList;}public int getQueryLimit() {return queryLimit;}public void setQueryLimit(int queryLimit) {this.queryLimit = queryLimit;} }

抽象action繼承自Struts的ActionSupport,方法功能看注釋,不廢話了。

2.創建具體控制器AreaCodeAction.java:

public class AreaCodeAction extends AbstractAction{//通過spring將AreaCodeService注入進來@Resourceprivate AreaCodeService service;public String gridList() {SmartRowBounds rb = new SmartRowBounds(queryOffset, queryLimit, true);List<AreaCode> list = service.listAreaCode(rb);//使用AreaCodeService的方法1setJsonGrid(list, rb.getTotalCount());return JSON;} }

只有一個gridList ()方法,通過前臺頁面傳進來的分頁參數創建SmartRowBounds交給service進行分頁查詢,查詢完了會通過rb回取總記錄數,一起交給父類的setJsonGrid()方法。

上文提到的,將控制器屬性gridDataList轉化為json對象傳到前臺jsp,需要在struct.xml中加一段配置,內容片段如下:

<global-results><result name="json" type="json"><param name="root">gridDataList</param></result> </global-results>

spring中mybatis攔截器配置片段:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="xx" /><property name="plugins"><array><!--攔截器類全稱--><bean class="PageStatementInterceptor"></bean></array></property> </bean>

自此所有內容已敘完,最后繼續探討下前面的遺留問題,AreaCodeService的方法2,

如何將查詢總數返回到控制器?

其實至少有兩個方法:

1.List<AreaCode> listAreaCode(int offset, int limit)再增加第三個參數,然后將SmartRowBounds的記錄總數放到第三個參數中,供控制器取,以下是模擬實現:

public List<AreaCode> listAreaCode(int offset, int limit, ResultHook hook) {//方法2SmartRowBounds rb = new SmartRowBounds(offset, limit);try {return mapper.list(new SmartRowBounds(offset, limit,true));} catch (Exception e) {e.printStackTrace();} finally {//將記錄總數傳給hook,通知到控制器去取hook.setResult(rb.getTotalCount());}} }

?2.借助線程本地上下文ThreadLocal,同一個線程總能共享相同的數據對象,創建RowBoundsHolder對象:

public class RowBoundsHolder {private static ThreadLocal<SmartRowBounds> rowBoundHolder = new ThreadLocal<SmartRowBounds>();public static SmartRowBoundsinstance(int offset, int limit) {SmartRowBounds rb = new SmartRowBounds(offset, limit);rowBoundHolder.set(rb);return rb;}public static SmartRowBounds instance(int offset, int limit, boolean supportDbPage) {SmartRowBounds rb = new SmartRowBounds(offset, limit, supportDbPage);rowBoundHolder.set(rb);return rb;}public static SmartRowBoundsget() {return rowBoundHolder.get();} }

?RowBoundsHolder的instance方法創建一個SmartRowBounds rb并設置到線程本地山下文中,在同一個線程中,調用任意對象的任意方法,總能訪問此rb對象的引用。

改造AreaCodeService的方法2:

@Service public class AreaCodeService {//通過spring進行自動注入@Resourceprivate AreaCodeMapper mapper;public List<AreaCode> listAreaCode(int offset, int limit) {//方法2return mapper.list(RowBoundsHolder.instance(offset, limit, true));} }

?改造AbstractAction添加新方法:

protected void setJsonGrid(List list) {int total = list == null ? 0 : list.size();SmartRowBounds rb = RowBoundsHolder.get();if (rb != null) {//通線程上下文獲取總記錄數total = rb.getTotalCount();}Map data = new HashMap();data.put("total", total);data.put("rows", list);this.gridDataList = data;}

修改AreaCodeAction.java:

public class AreaCodeAction extends AbstractAction{//通過spring將AreaCodeService注入進來@Resourceprivate AreaCodeService service;public String gridList() {List<AreaCode> list = service.listAreaCode(queryOffset, queryLimit);//使用AreaCodeService的方法2setJsonGrid(list);return JSON;} }

?All right,如果你覺得有“點”幫助,請點個“贊”哦,3q。

以上實現僅供參考,思路各異,滿意就行。

原創博文,轉載請注明出處。

總結

以上是生活随笔為你收集整理的一步步教你mybatis分页,mybatis分页拦截器 使用,mybatis拦截器分页的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。