diff --git a/mybatis-plus-core/build.gradle b/mybatis-plus-core/build.gradle index 601d5540d..70edf8209 100644 --- a/mybatis-plus-core/build.gradle +++ b/mybatis-plus-core/build.gradle @@ -1,6 +1,5 @@ dependencies { api project(":mybatis-plus-annotation") - api "${lib.'jsqlparser'}" api "${lib.mybatis}" implementation "${lib.cglib}" diff --git a/mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java b/mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java index 911e0aeca..7aefc9b64 100644 --- a/mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java +++ b/mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java @@ -47,9 +47,6 @@ class GeneratePomTest { Dependency mybatis = dependenciesMap.get("mybatis"); Assertions.assertEquals("compile", mybatis.getScope()); Assertions.assertFalse(mybatis.isOptional()); - Dependency jsqlParser = dependenciesMap.get("jsqlparser"); - Assertions.assertEquals("compile", jsqlParser.getScope()); - Assertions.assertFalse(jsqlParser.isOptional()); Dependency cglib = dependenciesMap.get("cglib"); Assertions.assertEquals("compile", cglib.getScope()); Assertions.assertTrue(cglib.isOptional()); diff --git a/mybatis-plus-extension/build.gradle b/mybatis-plus-extension/build.gradle index b30fcc274..062f3e157 100644 --- a/mybatis-plus-extension/build.gradle +++ b/mybatis-plus-extension/build.gradle @@ -17,9 +17,6 @@ dependencies { implementation "${lib['mybatis-thymeleaf']}" implementation "${lib.'mybatis-velocity'}" implementation "${lib.'mybatis-freemarker'}" - implementation "de.ruedigermoeller:fst:3.0.4-jdk17" - implementation "com.github.ben-manes.caffeine:caffeine:2.9.3" - testImplementation "io.github.classgraph:classgraph:4.8.176" testImplementation "${lib."spring-context-support"}" testImplementation "${lib.h2}" testImplementation "${lib.mysql}" diff --git a/mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/pagination/dialects/IDialectTest.java b/mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/pagination/dialects/IDialectTest.java index 84a84bcd4..488adc681 100644 --- a/mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/pagination/dialects/IDialectTest.java +++ b/mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/pagination/dialects/IDialectTest.java @@ -2,11 +2,11 @@ package com.baomidou.mybatisplus.test.extension.plugins.pagination.dialects; import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel; import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect; -import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.junit.platform.commons.util.ReflectionUtils; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -36,7 +36,9 @@ class IDialectTest { DialectModel model = o.buildPaginationSql("select * from table", 1, 10); String sql = model.getDialectSql(); if (!map.containsKey(sql)) { - map.put(sql, Lists.newArrayList(i)); + ArrayList> list = new ArrayList<>(); + list.add(i); + map.put(sql,list); } else { map.get(sql).add(i); } diff --git a/mybatis-plus-jsqlparser/build.gradle b/mybatis-plus-jsqlparser/build.gradle new file mode 100644 index 000000000..408657961 --- /dev/null +++ b/mybatis-plus-jsqlparser/build.gradle @@ -0,0 +1 @@ +tasks.matching {it.group == 'publishing' || it.group == 'central publish' }.each { it.enabled = false } diff --git a/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/build.gradle b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/build.gradle new file mode 100644 index 000000000..fe99ad8bf --- /dev/null +++ b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/build.gradle @@ -0,0 +1,14 @@ +dependencies { + api "com.github.jsqlparser:jsqlparser:4.9" + api project(":mybatis-plus-jsqlparser:mybatis-plus-jsqlparser-common") + implementation "${lib."slf4j-api"}" + implementation "de.ruedigermoeller:fst:3.0.3" + implementation "com.github.ben-manes.caffeine:caffeine:2.9.3" + testImplementation "io.github.classgraph:classgraph:4.8.176" + testImplementation "${lib."spring-context-support"}" + testImplementation "${lib.h2}" + testImplementation group: 'com.google.guava', name: 'guava', version: '33.3.1-jre' + +} + +compileJava.dependsOn(processResources) diff --git a/mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserFunction.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserFunction.java similarity index 100% rename from mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserFunction.java rename to mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserFunction.java diff --git a/mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserGlobal.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserGlobal.java similarity index 100% rename from mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserGlobal.java rename to mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserGlobal.java diff --git a/mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserSupport.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserSupport.java similarity index 100% rename from mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserSupport.java rename to mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserSupport.java diff --git a/mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/AbstractCaffeineJsqlParseCache.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/AbstractCaffeineJsqlParseCache.java similarity index 100% rename from mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/AbstractCaffeineJsqlParseCache.java rename to mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/AbstractCaffeineJsqlParseCache.java diff --git a/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstFactory.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstFactory.java new file mode 100644 index 000000000..9136a4f50 --- /dev/null +++ b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstFactory.java @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2011-2024, baomidou (jobob@qq.com). + * + * 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.baomidou.mybatisplus.extension.parser.cache; + +import org.nustaq.serialization.FSTConfiguration; + +/** + * Fst Factory + * + * @author miemie + * @since 2023-08-06 + */ +public class FstFactory { + private static final FstFactory FACTORY = new FstFactory(); + private final FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration(); + + public static FstFactory getDefaultFactory() { + return FACTORY; + } + + public FstFactory() { + conf.registerClass(net.sf.jsqlparser.expression.Alias.class); + conf.registerClass(net.sf.jsqlparser.expression.Alias.AliasColumn.class); + conf.registerClass(net.sf.jsqlparser.expression.AllValue.class); + conf.registerClass(net.sf.jsqlparser.expression.AnalyticExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.AnyComparisonExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.ArrayConstructor.class); + conf.registerClass(net.sf.jsqlparser.expression.ArrayExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.CaseExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.CastExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.CollateExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.ConnectByRootOperator.class); + conf.registerClass(net.sf.jsqlparser.expression.DateTimeLiteralExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.DateValue.class); + conf.registerClass(net.sf.jsqlparser.expression.DoubleValue.class); + conf.registerClass(net.sf.jsqlparser.expression.ExtractExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.FilterOverImpl.class); + conf.registerClass(net.sf.jsqlparser.expression.Function.class); + conf.registerClass(net.sf.jsqlparser.expression.HexValue.class); + conf.registerClass(net.sf.jsqlparser.expression.IntervalExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.JdbcNamedParameter.class); + conf.registerClass(net.sf.jsqlparser.expression.JdbcParameter.class); + conf.registerClass(net.sf.jsqlparser.expression.JsonAggregateFunction.class); + conf.registerClass(net.sf.jsqlparser.expression.JsonExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.JsonFunction.class); + conf.registerClass(net.sf.jsqlparser.expression.JsonFunctionExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.JsonKeyValuePair.class); + conf.registerClass(net.sf.jsqlparser.expression.KeepExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.LongValue.class); + conf.registerClass(net.sf.jsqlparser.expression.MySQLGroupConcat.class); + conf.registerClass(net.sf.jsqlparser.expression.MySQLIndexHint.class); + conf.registerClass(net.sf.jsqlparser.expression.NextValExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.NotExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.NullValue.class); + conf.registerClass(net.sf.jsqlparser.expression.NumericBind.class); + conf.registerClass(net.sf.jsqlparser.expression.OracleHierarchicalExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.OracleHint.class); + conf.registerClass(net.sf.jsqlparser.expression.OracleNamedFunctionParameter.class); + conf.registerClass(net.sf.jsqlparser.expression.OrderByClause.class); + conf.registerClass(net.sf.jsqlparser.expression.OverlapsCondition.class); + conf.registerClass(net.sf.jsqlparser.expression.Parenthesis.class); + conf.registerClass(net.sf.jsqlparser.expression.PartitionByClause.class); + conf.registerClass(net.sf.jsqlparser.expression.RangeExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.RowConstructor.class); + conf.registerClass(net.sf.jsqlparser.expression.RowGetExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.SQLServerHints.class); + conf.registerClass(net.sf.jsqlparser.expression.SignedExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.StringValue.class); + conf.registerClass(net.sf.jsqlparser.expression.TimeKeyExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.TimeValue.class); + conf.registerClass(net.sf.jsqlparser.expression.TimestampValue.class); + conf.registerClass(net.sf.jsqlparser.expression.TimezoneExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.TranscodingFunction.class); + conf.registerClass(net.sf.jsqlparser.expression.TrimFunction.class); + conf.registerClass(net.sf.jsqlparser.expression.UserVariable.class); + conf.registerClass(net.sf.jsqlparser.expression.VariableAssignment.class); + conf.registerClass(net.sf.jsqlparser.expression.WhenClause.class); + conf.registerClass(net.sf.jsqlparser.expression.WindowDefinition.class); + conf.registerClass(net.sf.jsqlparser.expression.WindowElement.class); + conf.registerClass(net.sf.jsqlparser.expression.WindowOffset.class); + conf.registerClass(net.sf.jsqlparser.expression.WindowRange.class); + conf.registerClass(net.sf.jsqlparser.expression.XMLSerializeExpr.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Addition.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Concat.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Division.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Modulo.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Multiplication.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Subtraction.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.AndExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.OrExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.XorExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Between.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ContainedBy.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Contains.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.DoubleAnd.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.EqualsTo.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ExistsExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ExpressionList.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.FullTextSearch.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GeometryDistance.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GreaterThan.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.InExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsNullExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.JsonOperator.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.LikeExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Matches.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MemberOfExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MinorThan.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MinorThanEquals.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.NamedExpressionList.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.NotEqualsTo.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.SimilarToExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.TSQLRightJoin.class); + conf.registerClass(net.sf.jsqlparser.parser.ASTNodeAccessImpl.class); + conf.registerClass(net.sf.jsqlparser.parser.Token.class); + conf.registerClass(net.sf.jsqlparser.schema.Column.class); + conf.registerClass(net.sf.jsqlparser.schema.Sequence.class); + conf.registerClass(net.sf.jsqlparser.schema.Synonym.class); + conf.registerClass(net.sf.jsqlparser.schema.Table.class); + conf.registerClass(net.sf.jsqlparser.statement.Block.class); + conf.registerClass(net.sf.jsqlparser.statement.Commit.class); + conf.registerClass(net.sf.jsqlparser.statement.DeclareStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.DeclareStatement.TypeDefExpr.class); + conf.registerClass(net.sf.jsqlparser.statement.DescribeStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.ExplainStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.ExplainStatement.Option.class); + conf.registerClass(net.sf.jsqlparser.statement.IfElseStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.OutputClause.class); + conf.registerClass(net.sf.jsqlparser.statement.PurgeStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.ReferentialAction.class); + conf.registerClass(net.sf.jsqlparser.statement.ResetStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.RollbackStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.SavepointStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.SetStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.ShowColumnsStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.ShowStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.Statements.class); + conf.registerClass(net.sf.jsqlparser.statement.UnsupportedStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.UseStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.alter.Alter.class); + conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.class); + conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDataType.class); + conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropDefault.class); + conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropNotNull.class); + conf.registerClass(net.sf.jsqlparser.statement.alter.AlterSession.class); + conf.registerClass(net.sf.jsqlparser.statement.alter.AlterSystemStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.alter.RenameTableStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.alter.sequence.AlterSequence.class); + conf.registerClass(net.sf.jsqlparser.statement.analyze.Analyze.class); + conf.registerClass(net.sf.jsqlparser.statement.comment.Comment.class); + conf.registerClass(net.sf.jsqlparser.statement.create.function.CreateFunction.class); + conf.registerClass(net.sf.jsqlparser.statement.create.index.CreateIndex.class); + conf.registerClass(net.sf.jsqlparser.statement.create.procedure.CreateProcedure.class); + conf.registerClass(net.sf.jsqlparser.statement.create.schema.CreateSchema.class); + conf.registerClass(net.sf.jsqlparser.statement.create.sequence.CreateSequence.class); + conf.registerClass(net.sf.jsqlparser.statement.create.synonym.CreateSynonym.class); + conf.registerClass(net.sf.jsqlparser.statement.create.table.CheckConstraint.class); + conf.registerClass(net.sf.jsqlparser.statement.create.table.ColDataType.class); + conf.registerClass(net.sf.jsqlparser.statement.create.table.ColumnDefinition.class); + conf.registerClass(net.sf.jsqlparser.statement.create.table.CreateTable.class); + conf.registerClass(net.sf.jsqlparser.statement.create.table.ExcludeConstraint.class); + conf.registerClass(net.sf.jsqlparser.statement.create.table.ForeignKeyIndex.class); + conf.registerClass(net.sf.jsqlparser.statement.create.table.Index.class); + conf.registerClass(net.sf.jsqlparser.statement.create.table.Index.ColumnParams.class); + conf.registerClass(net.sf.jsqlparser.statement.create.table.NamedConstraint.class); + conf.registerClass(net.sf.jsqlparser.statement.create.table.RowMovement.class); + conf.registerClass(net.sf.jsqlparser.statement.create.view.AlterView.class); + conf.registerClass(net.sf.jsqlparser.statement.create.view.CreateView.class); + conf.registerClass(net.sf.jsqlparser.statement.delete.Delete.class); + conf.registerClass(net.sf.jsqlparser.statement.drop.Drop.class); + conf.registerClass(net.sf.jsqlparser.statement.execute.Execute.class); + conf.registerClass(net.sf.jsqlparser.statement.grant.Grant.class); + conf.registerClass(net.sf.jsqlparser.statement.insert.Insert.class); + conf.registerClass(net.sf.jsqlparser.statement.insert.InsertConflictAction.class); + conf.registerClass(net.sf.jsqlparser.statement.insert.InsertConflictTarget.class); + conf.registerClass(net.sf.jsqlparser.statement.merge.Merge.class); + conf.registerClass(net.sf.jsqlparser.statement.merge.MergeDelete.class); + conf.registerClass(net.sf.jsqlparser.statement.merge.MergeInsert.class); + conf.registerClass(net.sf.jsqlparser.statement.merge.MergeUpdate.class); + conf.registerClass(net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.select.AllColumns.class); + conf.registerClass(net.sf.jsqlparser.statement.select.AllTableColumns.class); + conf.registerClass(net.sf.jsqlparser.statement.select.Distinct.class); + conf.registerClass(net.sf.jsqlparser.statement.select.ExceptOp.class); + conf.registerClass(net.sf.jsqlparser.statement.select.Fetch.class); + conf.registerClass(net.sf.jsqlparser.statement.select.First.class); + conf.registerClass(net.sf.jsqlparser.statement.select.ForClause.class); + conf.registerClass(net.sf.jsqlparser.statement.select.GroupByElement.class); + conf.registerClass(net.sf.jsqlparser.statement.select.IntersectOp.class); + conf.registerClass(net.sf.jsqlparser.statement.select.Join.class); + conf.registerClass(net.sf.jsqlparser.statement.select.KSQLJoinWindow.class); + conf.registerClass(net.sf.jsqlparser.statement.select.KSQLWindow.class); + conf.registerClass(net.sf.jsqlparser.statement.select.LateralSubSelect.class); + conf.registerClass(net.sf.jsqlparser.statement.select.LateralView.class); + conf.registerClass(net.sf.jsqlparser.statement.select.Limit.class); + conf.registerClass(net.sf.jsqlparser.statement.select.MinusOp.class); + conf.registerClass(net.sf.jsqlparser.statement.select.Offset.class); + conf.registerClass(net.sf.jsqlparser.statement.select.OptimizeFor.class); + conf.registerClass(net.sf.jsqlparser.statement.select.OrderByElement.class); + conf.registerClass(net.sf.jsqlparser.statement.select.ParenthesedFromItem.class); + conf.registerClass(net.sf.jsqlparser.statement.select.ParenthesedSelect.class); + conf.registerClass(net.sf.jsqlparser.statement.select.Pivot.class); + conf.registerClass(net.sf.jsqlparser.statement.select.PivotXml.class); + conf.registerClass(net.sf.jsqlparser.statement.select.PlainSelect.class); + conf.registerClass(net.sf.jsqlparser.statement.select.SelectItem.class); + conf.registerClass(net.sf.jsqlparser.statement.select.SetOperationList.class); + conf.registerClass(net.sf.jsqlparser.statement.select.Skip.class); + conf.registerClass(net.sf.jsqlparser.statement.select.TableFunction.class); + conf.registerClass(net.sf.jsqlparser.statement.select.TableStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.select.Top.class); + conf.registerClass(net.sf.jsqlparser.statement.select.UnPivot.class); + conf.registerClass(net.sf.jsqlparser.statement.select.UnionOp.class); + conf.registerClass(net.sf.jsqlparser.statement.select.Values.class); + conf.registerClass(net.sf.jsqlparser.statement.select.Wait.class); + conf.registerClass(net.sf.jsqlparser.statement.select.WithIsolation.class); + conf.registerClass(net.sf.jsqlparser.statement.select.WithItem.class); + conf.registerClass(net.sf.jsqlparser.statement.show.ShowIndexStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.show.ShowTablesStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.truncate.Truncate.class); + conf.registerClass(net.sf.jsqlparser.statement.update.Update.class); + conf.registerClass(net.sf.jsqlparser.statement.update.UpdateSet.class); + conf.registerClass(net.sf.jsqlparser.statement.upsert.Upsert.class); + conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultiAndExpression.class); + conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultiOrExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.BinaryExpression.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ComparisonOperator.class); + conf.registerClass(net.sf.jsqlparser.expression.operators.relational.OldOracleJoinBinaryExpression.class); + conf.registerClass(net.sf.jsqlparser.statement.CreateFunctionalStatement.class); + conf.registerClass(net.sf.jsqlparser.statement.select.Select.class); + conf.registerClass(net.sf.jsqlparser.statement.select.SetOperation.class); + conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultipleExpression.class); + } + + public byte[] asByteArray(Object obj) { + return conf.asByteArray(obj); + } + + public Object asObject(byte[] bytes) { + return conf.asObject(bytes); + } +} diff --git a/mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstSerialCaffeineJsqlParseCache.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstSerialCaffeineJsqlParseCache.java similarity index 100% rename from mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstSerialCaffeineJsqlParseCache.java rename to mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstSerialCaffeineJsqlParseCache.java diff --git a/mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JdkSerialCaffeineJsqlParseCache.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JdkSerialCaffeineJsqlParseCache.java similarity index 100% rename from mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JdkSerialCaffeineJsqlParseCache.java rename to mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JdkSerialCaffeineJsqlParseCache.java diff --git a/mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JsqlParseCache.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JsqlParseCache.java similarity index 100% rename from mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JsqlParseCache.java rename to mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JsqlParseCache.java diff --git a/mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/DataPermissionHandler.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/DataPermissionHandler.java similarity index 100% rename from mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/DataPermissionHandler.java rename to mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/DataPermissionHandler.java diff --git a/mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/MultiDataPermissionHandler.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/MultiDataPermissionHandler.java similarity index 100% rename from mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/MultiDataPermissionHandler.java rename to mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/MultiDataPermissionHandler.java diff --git a/mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/TenantLineHandler.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/TenantLineHandler.java similarity index 100% rename from mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/TenantLineHandler.java rename to mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/TenantLineHandler.java diff --git a/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/BaseMultiTableInnerInterceptor.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/BaseMultiTableInnerInterceptor.java new file mode 100644 index 000000000..cee4b900b --- /dev/null +++ b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/BaseMultiTableInnerInterceptor.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2011-2024, baomidou (jobob@qq.com). + * + * 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.baomidou.mybatisplus.extension.plugins.inner; + +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import net.sf.jsqlparser.expression.*; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +import net.sf.jsqlparser.expression.operators.relational.EqualsTo; +import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.InExpression; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.select.*; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 多表条件处理基对象,从原有的 {@link TenantLineInnerInterceptor} 拦截器中提取出来 + * + * @author houkunlin + * @since 3.5.2 + */ +@Data +@NoArgsConstructor +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +@SuppressWarnings({"rawtypes"}) +public abstract class BaseMultiTableInnerInterceptor extends JsqlParserSupport implements InnerInterceptor { + + protected void processSelectBody(Select selectBody, final String whereSegment) { + if (selectBody == null) { + return; + } + if (selectBody instanceof PlainSelect) { + processPlainSelect((PlainSelect) selectBody, whereSegment); + } else if (selectBody instanceof ParenthesedSelect) { + ParenthesedSelect parenthesedSelect = (ParenthesedSelect) selectBody; + processSelectBody(parenthesedSelect.getSelect(), whereSegment); + } else if (selectBody instanceof SetOperationList) { + SetOperationList operationList = (SetOperationList) selectBody; + List selects = setOperationList.getSelects(); + if (CollectionUtils.isEmpty(selects)) { + return columnNameValMap; + } + final Select selectBody = selects.get(0); + if (!(selectBody instanceof Values)) { + return columnNameValMap; + } + Values valuesStatement = (Values) selectBody; + if (valuesStatement.getExpressions() instanceof ExpressionList) { + ExpressionList expressionList = valuesStatement.getExpressions(); + List expressions = expressionList; + for (Expression expression : expressions) { + if (expression instanceof RowConstructor) { + final ExpressionList exprList = ((RowConstructor) expression); + final List insertExpList = exprList; + for (int i = 0; i < insertExpList.size(); ++i) { + Expression e = insertExpList.get(i); + if (!(e instanceof JdbcParameter)) { + final String columnName = columns.get(i).getColumnName(); + final String val = e.toString(); + columnNameValMap.put(columnName, val); + } + } + } + } + } + } + return columnNameValMap; + } + + private String getColumnNameByProperty(String propertyName, String tableName) { + for (TableInfo tableInfo : TableInfoHelper.getTableInfos()) { + if (tableName.equalsIgnoreCase(tableInfo.getTableName())) { + final List fieldList = tableInfo.getFieldList(); + if (CollectionUtils.isEmpty(fieldList)) { + return propertyName; + } + for (TableFieldInfo tableFieldInfo : fieldList) { + if (propertyName.equalsIgnoreCase(tableFieldInfo.getProperty())) { + return tableFieldInfo.getColumn().toUpperCase(); + } + } + return propertyName; + } + } + return propertyName; + } + + + private Map buildParameterObjectMap(BoundSql boundSql) { + MetaObject metaObject = PluginUtils.getMetaObject(boundSql.getParameterObject()); + Map propertyValMap = new HashMap<>(boundSql.getParameterMappings().size()); + for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) { + String propertyName = parameterMapping.getProperty(); + if (propertyName.startsWith("ew.paramNameValuePairs")) { + continue; + } + Object propertyValue = metaObject.getValue(propertyName); + propertyValMap.put(propertyName, propertyValue); + } + return propertyValMap; + + } + + + private String buildOriginalData(Select selectStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) { + try (PreparedStatement statement = connection.prepareStatement(selectStmt.toString())) { + DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), boundSql); + parameterHandler.setParameters(statement); + ResultSet resultSet = statement.executeQuery(); + final ResultSetMetaData metaData = resultSet.getMetaData(); + int columnCount = metaData.getColumnCount(); + StringBuilder sb = new StringBuilder("["); + int count = 0; + while (resultSet.next()) { + ++count; + if (checkTableBatchLimitExceeded(selectStmt, count)) { + logger.error("batch delete limit exceed: count={}, BATCH_UPDATE_LIMIT={}", count, BATCH_UPDATE_LIMIT); + throw DataUpdateLimitationException.DEFAULT; + } + sb.append("{"); + for (int i = 1; i <= columnCount; ++i) { + sb.append("\"").append(metaData.getColumnName(i)).append("\":\""); + Object res = resultSet.getObject(i); + if (res instanceof Clob) { + sb.append(DataColumnChangeResult.convertClob((Clob) res)); + } else { + sb.append(res); + } + sb.append("\","); + } + sb.replace(sb.length() - 1, sb.length(), "}"); + } + sb.append("]"); + resultSet.close(); + return sb.toString(); + } catch (Exception e) { + if (e instanceof DataUpdateLimitationException) { + throw (DataUpdateLimitationException) e; + } + logger.error("try to get record tobe deleted for selectStmt={}", selectStmt, e); + return "failed to get original data"; + } + } + + private OriginalDataObj buildOriginalObjectData(Map updatedColumnDatas, Select selectStmt, Column pk, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) { + try (PreparedStatement statement = connection.prepareStatement(selectStmt.toString())) { + DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), boundSql); + parameterHandler.setParameters(statement); + ResultSet resultSet = statement.executeQuery(); + List originalObjectDatas = new LinkedList<>(); + int count = 0; + + while (resultSet.next()) { + ++count; + if (checkTableBatchLimitExceeded(selectStmt, count)) { + logger.error("batch update limit exceed: count={}, BATCH_UPDATE_LIMIT={}", count, BATCH_UPDATE_LIMIT); + throw DataUpdateLimitationException.DEFAULT; + } + originalObjectDatas.add(prepareOriginalDataObj(updatedColumnDatas, resultSet, pk)); + } + OriginalDataObj result = new OriginalDataObj(); + result.setOriginalDataObj(originalObjectDatas); + resultSet.close(); + return result; + } catch (Exception e) { + if (e instanceof DataUpdateLimitationException) { + throw (DataUpdateLimitationException) e; + } + logger.error("try to get record tobe updated for selectStmt={}", selectStmt, e); + return new OriginalDataObj(); + } + } + + /** + * 防止出现全表批量更新 + * 默认一次更新不超过1000条 + * + * @param selectStmt + * @param count + * @return + */ + private boolean checkTableBatchLimitExceeded(Select selectStmt, int count) { + if (!batchUpdateLimitationOpened) { + return false; + } + final PlainSelect selectBody = (PlainSelect) selectStmt; + final FromItem fromItem = selectBody.getFromItem(); + if (fromItem instanceof Table) { + Table fromTable = (Table) fromItem; + final String tableName = fromTable.getName().toUpperCase(); + if (!BATCH_UPDATE_LIMIT_MAP.containsKey(tableName)) { + if (count > BATCH_UPDATE_LIMIT) { + logger.error("batch update limit exceed for tableName={}, BATCH_UPDATE_LIMIT={}, count={}", + tableName, BATCH_UPDATE_LIMIT, count); + return true; + } + return false; + } + final Integer limit = BATCH_UPDATE_LIMIT_MAP.get(tableName); + if (count > limit) { + logger.error("batch update limit exceed for configured tableName={}, BATCH_UPDATE_LIMIT={}, count={}", + tableName, limit, count); + return true; + } + return false; + } + return count > BATCH_UPDATE_LIMIT; + } + + + /** + * get records : include related column with original data in DB + * + * @param resultSet + * @param pk + * @return + * @throws SQLException + */ + private DataChangedRecord prepareOriginalDataObj(Map updatedColumnDatas, ResultSet resultSet, Column pk) throws SQLException { + final ResultSetMetaData metaData = resultSet.getMetaData(); + int columnCount = metaData.getColumnCount(); + List originalColumnDatas = new LinkedList<>(); + DataColumnChangeResult pkval = null; + for (int i = 1; i <= columnCount; ++i) { + String columnName = metaData.getColumnName(i).toUpperCase(); + DataColumnChangeResult col; + Object updateVal = updatedColumnDatas.get(columnName); + if (updateVal != null && updateVal.getClass().getCanonicalName().startsWith("java.")) { + col = DataColumnChangeResult.constrcutByOriginalVal(columnName, resultSet.getObject(i, updateVal.getClass())); + } else { + col = DataColumnChangeResult.constrcutByOriginalVal(columnName, resultSet.getObject(i)); + } + if (pk != null && columnName.equalsIgnoreCase(pk.getColumnName())) { + pkval = col; + } else { + originalColumnDatas.add(col); + } + } + DataChangedRecord changedRecord = new DataChangedRecord(); + changedRecord.setOriginalColumnDatas(originalColumnDatas); + if (pkval != null) { + changedRecord.setPkColumnName(pkval.getColumnName()); + changedRecord.setPkColumnVal(pkval.getOriginalValue()); + } + return changedRecord; + } + + + private Columns2SelectItemsResult buildColumns2SelectItems(String tableName, List columns) { + if (columns == null || columns.isEmpty()) { + return Columns2SelectItemsResult.build(Collections.singletonList(new SelectItem<>(new AllColumns())), 0); + } + List> selectItems = new ArrayList<>(columns.size()); + for (Column column : columns) { + selectItems.add(new SelectItem<>(column)); + } + TableInfo tableInfo = getTableInfoByTableName(tableName); + if (tableInfo == null) { + return Columns2SelectItemsResult.build(selectItems, 0); + } + Column pk = new Column(tableInfo.getKeyColumn()); + selectItems.add(new SelectItem<>(pk)); + Columns2SelectItemsResult result = Columns2SelectItemsResult.build(selectItems, 1); + result.setPk(pk); + return result; + } + + private String buildParameterObject(BoundSql boundSql) { + Object paramObj = boundSql.getParameterObject(); + StringBuilder sb = new StringBuilder(); + sb.append("{"); + if (paramObj instanceof Map) { + Map paramMap = (Map) paramObj; + int index = 1; + boolean hasParamIndex = false; + String key; + while (paramMap.containsKey((key = "param" + index))) { + Object paramIndex = paramMap.get(key); + sb.append("\"").append(key).append("\"").append(":").append("\"").append(paramIndex).append("\"").append(","); + hasParamIndex = true; + ++index; + } + if (hasParamIndex) { + sb.delete(sb.length() - 1, sb.length()); + sb.append("}"); + return sb.toString(); + } + for (Map.Entry ety : paramMap.entrySet()) { + sb.append("\"").append(ety.getKey()).append("\"").append(":").append("\"").append(ety.getValue()).append("\"").append(","); + } + sb.delete(sb.length() - 1, sb.length()); + sb.append("}"); + return sb.toString(); + } + sb.append("param:").append(paramObj); + sb.append("}"); + return sb.toString(); + } + + public OperationResult processDelete(Delete deleteStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) { + Table table = deleteStmt.getTable(); + Expression where = deleteStmt.getWhere(); + PlainSelect selectBody = new PlainSelect(); + selectBody.setFromItem(table); + selectBody.setSelectItems(Collections.singletonList(new SelectItem<>((new AllColumns())))); + selectBody.setWhere(where); + String originalData = buildOriginalData(selectBody, mappedStatement, boundSql, connection); + OperationResult result = new OperationResult(); + result.setOperation("delete"); + result.setTableName(table.getName()); + result.setRecordStatus(originalData.startsWith("[")); + result.setChangedData(originalData); + return result; + } + + /** + * 设置批量更新记录条数上限 + * + * @param limit + * @return + */ + public DataChangeRecorderInnerInterceptor setBatchUpdateLimit(int limit) { + this.BATCH_UPDATE_LIMIT = limit; + return this; + } + + public DataChangeRecorderInnerInterceptor openBatchUpdateLimitation() { + this.batchUpdateLimitationOpened = true; + return this; + } + + public DataChangeRecorderInnerInterceptor configTableLimitation(String tableName, int limit) { + this.BATCH_UPDATE_LIMIT_MAP.put(tableName.toUpperCase(), limit); + return this; + } + + /** + * ignoredColumns = TABLE_NAME1.COLUMN1,COLUMN2; TABLE2.COLUMN1,COLUMN2; TABLE3.*; *.COLUMN1,COLUMN2 + * 多个表用分号分隔 + * TABLE_NAME1.COLUMN1,COLUMN2 : 表示忽略这个表的这2个字段 + * TABLE3.*: 表示忽略这张表的INSERT/UPDATE,delete暂时还保留 + * *.COLUMN1,COLUMN2:表示所有表的这个2个字段名都忽略 + * + * @param properties + */ + @Override + public void setProperties(Properties properties) { + + String ignoredTableColumns = properties.getProperty("ignoredTableColumns"); + if (ignoredTableColumns == null || ignoredTableColumns.trim().isEmpty()) { + return; + } + String[] array = ignoredTableColumns.split(";"); + for (String table : array) { + int index = table.indexOf("."); + if (index == -1) { + logger.warn("invalid data={} for ignoredColumns, format should be TABLE_NAME1.COLUMN1,COLUMN2; TABLE2.COLUMN1,COLUMN2;", table); + continue; + } + String tableName = table.substring(0, index).trim().toUpperCase(); + String[] columnArray = table.substring(index + 1).split(","); + Set columnSet = new HashSet<>(columnArray.length); + for (String column : columnArray) { + column = column.trim().toUpperCase(); + if (column.isEmpty()) { + continue; + } + columnSet.add(column); + } + if ("*".equals(tableName)) { + ignoreAllColumns.addAll(columnSet); + } else { + this.ignoredTableColumns.put(tableName, columnSet); + } + } + } + + @Data + public static class OperationResult { + + private String operation; + private boolean recordStatus; + private String tableName; + private String changedData; + /** + * cost for this plugin, ms + */ + private long cost; + + public void buildDataStr(List records) { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (DataChangedRecord r : records) { + sb.append(r.generateUpdatedDataStr()).append(","); + } + if (sb.length() == 1) { + sb.append("]"); + changedData = sb.toString(); + return; + } + sb.replace(sb.length() - 1, sb.length(), "]"); + changedData = sb.toString(); + } + + @Override + public String toString() { + return "{" + + "\"tableName\":\"" + tableName + "\"," + + "\"operation\":\"" + operation + "\"," + + "\"recordStatus\":\"" + recordStatus + "\"," + + "\"changedData\":" + changedData + "," + + "\"cost(ms)\":" + cost + "}"; + } + } + + @Data + public static class Columns2SelectItemsResult { + + private Column pk; + /** + * all column with additional columns: ID, etc. + */ + private List> selectItems; + /** + * newly added column count from meta data. + */ + private int additionalItemCount; + + public static Columns2SelectItemsResult build(List> selectItems, int additionalItemCount) { + Columns2SelectItemsResult result = new Columns2SelectItemsResult(); + result.setSelectItems(selectItems); + result.setAdditionalItemCount(additionalItemCount); + return result; + } + } + + @Data + public static class OriginalDataObj { + + private List originalDataObj; + + public boolean isEmpty() { + return originalDataObj == null || originalDataObj.isEmpty(); + } + + } + + @Data + public static class DataColumnChangeResult { + + private String columnName; + private Object originalValue; + private Object updateValue; + + @SuppressWarnings("rawtypes") + public boolean isDataChanged(Object updateValue) { + if (!Objects.equals(originalValue, updateValue)) { + if (originalValue instanceof Clob) { + String originalStr = convertClob((Clob) originalValue); + setOriginalValue(originalStr); + return !originalStr.equals(updateValue); + } + if (originalValue instanceof Comparable) { + Comparable original = (Comparable) originalValue; + Comparable update = (Comparable) updateValue; + try { + return update == null || original.compareTo(update) != 0; + } catch (Exception e) { + return true; + } + } + return true; + } + return false; + } + + public static String convertClob(Clob clobObj) { + try { + return clobObj.getSubString(0, (int) clobObj.length()); + } catch (Exception e) { + try (Reader is = clobObj.getCharacterStream()) { + char[] chars = new char[64]; + int readChars; + StringBuilder sb = new StringBuilder(); + while ((readChars = is.read(chars)) != -1) { + sb.append(chars, 0, readChars); + } + return sb.toString(); + } catch (Exception e2) { + //ignored + return "unknown clobObj"; + } + } + } + + public static DataColumnChangeResult constrcutByUpdateVal(String columnName, Object updateValue) { + DataColumnChangeResult res = new DataColumnChangeResult(); + res.setColumnName(columnName); + res.setUpdateValue(updateValue); + return res; + } + + public static DataColumnChangeResult constrcutByOriginalVal(String columnName, Object originalValue) { + DataColumnChangeResult res = new DataColumnChangeResult(); + res.setColumnName(columnName); + res.setOriginalValue(originalValue); + return res; + } + + public String generateDataStr() { + StringBuilder sb = new StringBuilder(); + sb.append("\"").append(columnName).append("\"").append(":").append("\"").append(convertDoubleQuotes(originalValue)).append("->").append(convertDoubleQuotes(updateValue)).append("\"").append(","); + return sb.toString(); + } + + public String convertDoubleQuotes(Object obj) { + if (obj == null) { + return null; + } + return obj.toString().replace("\"", "\\\""); + } + } + + @Data + public static class DataChangedRecord { + + private String pkColumnName; + private Object pkColumnVal; + private List originalColumnDatas; + private List updatedColumns; + + public boolean hasUpdate(Map columnNameValMap, Set ignoredColumns, Set ignoreAllColumns) { + if (originalColumnDatas == null) { + return true; + } + boolean hasUpdate = false; + updatedColumns = new ArrayList<>(originalColumnDatas.size()); + for (DataColumnChangeResult originalColumn : originalColumnDatas) { + final String columnName = originalColumn.getColumnName().toUpperCase(); + if (ignoredColumns != null && ignoredColumns.contains(columnName) || ignoreAllColumns.contains(columnName)) { + continue; + } + Object updatedValue = columnNameValMap.get(columnName); + if (originalColumn.isDataChanged(updatedValue)) { + hasUpdate = true; + originalColumn.setUpdateValue(updatedValue); + updatedColumns.add(originalColumn); + } + } + return hasUpdate; + } + + public String generateUpdatedDataStr() { + StringBuilder sb = new StringBuilder(); + sb.append("{"); + if (pkColumnName != null) { + sb.append("\"").append(pkColumnName).append("\"").append(":").append("\"").append(convertDoubleQuotes(pkColumnVal)).append("\"").append(","); + } + for (DataColumnChangeResult update : updatedColumns) { + sb.append(update.generateDataStr()); + } + sb.replace(sb.length() - 1, sb.length(), "}"); + return sb.toString(); + } + + public String convertDoubleQuotes(Object obj) { + if (obj == null) { + return null; + } + return obj.toString().replace("\"", "\\\""); + } + } + + public static class DataUpdateLimitationException extends MybatisPlusException { + + public DataUpdateLimitationException(String message) { + super(message); + } + + public static DataUpdateLimitationException DEFAULT = new DataUpdateLimitationException("本次操作 因超过系统安全阈值 被拦截,如需继续,请联系管理员!"); + } +} diff --git a/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DataPermissionInterceptor.java b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DataPermissionInterceptor.java new file mode 100644 index 000000000..5e6246d4b --- /dev/null +++ b/mybatis-plus-jsqlparser/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DataPermissionInterceptor.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2011-2024, baomidou (jobob@qq.com). + * + * 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.baomidou.mybatisplus.extension.plugins.inner; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; + +import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler; +import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; + +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.PluginUtils; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.select.WithItem; +import net.sf.jsqlparser.statement.update.Update; + +/** + * 数据权限处理器 + * + * @author hubin + * @since 3.5.2 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +@SuppressWarnings({"rawtypes"}) +public class DataPermissionInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor { + + private DataPermissionHandler dataPermissionHandler; + + @SuppressWarnings("RedundantThrows") + @Override + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql); + mpBs.sql(parserSingle(mpBs.sql(), ms.getId())); + } + + @Override + public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { + PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); + MappedStatement ms = mpSh.mappedStatement(); + SqlCommandType sct = ms.getSqlCommandType(); + if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); + mpBs.sql(parserMulti(mpBs.sql(), ms.getId())); + } + } + + @Override + protected void processSelect(Select select, int index, String sql, Object obj) { + if (dataPermissionHandler == null) { + return; + } + if (dataPermissionHandler instanceof MultiDataPermissionHandler) { + // 参照 com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor.processSelect 做的修改 + final String whereSegment = (String) obj; + processSelectBody(select, whereSegment); + List withItemsList = select.getWithItemsList(); + if (!CollectionUtils.isEmpty(withItemsList)) { + withItemsList.forEach(withItem -> processSelectBody(withItem, whereSegment)); + } + } else { + // 兼容原来的旧版 DataPermissionHandler 场景 + if (select instanceof PlainSelect) { + this.setWhere((PlainSelect) select, (String) obj); + } else if (select instanceof SetOperationList) { + SetOperationList setOperationList = (SetOperationList) select; + List