All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alilitech.mybatis.jpa.pagination.PaginationInterceptor Maven / Gradle / Ivy

The newest version!
/*
 *    Copyright 2017-2022 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package com.alilitech.mybatis.jpa.pagination;

import com.alilitech.mybatis.MybatisJpaProperties;
import com.alilitech.mybatis.dialect.SqlDialectFactory;
import com.alilitech.mybatis.jpa.EntityMetaDataRegistry;
import com.alilitech.mybatis.jpa.StatementRegistry;
import com.alilitech.mybatis.jpa.definition.MethodDefinition;
import com.alilitech.mybatis.jpa.meta.ColumnMetaData;
import com.alilitech.mybatis.jpa.meta.EntityMetaData;
import com.alilitech.mybatis.jpa.pagination.sqlparser.ParseResult;
import com.alilitech.mybatis.jpa.pagination.sqlparser.SqlParser;
import com.alilitech.mybatis.jpa.pagination.sqlparser.SqlSelectBody;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.*;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;


/**
 * Pagination Plugin
 *
 * @author Zhou Xiaoxiang
 * @since 1.0
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PaginationInterceptor implements Interceptor {

    private final Log log = LogFactory.getLog(PaginationInterceptor.class);

    private MybatisJpaProperties mybatisJpaProperties;

    public PaginationInterceptor(MybatisJpaProperties mybatisJpaProperties) {
        this.mybatisJpaProperties = mybatisJpaProperties;
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);

        // determine whether it is a SELECT operation
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return invocation.proceed();
        }

        // if it is not the RowBounds parameter then skip it
        RowBounds rowBounds = (RowBounds) metaObject.getValue("delegate.rowBounds");
        if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
            return invocation.proceed();
        }

        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        String originalSql = boundSql.getSql();

        if (rowBounds instanceof Pagination) {
            Pagination page = (Pagination) rowBounds;

            // determine whether the paging dialect is actively set or auto set by config
            // add since v1.2.7
            Connection connection = (Connection) invocation.getArgs()[0];
            SqlDialectFactory sqlDialectFactory;
            if (page.getDatabaseType() == null) {
                // add since v1.2.8 use autoDialect
                if (this.mybatisJpaProperties.getPage().isAutoDialect()) {
                    String databaseProductName = connection.getMetaData().getDatabaseProductName();
                    sqlDialectFactory = new SqlDialectFactory(databaseProductName);

                    if (sqlDialectFactory.getDatabaseType() == null) {
                        log.warn("The databaseId of current connection used auto dialect do not has databaseType in com.alilitech.mybatis.jpa.DatabaseTypeRegistry, it will use mybatis configuration's databaseId!");
                        // get the configuration and instantiate a SqlDialectFactory
                        Configuration configuration = (Configuration) metaObject.getValue("delegate.configuration");
                        sqlDialectFactory = new SqlDialectFactory(configuration.getDatabaseId());
                    }

                } else {
                    // get the configuration and instantiate a SqlDialectFactory
                    Configuration configuration = (Configuration) metaObject.getValue("delegate.configuration");
                    sqlDialectFactory = new SqlDialectFactory(configuration.getDatabaseId());
                }
            } else {
                sqlDialectFactory = new SqlDialectFactory(page.getDatabaseType());
            }
            originalSql = generateToPageSql(mappedStatement.getId(), originalSql, sqlDialectFactory, page);
        }
        // 替换成分页sql
        metaObject.setValue("delegate.boundSql.sql", originalSql);
        // 取消逻辑分页
        metaObject.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
        metaObject.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
        return invocation.proceed();
    }

    /**
     * 生成需要跟分页参数拼接的sql
     */
    private String generateToPageSql(String statementId, String originalSql, SqlDialectFactory sqlDialectFactory, Pagination page) throws JSQLParserException {
        // custom sql
        if(!StatementRegistry.getInstance().contains(statementId)) {
            return sqlDialectFactory.buildPaginationSql(page, originalSql);
        }

        ParseResult parseResult = SqlParser.getInstance().parse(originalSql);

        // 如果没有关联查询,则直接拼接
        if(parseResult.isJoinEmpty()) {
            return sqlDialectFactory.buildPaginationSql(page, originalSql);
        }

        MethodDefinition methodDefinition = StatementRegistry.getInstance().getMethodDefinition(statementId);
        EntityMetaData entityMetaData = EntityMetaDataRegistry.getInstance().get(methodDefinition.getMapperDefinition().getGenericType().getDomainType());
        String mainTableAlias = entityMetaData.getTableAlias() + "_0";

        PlainSelect plainSelect = parseResult.getSelect();
        List selectItems = plainSelect.getSelectItems();

        // 基于原始的解析copy一个,也是
        PlainSelect plainSelectPage = new PlainSelect()
                .withSelectItems(plainSelect.getSelectItems())
                .withJoins(plainSelect.getJoins())
                .withOrderByElements(plainSelect.getOrderByElements())
                .withFromItem(plainSelect.getFromItem())
                .withWhere(plainSelect.getWhere());


        PlainSelect plainSelectFrom;
        // 只有主表时,直接不需要join,加条件和排序,再加分页作为子表
        if (parseResult.isOnlyMainTable()) {
            List mainTableItems = selectItems.stream()
                    .filter(selectItem -> Objects.equals(mainTableAlias, ((Column) ((SelectExpressionItem) selectItem).getExpression()).getTable().getName()))
                    .collect(Collectors.toList());
            plainSelectFrom = new PlainSelect()
                    .withSelectItems(mainTableItems)
                    .withFromItem(plainSelect.getFromItem())
                    .withWhere(plainSelect.getWhere())
                    .withOrderByElements(plainSelect.getOrderByElements());

        } else {
            // 非主表时分有没有子表的排序,如果没有
            // 则子查询是:查询主表字段,关联有条件的表,再加上条件,group by主表的主键,并limit
            // 最后外层再套一层查询,作为最终查询
            List mainTableItems = selectItems.stream()
                    .filter(selectItem -> Objects.equals(mainTableAlias, ((Column) ((SelectExpressionItem) selectItem).getExpression()).getTable().getName()))
                    .collect(Collectors.toList());
            // 准备join
            Set joins = new LinkedHashSet<>();
            parseResult.getJoinMap().forEach((key, value) -> {
                if (parseResult.getWhereTables().contains(key)) {
                    joins.add(value);
                }
                if (parseResult.getOrderTables().contains(key)) {
                    joins.add(value);
                }
            });

            plainSelectFrom = new PlainSelect()
                    .withSelectItems(mainTableItems)
                    .withFromItem(plainSelect.getFromItem())
                    .withWhere(plainSelect.getWhere())
                    .withOrderByElements(plainSelect.getOrderByElements())
                    .withJoins(new ArrayList<>(joins));

            // 准备group by column
            Set primaryKeys = entityMetaData.getPrimaryColumnMetaDatas().stream().map(ColumnMetaData::getColumnName).collect(Collectors.toSet());

            List primaryColumns = mainTableItems.stream()
                    .map(selectItem -> (Column) ((SelectExpressionItem) selectItem).getExpression())
                    .filter(column -> primaryKeys.contains(column.getColumnName()))
                    .collect(Collectors.toList());
            primaryColumns.forEach(plainSelectFrom::addGroupByColumnReference);

        }

        String paginationSql = sqlDialectFactory.buildPaginationSql(page, plainSelectFrom.toString());

        SqlSelectBody sqlSelectBody = new SqlSelectBody().withSql(paginationSql);

        SubSelect subSelect = new SubSelect().withSelectBody(sqlSelectBody).withAlias(new Alias(mainTableAlias, false));

        plainSelectPage.setFromItem(subSelect);
        // 外层查询不需要where与orderBy了
        plainSelectPage.setWhere(null);
        plainSelectPage.setOrderByElements(null);
        return new Select().withSelectBody(plainSelectPage).toString();

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy