支援連表等複雜查詢的PageHelper修改 —— 支援PageHelper5.1.4 Mybatis3.4.6
一、PageHelper中分頁存在的問題:
- 主表結構如下:
provider_order表,訂單主表。我們這裡簡化處理,它只有一個欄位,即id。
VARCHAR | po_id | 訂單表id
- 從表結構如下:
order_info表,訂單從表。它負責記錄訂單的一些資訊,與主表是一對多的關係。
VARCHAR | oi_id | 訂單資訊id
VARCHAR | po_id | 訂單表id
TEXT | info | 資訊詳情
- 現在我們需要一個簡單的連表查詢操作,sql語句如下:
<select id="getProviderOrderWithOrderInfoTest" resultMap="ResultMapWithOrderInfo" parameterType="java.lang.String">
SELECT *
FROM (SELECT * FROM provider_order WHERE po_id LIKE #{po_id}) AS limitable
LEFT JOIN order_info ON limitable.po_id = order_info.provider_order_id
</select >
- 進行PageHelper的分頁操作,查詢前十條記錄:
Page page = PageHelper.startPage(0, 10);
List<ProviderOrder> providerOrderList = testService.getProviderOrderWithOrderInfoTest("%-%");
PageInfo<ProviderOrder> pageInfo = new PageInfo<>(providerOrderList);
System.out.println(pageInfo);
- 目前我們的主表有兩條記錄,從表有二十條記錄。
我們會發現,查詢出來的總數是20,然後查詢結果是兩個list(各5條從表資訊),不僅如此,分頁資料也很奇怪,我們只有兩條主表記錄,但卻被分成了兩頁(pages = 2),並且hasNextPage = true。這並不是我們要的結果。我們想要查詢的效果是:
前十條 訂單 記錄,然後連表查詢出該十條訂單記錄下的所有 訂單資訊
也就是說,結果集中,首先我們應該得到兩個list,list中各10條從表資訊,而且也不該有下一頁,總頁數應為1,總資料數應為2。
檢視PageHelper的原始碼我們來看看,它的count,以及limit,到底是怎麼回事。
二、 PageHelper的count實現
private Long executeAutoCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql, RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException,
SQLException {
Map<String, Object> additionalParameters = (Map) this.additionalParametersField.get(boundSql);
CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
String countSql = this.dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);
BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter);
Iterator var11 = additionalParameters.keySet()
.iterator();
while (var11.hasNext()) {
String key = (String) var11.next();
countBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
Long count = (Long) ((List) countResultList).get(0);
// 獲取count
return count;
}
我們很容易看出來,count的sql語句,在這裡經過了處理,這裡呼叫了dialect的getCountSql方法。
它的實現如下:
public String getSmartCountSql(String sql, String name) {
Statement stmt = null;
if (sql.indexOf("/*keep orderby*/") >= 0) {
return this.getSimpleCountSql(sql);
} else {
try {
stmt = CCJSqlParserUtil.parse(sql);
} catch (Throwable var8) {
return this.getSimpleCountSql(sql);
}
Select select = (Select)stmt;
SelectBody selectBody = select.getSelectBody();
try {
this.processSelectBody(selectBody);
} catch (Exception var7) {
return this.getSimpleCountSql(sql);
}
this.processWithItemsList(select.getWithItemsList());
this.sqlToCount(select, name);
String result = select.toString();
return result;
}
}
public String getSimpleCountSql(String sql) {
return this.getSimpleCountSql(sql, "0");
}
public String getSimpleCountSql(String sql, String name) {
StringBuilder stringBuilder = new StringBuilder(sql.length() + 40);
stringBuilder.append("select count(");
stringBuilder.append(name);
stringBuilder.append(") from (");
stringBuilder.append(sql);
stringBuilder.append(") tmp_count");
return stringBuilder.toString();
}
簡單來說,就是在sql外面套一層select count語句,我們的sql語句變成了 SELECT count(0) FROM (我們的sql語句)(真實情況沒有這麼簡單,這只是其中一種情況。)這樣就很容易得知,為什麼最後的total是20(所有資料數),而不是2(主表資料數)了。
三、 PageHelper的分頁實現
與上面相同,這裡以dialect(為了適配各種資料庫)為mysql為例,我們可以看到,如果你沒有在sql語句裡面寫limit,就會在sql語句的最後面,新增limit語句。
public String getPageSql(String sql, RowBounds rowBounds, CacheKey pageKey) {
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
sqlBuilder.append(sql);
if (rowBounds.getOffset() == 0) {
sqlBuilder.append(" LIMIT ");
sqlBuilder.append(rowBounds.getLimit());
} else {
sqlBuilder.append(" LIMIT ");
sqlBuilder.append(rowBounds.getOffset());
sqlBuilder.append(",");
sqlBuilder.append(rowBounds.getLimit());
pageKey.update(rowBounds.getOffset());
}
pageKey.update(rowBounds.getLimit());
return sqlBuilder.toString();
}
程式執行得到sql語句如下,PageHelper對查詢的總結果集(包括主表資料與從表資料),進行了分頁
在得知PageHelper在這兩個步驟的原理後,我們開始對PageHelper進行改造。
四、 count結果修正
在上面的count原始碼中,我們可以看到這麼一段程式碼:
public String getSmartCountSql(String sql, String name) {
Statement stmt = null;
if (sql.indexOf("/*keep orderby*/") >= 0) {
return this.getSimpleCountSql(sql);
} else {
try {
stmt = CCJSqlParserUtil.parse(sql);
} catch (Throwable var8) {
return this.getSimpleCountSql(sql);
}
Select select = (Select)stmt;
SelectBody selectBody = select.getSelectBody();
try {
this.processSelectBody(selectBody);
} catch (Exception var7) {
return this.getSimpleCountSql(sql);
}
this.processWithItemsList(select.getWithItemsList());
this.sqlToCount(select, name);
String result = select.toString();
return result;
}
}
public void sqlToCount(Select select, String name) {
SelectBody selectBody = select.getSelectBody();
List<SelectItem> COUNT_ITEM = new ArrayList();
COUNT_ITEM.add(new SelectExpressionItem(new Column("count(" + name + ")")));
if (selectBody instanceof PlainSelect && this.isSimpleCount((PlainSelect)selectBody)) {
((PlainSelect)selectBody).setSelectItems(COUNT_ITEM);
} else {
PlainSelect plainSelect = new PlainSelect();
SubSelect subSelect = new SubSelect();
subSelect.setSelectBody(selectBody);
subSelect.setAlias(TABLE_ALIAS);
plainSelect.setFromItem(subSelect);
plainSelect.setSelectItems(COUNT_ITEM);
select.setSelectBody(plainSelect);
}
}
實際上PageHelper是有提供給我們一套指定colum查詢的入口的。我們將查詢程式碼做如下改造:
Page page = PageHelper.startPage(0, 10);
page.setCountColumn("DISTINCT po_id");
List<ProviderOrder> providerOrderList = testService.getProviderOrderWithOrderInfoTest("%-%");
PageInfo<ProviderOrder> pageInfo = new PageInfo<>(providerOrderList);
System.out.println(pageInfo);
執行查詢,得到結果如下:
我們會發現,我們需要的分頁資料正常了,總頁碼(pages:1),total:2。
五、 查詢結果集修正
我們在三中,發現了PageHelper limit新增的位置不正確,我們需要將其放在正確的位置下。所以新建一個類如下:
/**
* Created by Anur IjuoKaruKas on 2018/6/8
*/
public class PageSqlResolver {
public static final String SQL_SIGN = "AS limitable";
public static final String LIMIT_SIGN = "LIMIT ?";
public static final String LIMIT_SIGN_EX = "LIMIT ?, ?";
public static String resolveLimit(String pageSql) {
if (pageSql == null) {
return null;
}
if (pageSql.contains(SQL_SIGN)) {// 如果需要特殊分頁
String changer = "";
if (pageSql.contains(LIMIT_SIGN_EX)) {
changer = LIMIT_SIGN_EX;
} else if (pageSql.contains(LIMIT_SIGN)) {
changer = LIMIT_SIGN;
}
pageSql = pageSql.replace(changer, "");
StringBuilder sqlBuilder = new StringBuilder(pageSql);
StringBuilder mae = new StringBuilder(sqlBuilder.substring(0, sqlBuilder.indexOf(SQL_SIGN)));// mae 截止sql語句到 limitable
StringBuilder uShiRo = new StringBuilder(sqlBuilder.substring(sqlBuilder.indexOf(SQL_SIGN), sqlBuilder.length()));// 剩餘的
mae.insert(mae.lastIndexOf(")"), String.format(" %s", changer));
return mae.append(uShiRo)
.toString();
} else {
return pageSql;
}
}
}
這段程式碼的含義是:將AS limitable,也就是主表,標記為真正分頁的地方,並向其添如limit ? ,?
SELECT *
FROM (SELECT * FROM provider_order WHERE po_id LIKE ?) AS limitable
LEFT JOIN order_info ON limitable.po_id = order_info.provider_order_id LIMIT ?
sql語句從上面變成了:
SELECT *
FROM (SELECT * FROM provider_order WHERE po_id LIKE ? LIMIT ?) AS limitable
LEFT JOIN order_info ON limitable.po_id = order_info.provider_order_id
執行程式,得到結果:
我們發現這就是我們想要的結果,它的分頁是針對主表進行的,而不是針對結果集。
解析到這裡就結束了
六、 改造步驟詳解
1、引入依賴
這裡使用了mybatis 3.4.6(tk.mybatis 4.0.2),pagehelper5.1.4(pagehelper-spring-boot-starter1.2.5),你可以引入原依賴,也可以用這裡使用的tkMybatis,以及pagehelper-spring-boot-starter。
<?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.anur</groupId>
<artifactId>page-helper</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>page-helper</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- page - helper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- codeGen -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.6</version>
</dependency>
<!-- tk - mybatis -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.0.2</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<proc>none</proc>
</configuration>
</plugin>
</plugins>
</build>
</project>
2、新增兩個類,用於修正查詢時sql語句
package com.anur.pagehelper.page;
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.github.pagehelper.Dialect;
import com.github.pagehelper.PageException;
import com.github.pagehelper.cache.Cache;
import com.github.pagehelper.cache.CacheFactory;
import com.github.pagehelper.util.MSUtils;
import com.github.pagehelper.util.StringUtil;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
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.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
/**
* Created by Anur IjuoKaruKas on 2018/6/8
*/
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {
MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class
}
),
@Signature(
type = Executor.class,
method = "query",
args = {
MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class,
CacheKey.class,
BoundSql.class
}
)
})
public class CustomPageInterceptor implements Interceptor {
protected Cache<String, MappedStatement> msCountMap = null;
private Dialect dialect;
private String default_dialect_class = "com.github.pagehelper.PageHelper";
private Field additionalParametersField;
private String countSuffix = "_COUNT";
public CustomPageInterceptor() {
}
/**
* 攔截
*/
public Object intercept(Invocation invocation) throws Throwable {
try {
Object[] args = invocation.getArgs();
// 對應一個Mapper節點,描述一條sql語句
MappedStatement ms = (MappedStatement) args[0];
// 引數
Object parameter = args[1];
// mybatis自帶分頁
RowBounds rowBounds = (RowBounds) args[2];
// 結果集處理器
ResultHandler resultHandler = (ResultHandler) args[3];
// 執行器
Executor executor = (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
if (args.length == 4) {
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
List resultList;
if (this.dialect.skip(ms, parameter, rowBounds)) {
resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
} else {
String msId = ms.getId();
Configuration configuration = ms.getConfiguration();
Map<String, Object> additionalParameters = (Map) this.additionalParametersField.get(boundSql);
if (this.dialect.beforeCount(ms, parameter, rowBounds)) {
String countMsId = msId + this.countSuffix;
MappedStatement countMs = this.getExistedMappedStatement(configuration, countMsId);
Long count;
if (countMs != null) {
count = this.executeManualCount(executor, countMs, parameter, boundSql, resultHandler);
} else {
countMs = (MappedStatement) this.msCountMap.get(countMsId);
if (countMs == null) {
countMs = MSUtils.newCountMappedStatement(ms, countMsId);
this.msCountMap.put(countMsId, countMs);
}
// 這裡是count,可能要改
// boundSql = PageSqlResolver.resolveCount(configuration, boundSql, parameter);
count = this.executeAutoCount(executor, countMs, parameter, boundSql, rowBounds, resultHandler);
}
if (!this.dialect.afterCount(count, parameter, rowBounds)) {
Object var24 = this.dialect.afterPage(new ArrayList(), parameter, rowBounds);
return var24;
}
}
if (!this.dialect.beforePage(ms, parameter, rowBounds)) {
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
} else {
parameter = this.dialect.processParameterObject(ms, parameter, boundSql, cacheKey);
String pageSql = this.dialect.getPageSql(ms, boundSql, parameter, rowBounds, cacheKey);
// 對sql進行改造
pageSql = PageSqlResolver.resolveLimit(pageSql);
BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter);
Iterator var17 = additionalParameters.keySet()
.iterator();
while (true) {
if (!var17.hasNext()) {
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, pageBoundSql);
break;
}
String key = (String) var17.next();
pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
}
}
Object var22 = this.dialect.afterPage(resultList, parameter, rowBounds);
return var22;
} finally {
this.dialect.afterAll();
}
}
private Long executeManualCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql, ResultHandler resultHandler) throws IllegalAccessException, SQLException {
CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
BoundSql countBoundSql = countMs.getBoundSql(parameter);
Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
Long count = ((Number) ((List) countResultList).get(0)).longValue();
return count;
}
private Long executeAutoCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql, RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException,
SQLException {
Map<String, Object> additionalParameters = (Map) this.additionalParametersField.get(boundSql);
CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
String countSql = this.dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);
BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter);
Iterator var11 = additionalParameters.keySet()
.iterator();
while (var11.hasNext()) {
String key = (String) var11.next();
countBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
Long count = (Long) ((List) countResultList).get(0);
// 獲取count
return count;
}
private MappedStatement getExistedMappedStatement(Configuration configuration, String msId) {
MappedStatement mappedStatement = null;
try {
mappedStatement = configuration.getMappedStatement(msId, false);
} catch (Throwable var5) {
;
}
return mappedStatement;
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
this.msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties);
String dialectClass = properties.getProperty("dialect");
if (StringUtil.isEmpty(dialectClass)) {
dialectClass = this.default_dialect_class;
}
try {
Class<?> aClass = Class.forName(dialectClass);
this.dialect = (Dialect) aClass.newInstance();
} catch (Exception var6) {
throw new PageException(var6);
}
this.dialect.setProperties(properties);
String countSuffix = properties.getProperty("countSuffix");
if (StringUtil.isNotEmpty(countSuffix)) {
this.countSuffix = countSuffix;
}
try {
this.additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters");
this.additionalParametersField.setAccessible(true);
} catch (NoSuchFieldException var5) {
throw new PageException(var5);
}
}
}
package com.anur.pagehelper.page;
/**
* Created by Anur IjuoKaruKas on 2018/6/8
*/
public class PageSqlResolver {
public static final String SQL_SIGN = "AS limitable";
public static final String LIMIT_SIGN = "LIMIT ?";
public static final String LIMIT_SIGN_EX = "LIMIT ?, ?";
public static String resolveLimit(String pageSql) {
if (pageSql == null) {
return null;
}
if (pageSql.contains(SQL_SIGN)) {// 如果需要特殊分頁
String changer = "";
if (pageSql.contains(LIMIT_SIGN_EX)) {
changer = LIMIT_SIGN_EX;
} else if (pageSql.contains(LIMIT_SIGN)) {
changer = LIMIT_SIGN;
}
pageSql = pageSql.replace(changer, "");
StringBuilder sqlBuilder = new StringBuilder(pageSql);
StringBuilder mae = new StringBuilder(sqlBuilder.substring(0, sqlBuilder.indexOf(SQL_SIGN)));// mae 截止sql語句到 limitable
StringBuilder uShiRo = new StringBuilder(sqlBuilder.substring(sqlBuilder.indexOf(SQL_SIGN), sqlBuilder.length()));// 剩餘的
mae.insert(mae.lastIndexOf(")"), String.format(" %s", changer));
return mae.append(uShiRo)
.toString();
} else {
return pageSql;
}
}
}
3、新增配置檔案如下
注意,這裡配置了我們的CustomPageInterceptor(2步驟裡的那個類)作為外掛,如果不進行此處配置,我們的修改是不起作用的。
/**
* Created by Anur IjuoKaruKas on 2017/12/13.
*/
@Configuration
public class MybatisConfiguration {
@Bean
public SqlSessionFactory sqlSessionFactoryBean(@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setTypeAliasesPackage(Constant.MODEL_PACKAGE);
// 配置分頁外掛,詳情請查閱官方文件
PageHelperProperties properties = new PageHelperProperties();
properties.setPageSizeZero("true");// 分頁尺寸為0時查詢所有紀錄不再執行分頁
properties.setReasonable("true");// 頁碼<=0 查詢第一頁,頁碼>=總頁數查詢最後一頁
properties.setSupportMethodsArguments("true");// 支援通過 Mapper 介面引數來傳遞分頁引數
CustomPageInterceptor pageInterceptor = new CustomPageInterceptor();
pageInterceptor.setProperties(properties.getProperties());
// 新增外掛
factory.setPlugins(new Interceptor[]{pageInterceptor});
// 新增XML目錄
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
factory.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
return factory.getObject();
}
}
4、sql語句注意事項
在需要被查詢的主表上,你需要手動包裝一層select,並將其別名設定為limitable。AS必須大寫。
SELECT *
FROM (SELECT *
FROM provider_order) AS limitable
LEFT JOIN order_info ON limitable.po_id = order_info.provider_order_id
這是原語句:
SELECT *
FROM provider_order limitable
LEFT JOIN order_info ON limitable.po_id = order_info.provider_order_id
5、查詢時注意事項
在查詢之前,我們需要將主表的主鍵(必須唯一)設定到countColumn中。
Page page = PageHelper.startPage(0, 10);
page.setCountColumn("DISTINCT po_id");
List<ProviderOrder> providerOrderList = testService.getProviderOrderWithOrderInfoTest("%-%");
如果已經確保完成了上面的五個步驟,執行查詢即可,我們將得到正確的連表查詢結果。
這個改造並不能完全適應所有的複雜查詢,不過它提供了一種思路,你可以按照自己的實際情況做更好的調整!
相關推薦
支援連表等複雜查詢的PageHelper修改
新版本的PageHelper與mybatis請移步這裡——支援PageHelper5.1.4 Mybatis3.4.6 先說說使用PageHelper踩過的坑: - 在mapper對映檔案中,如果使用了limit關鍵字,而又使用了PageHelper的
支援連表等複雜查詢的PageHelper修改 —— 支援PageHelper5.1.4 Mybatis3.4.6
一、PageHelper中分頁存在的問題: - 主表結構如下: provider_order表,訂單主表。我們這裡簡化處理,它只有一個欄位,即id。 VARCHAR | po_id | 訂單表id - 從表結
(ORACLE)PL/SQL 表的複雜查詢
表的複雜查詢 在實際應用中,常常需要執行復雜的資料統計,經常需要顯示多張表的資料現在我們來學習比較複雜的select的語句。我們將繼續使用scott使用者下emp表作為示例。 聚合函式 MAX函式: 對一列取最大值 MIN函式: 對一列取最小值 AVG函式: 對一列取平均值 SU
MySQL:記錄的增刪改查、單表查詢、約束條件、多表查詢、連表、子查詢、pymysql模組、MySQL內建功能
資料操作 插入資料(記錄): 用insert; 補充:插入查詢結果: insert into 表名(欄位1,欄位2,...欄位n) select (欄位1,欄位2,...欄位n) where ...; 更新資料update 語法: update 表名 set 欄位1=
表的複雜查詢----多表查詢、兩表的條件連線、內連線(自連線)、左外連線、右外連線、子查詢、分頁查詢
基於兩個或兩個以上的表或檢視的查詢。例如:emp和dept是兩張表。(這兩張表的deptno是共同欄位) ①通過什麼條件把兩張表關聯(如果不使用條件關聯將產生行數乘積的合集---笛卡爾集)例子:emp表字段包含:每行有自己的名字和id以及自己的上級的id,同時自己的上級
MySQL複雜查詢 —— 跨表/多表查詢,如何解決MySQL不支援全連線的問題 —— 使用結果集的合併
SELECT sex, MAX(salary), MIN(salary), AVG(salary) FROM emp GROUP BY sex; 5.MySQL複雜查詢 —— 跨表/多表查詢 —— 瞭解 查詢結果集中的資料來自於多個不同的
不同服務器數據庫表連接查詢,修改
server dlink 數據庫 exe bsp 連接查詢 sql 對數 als 不同服務器數據庫表連接查詢,修改 exec sp_addlinkedserver ‘ERP‘,‘‘,‘SQLOLEDB‘,‘10.0.10.0‘ exec sp_addlinkedsrvl
介紹 複雜查詢,包括多表關聯,分頁,排序等
本篇進行Spring-data-jpa的介紹,幾乎涵蓋該框架的所有方面,在日常的開發當中,基本上能滿足所有需求。這裡不講解JPA和Spring-data-jpa單獨使用,所有的內容都是在和Spring整合的環境中實現。如果需要了解該框架的入門,百度一下,很多入門的介紹。在這篇文章的接下來一篇,會有一個
hive表信息查詢:查看表結構、表操作等--轉
cal ica class depend yun sel getting 結構 dconf 原文地址:http://www.aboutyun.com/forum.PHP?mod=viewthread&tid=8590&highlight=Hive 問題導讀
連表查詢(3)之根據相關條件,查詢另外一個表或多個表的某字段
兩個 mode object 添加 就會 resultmap control var 多個 1、送檢單位 代理商是從代理商表中取得數據 銷售人員從銷售表中取得數據 說一個簡單的思路: sql語句找到連接兩個表的條件,把另外一個表要用到的字段 寫一個 <re
Java鏈接HBASE數據庫,創建一個表,刪除一張表,修改表,輸出插入,修改,數據刪除,數據獲取,顯示表信息,過濾查詢,分頁查詢,地理hash
can charat nfa true 目錄結構 dfa byte sin extra 準備工作 1、創建Java的Maven項目 創建好的目錄結構如下: 另外註意junit的版本,最好不要太高,最開始筆者使用的junit4.12的,發現運行的時候會報錯。最後把Junit
連表查詢的簡單方法!!
etl point return rim plist turn public 方法 nbsp 在主表(Appointment)裏面private+副表的對象 private Department(副表) depa; private User(副表) u; 然後source
安裝vsphere client時出現“錯誤2229 無法在SQL查詢中加載表”等錯誤
錯誤2229解決辦法:請按照以下步驟繼續進行升級:找到日誌文件 %TEMP%vim-sso-msi.log搜索上次安裝期間作為緩存文件的 *.mst 文件。例如: c:\Windows \Installer\xxxxx.mst找到該 *.mst 文件並將其刪除。重新安裝vsphere client即可本文出自
Linq to SQL 的連表查詢(轉)
equal query 交集 數據庫 調用 數據 變量 bst log 關於數據庫的查詢中經常需要用到多表的連接查詢,這裏就簡單地展示關於linq的查詢功能。 1、單表的查詢 [csharp] view plain copy var query = from
表連接和分組查詢
兩張 -- 多表連接查詢 數據行 產生 記錄 group bsp log 分組查詢:分組查詢就是按某一列分組,相同的列合並,得到結果可能他少於總記錄 使用group by分組查詢:按什麽分(年級、姓氏、地址、年齡)年級 分組查詢語法:Select * from <表名
SQL總結 連表查詢
表示 自然連接 簡單 put delete title null ros insert 連接查詢包括合並、內連接、外連接和交叉連接,如果涉及多表查詢,了解這些連接的特點很重要。 只有真正了解它們之間的區別,才能正確使用。 1、Union UNION 操作符用於合並兩個或
Sql連表查詢
teacher rom SQ 學生 全外連接 連接 教師表 tom 重點 1.Union:使用union是組合兩張表,消去表中重復行,兩張表查詢的結果有相同數量的列、列類型相似;UNION ALL,不消除重復行教師表: ID Name 101 Mrs Lee
在Mybatis中使用連表查詢的一次實際應用
多表關聯 del 應用 行記錄 全部 業務 val 一定的 att 以前在工作中很少使用多表關聯查詢,對連表查詢的具體作用和使用場景也沒有很直觀的認識,通過這次在項目中的實際應用,對此有了一定的認識,特記錄如下。 關聯表介紹: 分別是屬性表attr_info、屬性值表a
連表查詢都用Left Join吧
簡單 不知道 效率 行記錄 我們 主體 left 註意 說明 最近看同事的代碼,SQL連表查詢的時候很多時候用的是Inner Join,而我覺得對我們的業務而言,99.9%都應該使用Left Join(還有0.1%我不知道在哪),我用最簡單的方式來描述這兩者的區別,直接看圖
連表查詢
regex 另一個 employee 數據 tab png ima false 通配符 1.內鏈接查詢 1.查詢department表和employee表,d_id字段相等的數據 2.外連接查詢 1.左連接查詢:可以查詢出表1的所指的所有記錄,表2只能查詢出匹配的記錄