diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/pom.xml b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/pom.xml index e78893386e609ae5501b53f30d4c75dd3757868a..f63fe9706a1e85e4591bc714ab7e7061a4c5c9b1 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/pom.xml +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/pom.xml @@ -14,7 +14,6 @@ mybatis-plus-extension-solon-plugin jar - org.noear @@ -52,6 +51,13 @@ provided + + com.alibaba.fastjson2 + fastjson2 + 2.0.53 + provided + + com.fasterxml.jackson.core jackson-databind @@ -87,6 +93,42 @@ test + + de.ruedigermoeller + fst + 3.0.4-jdk17 + + + + org.mybatis.scripting + mybatis-thymeleaf + 1.0.4 + + + + org.mybatis.scripting + mybatis-velocity + 2.2.0 + + + + org.mybatis.scripting + mybatis-freemarker + 1.3.0 + + + + com.github.ben-manes.caffeine + caffeine + 2.9.3 + + + + io.github.classgraph + classgraph + 4.8.176 + + com.zaxxer HikariCP diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/activerecord/Model.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/activerecord/Model.java index 933705627d70d8f212240b5de68518cd41e57478..d7e68aa860364b6fc2843501916cf1a4857088eb 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/activerecord/Model.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/activerecord/Model.java @@ -86,7 +86,12 @@ public abstract class Model> implements Serializable { */ public boolean deleteById() { Assert.isFalse(StringUtils.checkValNull(pkVal()), "deleteById primaryKey is null."); - return deleteById(pkVal()); + SqlSession sqlSession = sqlSession(); + try { + return SqlHelper.retBool(sqlSession.delete(sqlStatement(SqlMethod.DELETE_BY_ID), this)); + } finally { + closeSqlSession(sqlSession); + } } /** @@ -110,7 +115,6 @@ public abstract class Model> implements Serializable { */ public boolean updateById() { Assert.isFalse(StringUtils.checkValNull(pkVal()), "updateById primaryKey is null."); - // updateById Map map = CollectionUtils.newHashMapWithExpectedSize(1); map.put(Constants.ENTITY, this); SqlSession sqlSession = sqlSession(); @@ -210,7 +214,7 @@ public abstract class Model> implements Serializable { map.put("page", page); SqlSession sqlSession = sqlSession(); try { - page.setRecords(sqlSession.selectList(sqlStatement(SqlMethod.SELECT_PAGE), map)); + page.setRecords(sqlSession.selectList(sqlStatement(SqlMethod.SELECT_LIST), map)); } finally { closeSqlSession(sqlSession); } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/AbstractChainWrapper.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/AbstractChainWrapper.java index 76f9f8f96f891011d6f05ce1d6e9de78b4429876..5543e7dc018b73340dfcb27653f5738145e3eb40 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/AbstractChainWrapper.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/AbstractChainWrapper.java @@ -201,6 +201,12 @@ public abstract class AbstractChainWrapper extends ChainWrapper { * @return 集合 */ default List list() { - return getBaseMapper().selectList(getWrapper()); + return execute(mapper -> mapper.selectList(getWrapper())); + } + + /** + * 获取集合 + * + * @param page 分页条件 + * @return 集合记录 + * @since 3.5.3.2 + */ + default List list(IPage page) { + return execute(mapper -> mapper.selectList(page, getWrapper())); } /** @@ -45,7 +56,7 @@ public interface ChainQuery extends ChainWrapper { * @return 单个 */ default T one() { - return getBaseMapper().selectOne(getWrapper()); + return execute(mapper -> mapper.selectOne(getWrapper())); } /** @@ -64,7 +75,7 @@ public interface ChainQuery extends ChainWrapper { * @return count */ default Long count() { - return SqlHelper.retCount(getBaseMapper().selectCount(getWrapper())); + return execute(mapper -> SqlHelper.retCount(mapper.selectCount(getWrapper()))); } /** @@ -83,6 +94,6 @@ public interface ChainQuery extends ChainWrapper { * @return 分页数据 */ default > E page(E page) { - return getBaseMapper().selectPage(page, getWrapper()); + return execute(mapper -> mapper.selectPage(page, getWrapper())); } } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/query/LambdaQueryChainWrapper.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/query/LambdaQueryChainWrapper.java index 08d0bc9944d69505ee00820ef0635713d06b6c30..fd26b0c215b6ebf0e2eac9eb3302e2e0307c40a4 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/query/LambdaQueryChainWrapper.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/query/LambdaQueryChainWrapper.java @@ -33,7 +33,7 @@ import java.util.function.Predicate; */ @SuppressWarnings({"serial"}) public class LambdaQueryChainWrapper extends AbstractChainWrapper, LambdaQueryChainWrapper, LambdaQueryWrapper> - implements ChainQuery, Query, T, SFunction> { + implements ChainQuery, Query, T, SFunction> { private final BaseMapper baseMapper; @@ -61,6 +61,12 @@ public class LambdaQueryChainWrapper extends AbstractChainWrapper(entityClass); } + public LambdaQueryChainWrapper(BaseMapper baseMapper, LambdaQueryWrapper wrapperChildren) { + super(); + this.baseMapper = baseMapper; + super.wrapperChildren = wrapperChildren; + } + @Override public LambdaQueryChainWrapper select(boolean condition, List> columns) { return doSelect(condition, columns); diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/query/QueryChainWrapper.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/query/QueryChainWrapper.java index 905da70a1f9a87ac91d0b948ff291d8a05989bea..7a22ce057869825cbd1c9e1818f4fb89f1beb937 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/query/QueryChainWrapper.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/query/QueryChainWrapper.java @@ -83,4 +83,13 @@ public class QueryChainWrapper extends AbstractChainWrapper getEntityClass() { return entityClass; } + + + public LambdaQueryChainWrapper lambda(){ + return new LambdaQueryChainWrapper<>( + baseMapper, + wrapperChildren.lambda() + ); + } + } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/ChainUpdate.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/ChainUpdate.java index 7935acd91d0a8b13edb483b861f244a3d3d4fa3f..3028015213d6ca1f8fe6873174e04c7f6eb1a5bb 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/ChainUpdate.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/ChainUpdate.java @@ -28,6 +28,7 @@ public interface ChainUpdate extends ChainWrapper { /** * 更新数据 + *

此方法无法进行自动填充,如需自动填充请使用{@link #update(Object)}

* * @return 是否成功 */ @@ -38,11 +39,11 @@ public interface ChainUpdate extends ChainWrapper { /** * 更新数据 * - * @param entity 实体类 + * @param entity 实体类(当entity为空时无法进行自动填充) * @return 是否成功 */ default boolean update(T entity) { - return SqlHelper.retBool(getBaseMapper().update(entity, getWrapper())); + return execute(mapper -> SqlHelper.retBool(mapper.update(entity, getWrapper()))); } /** @@ -51,6 +52,6 @@ public interface ChainUpdate extends ChainWrapper { * @return 是否成功 */ default boolean remove() { - return SqlHelper.retBool(getBaseMapper().delete(getWrapper())); + return execute(mapper -> SqlHelper.retBool(mapper.delete(getWrapper()))); } } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/LambdaUpdateChainWrapper.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/LambdaUpdateChainWrapper.java index 74eb9716c8df59436aa49c8a287a7f08ab645b64..14ac1f59b22e3dd9106046ff09ebf439187e68f2 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/LambdaUpdateChainWrapper.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/LambdaUpdateChainWrapper.java @@ -18,7 +18,6 @@ package com.baomidou.mybatisplus.solon.conditions.update; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.Update; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils; import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.solon.conditions.AbstractChainWrapper; @@ -28,7 +27,6 @@ import com.baomidou.mybatisplus.solon.conditions.AbstractChainWrapper; * @author miemie * @since 2018-12-19 */ -@SuppressWarnings({"serial"}) public class LambdaUpdateChainWrapper extends AbstractChainWrapper, LambdaUpdateChainWrapper, LambdaUpdateWrapper> implements ChainUpdate, Update, SFunction> { @@ -46,6 +44,12 @@ public class LambdaUpdateChainWrapper extends AbstractChainWrapper(entityClass); } + public LambdaUpdateChainWrapper(BaseMapper baseMapper, LambdaUpdateWrapper wrapperChildren) { + super(); + this.baseMapper = baseMapper; + super.wrapperChildren = wrapperChildren; + } + @Override public LambdaUpdateChainWrapper set(boolean condition, SFunction column, Object val, String mapping) { wrapperChildren.set(condition, column, val, mapping); @@ -58,9 +62,35 @@ public class LambdaUpdateChainWrapper extends AbstractChainWrapper setIncrBy(boolean condition, SFunction column, Number val) { + wrapperChildren.setIncrBy(condition, column, val); + return typedThis; + } + + /** + * 字段自减变量 val 值 + * + * @param condition 条件 + * @param column 字段 + * @param val 值 + * @return this + * @since 3.5.6 + */ @Override - public String getSqlSet() { - throw ExceptionUtils.mpe("can not use this method for \"%s\"", "getSqlSet"); + public LambdaUpdateChainWrapper setDecrBy(boolean condition, SFunction column, Number val) { + wrapperChildren.setDecrBy(condition, column, val); + return typedThis; } @Override diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/UpdateChainWrapper.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/UpdateChainWrapper.java index f108288ff3bd050c38c5ee5e5fc9338b7b500f34..13c3213bf2c71a5e8dd990d38ece97241ba7a283 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/UpdateChainWrapper.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/conditions/update/UpdateChainWrapper.java @@ -29,7 +29,7 @@ import com.baomidou.mybatisplus.solon.conditions.AbstractChainWrapper; */ @SuppressWarnings({"serial"}) public class UpdateChainWrapper extends AbstractChainWrapper, UpdateWrapper> - implements ChainUpdate, Update, String> { + implements ChainUpdate, Update, String> { private final BaseMapper baseMapper; private final Class entityClass; @@ -60,6 +60,18 @@ public class UpdateChainWrapper extends AbstractChainWrapper setIncrBy(boolean condition, String column, Number val) { + wrapperChildren.setIncrBy(condition, column, val); + return typedThis; + } + + @Override + public UpdateChainWrapper setDecrBy(boolean condition, String column, Number val) { + wrapperChildren.setDecrBy(condition, column, val); + return typedThis; + } + @Override public String getSqlSet() { throw ExceptionUtils.mpe("can not use this method for \"%s\"", "getSqlSet"); @@ -74,4 +86,11 @@ public class UpdateChainWrapper extends AbstractChainWrapper getEntityClass() { return entityClass; } + + public LambdaUpdateChainWrapper lambda(){ + return new LambdaUpdateChainWrapper<>( + baseMapper, + wrapperChildren.lambda() + ); + } } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/ddl/DdlHelper.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/ddl/DdlHelper.java index 4b3ee1151e3a8ce07e4a50ac33e78fdba040085a..08725baabb615c6bb69e2a88def57345358e1d5a 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/ddl/DdlHelper.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/ddl/DdlHelper.java @@ -15,27 +15,31 @@ */ package com.baomidou.mybatisplus.solon.ddl; +import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.solon.ddl.history.IDdlGenerator; import com.baomidou.mybatisplus.solon.ddl.history.MysqlDdlGenerator; import com.baomidou.mybatisplus.solon.ddl.history.OracleDdlGenerator; import com.baomidou.mybatisplus.solon.ddl.history.PostgreDdlGenerator; -import com.baomidou.mybatisplus.solon.plugins.pagination.DialectFactory; -import com.baomidou.mybatisplus.solon.plugins.pagination.dialects.*; +import com.baomidou.mybatisplus.solon.ddl.history.SQLiteDdlGenerator; + import com.baomidou.mybatisplus.solon.toolkit.JdbcUtils; import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.io.Resources; import org.apache.ibatis.jdbc.ScriptRunner; import org.apache.ibatis.jdbc.SqlRunner; import org.noear.solon.core.util.ResourceUtil; import javax.sql.DataSource; import java.io.*; +import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.SQLException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map; +import java.util.Objects; /** * DDL 辅助类 @@ -52,8 +56,8 @@ public class DdlHelper { * @param ddlGenerator DDL 生成器 * @param connection 数据库连接 * @param sqlFiles SQL 文件列表 - * @param autoCommit 字段提交事务 - * @throws Exception + * @param autoCommit 自动提交事务 + * @throws SQLException SQLException */ public static void runScript(IDdlGenerator ddlGenerator, Connection connection, List sqlFiles, boolean autoCommit) throws SQLException { // 执行自定义 DDL 信息 @@ -83,6 +87,15 @@ public class DdlHelper { List> objectMap = sqlRunner.selectAll(ddlGenerator.selectDdlHistory(sqlFile, StringPool.SQL)); if (null == objectMap || objectMap.isEmpty()) { log.debug("run script file: {}", sqlFile); + String[] sqlFileArr = sqlFile.split(StringPool.HASH); + if (Objects.equals(2, sqlFileArr.length)) { + // 命令间的分隔符 + scriptRunner.setDelimiter(sqlFileArr[1]); + // 原始文件路径 + sqlFile = sqlFileArr[0]; + } else { + scriptRunner.setDelimiter(StringPool.SEMICOLON); + } File file = new File(sqlFile); if (file.exists()) { scriptRunner.runScript(new FileReader(file)); @@ -103,8 +116,7 @@ public class DdlHelper { * @param ddlGenerator DDL 生成器 * @param dataSource 数据源 * @param sqlFiles SQL 文件列表 - * @param autoCommit 字段提交事务 - * @throws Exception + * @param autoCommit 自动提交事务 */ public static void runScript(IDdlGenerator ddlGenerator, DataSource dataSource, List sqlFiles, boolean autoCommit) { try (Connection connection = dataSource.getConnection()) { @@ -124,23 +136,33 @@ public class DdlHelper { public static ScriptRunner getScriptRunner(Connection connection, boolean autoCommit) { ScriptRunner scriptRunner = new ScriptRunner(connection); + Resources.setCharset(StandardCharsets.UTF_8); scriptRunner.setAutoCommit(autoCommit); + scriptRunner.setEscapeProcessing(false); + scriptRunner.setRemoveCRs(true); scriptRunner.setStopOnError(true); + scriptRunner.setFullLineDelimiter(false); return scriptRunner; } protected static IDdlGenerator getDdlGenerator(String jdbcUrl) throws RuntimeException { - IDialect dialect = DialectFactory.getDialect(JdbcUtils.getDbType(jdbcUrl)); - if (dialect instanceof MySqlDialect) { + DbType dbType = JdbcUtils.getDbType(jdbcUrl); + // mysql same type + if (dbType.mysqlSameType()) { return MysqlDdlGenerator.newInstance(); } - if (dialect instanceof PostgreDialect) { - return PostgreDdlGenerator.newInstance(); - } - if (dialect instanceof OracleDialect || dialect instanceof Oracle12cDialect) { + // oracle same type + else if (dbType.oracleSameType()) { return OracleDdlGenerator.newInstance(); } - throw new RuntimeException("The database is not supported"); + else if (DbType.SQLITE == dbType){ + return SQLiteDdlGenerator.newInstance(); + } + // postgresql same type + else if (dbType.postgresqlSameType()) { + return PostgreDdlGenerator.newInstance(); + } + throw new RuntimeException("Unsupported database type: " + jdbcUrl); } public static String getDatabase(String jdbcUrl) { diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/ddl/DdlScript.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/ddl/DdlScript.java index 4d3709f7440be09eadbd2070be0bf893e0f8d64d..4cf3f9540228cb87d56bda6de2635925fd0c504d 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/ddl/DdlScript.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/ddl/DdlScript.java @@ -15,12 +15,18 @@ */ package com.baomidou.mybatisplus.solon.ddl; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.solon.ddl.history.IDdlGenerator; +import org.apache.ibatis.jdbc.ScriptRunner; import javax.sql.DataSource; import java.io.Reader; import java.io.StringReader; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; import java.util.List; +import java.util.function.Consumer; /** * Ddl 脚本执行 @@ -29,9 +35,12 @@ import java.util.List; * @since 2021-07-23 */ public class DdlScript { - private DataSource dataSource; - private IDdlGenerator ddlGenerator; - private boolean autoCommit; + + private final DataSource dataSource; + + private final IDdlGenerator ddlGenerator; + + private final boolean autoCommit; public DdlScript(DataSource dataSource) { this(dataSource, null); @@ -54,34 +63,102 @@ public class DdlScript { /** * 执行 SQL 脚本文件 * - * @param sqlFiles SQL 脚本文件列表 + * @param sqlFiles SQL 脚本文件列表 + * @param autoCommit 自动提交事务 */ public void run(List sqlFiles, boolean autoCommit) { DdlHelper.runScript(this.ddlGenerator, this.dataSource, sqlFiles, autoCommit); } + public void run(String sqlScript) throws Exception { + this.run(sqlScript, null); + } + /** * 执行 SQL 脚本 * * @param sqlScript SQL 脚本内容 + * @param delimiter 执行 SQL 分隔符,默认 ; 符号结束执行 * @throws Exception */ - public void run(String sqlScript) throws Exception { - this.run(new StringReader(sqlScript)); + public void run(String sqlScript, String delimiter) throws Exception { + this.run(new StringReader(sqlScript), this.autoCommit, delimiter); } public void run(Reader reader) throws Exception { - this.run(reader, this.autoCommit); + this.run(reader, this.autoCommit, null); + } + + public void run(Reader reader, boolean autoCommit) throws Exception { + this.run(reader, autoCommit, null); + } + + public void run(Reader reader, boolean autoCommit, String delimiter) throws Exception { + this.run(this.dataSource.getConnection(), reader, autoCommit, delimiter); } /** * 执行 SQL 脚本 * + * @param connection {@link Connection} * @param reader SQL 脚本内容 * @param autoCommit 自动提交事务 - * @throws Exception + * @param delimiter 执行 SQL 分隔符,默认 ; 符号结束执行 */ - public void run(Reader reader, boolean autoCommit) throws Exception { - DdlHelper.getScriptRunner(dataSource.getConnection(), autoCommit).runScript(reader); + public void run(Connection connection, Reader reader, boolean autoCommit, String delimiter) { + ScriptRunner scriptRunner = DdlHelper.getScriptRunner(connection, autoCommit); + // 设置自定义 SQL 分隔符,默认 ; 符号分割 + if (StringUtils.isNotBlank(delimiter)) { + scriptRunner.setDelimiter(delimiter); + } + // 执行 SQL 脚本 + scriptRunner.runScript(reader); + } + + public boolean execute(final String driverClassName, final String url, final String user, final String password, + final String sql, Consumer exceptionConsumer) { + return this.execute(driverClassName, url, user, password, sql, null, exceptionConsumer); + } + + /** + * jdbc 连接指定 sql 执行 + * + * @param driverClassName 连接驱动名 + * @param url 连接地址 + * @param user 数据库用户名 + * @param password 数据库密码 + * @param sql 执行 SQL + * @param delimiter 执行 SQL 分隔符,默认 ; 符号结束执行 + * @param exceptionConsumer 异常处理 + * @return + */ + public boolean execute(final String driverClassName, final String url, final String user, final String password, + final String sql, String delimiter, Consumer exceptionConsumer) { + Connection connection = null; + try { + Class.forName(driverClassName); + connection = DriverManager.getConnection(url, user, password); + // 执行 SQL 脚本 + this.run(connection, new StringReader(sql), this.autoCommit, delimiter); + return true; + } catch (Exception e) { + if (null != connection) { + try { + connection.rollback(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + exceptionConsumer.accept(e.getMessage()); + } finally { + if (null != connection) { + try { + connection.close(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + } + return false; } } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/ddl/history/SQLiteDdlGenerator.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/ddl/history/SQLiteDdlGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..1537a1e69ff8e7a9dfdee3beb8f5f77bd640e4d5 --- /dev/null +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/ddl/history/SQLiteDdlGenerator.java @@ -0,0 +1,50 @@ +/* + * 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.solon.ddl.history; + +import java.util.function.Function; + +/** + * SQLite DDL 生成器 + * + * @author 呆猫 + * @since 2024-04-01 + */ +public class SQLiteDdlGenerator implements IDdlGenerator { + + public static IDdlGenerator newInstance() { + return new SQLiteDdlGenerator(); + } + + @Override + public boolean existTable(String databaseName, Function executeFunction) { + StringBuffer sql = new StringBuffer(); + sql.append("SELECT count(1) NUM FROM sqlite_master WHERE name='"); + sql.append(getDdlHistory()).append("' AND type='table'"); + return executeFunction.apply(sql.toString()); + } + + @Override + public String createDdlHistory() { + StringBuffer sql = new StringBuffer(); + sql.append("CREATE TABLE IF NOT EXISTS `").append(getDdlHistory()).append("` ("); + sql.append("script TEXT primary key,"); + sql.append("type TEXT,"); + sql.append("version TEXT"); + sql.append(");"); + return sql.toString(); + } +} diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/AbstractJsonTypeHandler.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/AbstractJsonTypeHandler.java index 381675aaf061fac9aaaafb9df5855be28d116b38..0bffa00f41fa16f2ebadbcc2f45612ca4d18dd96 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/AbstractJsonTypeHandler.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/AbstractJsonTypeHandler.java @@ -15,10 +15,16 @@ */ package com.baomidou.mybatisplus.solon.handlers; +import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler; +import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; +import java.lang.reflect.Field; +import java.lang.reflect.Type; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -28,7 +34,41 @@ import java.sql.SQLException; * @author miemie * @since 2019-11-28 */ -public abstract class AbstractJsonTypeHandler extends BaseTypeHandler { +public abstract class AbstractJsonTypeHandler extends BaseTypeHandler implements IJsonTypeHandler { + + protected final Log log = LogFactory.getLog(this.getClass()); + + protected final Class type; + + /** + * @since 3.5.6 + */ + protected Type genericType; + + /** + * 默认初始化 + * + * @param type 类型 + */ + public AbstractJsonTypeHandler(Class type) { + this.type = type; + if (log.isTraceEnabled()) { + log.trace(this.getClass().getSimpleName() + "(" + type + ")"); + } + Assert.notNull(type, "Type argument cannot be null"); + } + + /** + * 通过字段初始化 + * + * @param type 类型 + * @param field 字段 + * @since 3.5.6 + */ + public AbstractJsonTypeHandler(Class type, Field field) { + this(type); + this.genericType = field.getGenericType(); + } @Override public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { @@ -53,7 +93,8 @@ public abstract class AbstractJsonTypeHandler extends BaseTypeHandler { return StringUtils.isBlank(json) ? null : parse(json); } - protected abstract T parse(String json); + public Type getFieldType() { + return this.genericType != null ? this.genericType : this.type; + } - protected abstract String toJson(T obj); } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/Fastjson2TypeHandler.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/Fastjson2TypeHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..0ccfb6751895961bea364b3f21cdd180e7098be9 --- /dev/null +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/Fastjson2TypeHandler.java @@ -0,0 +1,55 @@ +/* + * 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.solon.handlers; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; + +import java.lang.reflect.Field; + +/** + * Fastjson2 实现 JSON 字段类型处理器 + * + * @author nieqiurong + * @since 3.5.5 + */ +@MappedTypes({Object.class}) +@MappedJdbcTypes(JdbcType.VARCHAR) +public class Fastjson2TypeHandler extends AbstractJsonTypeHandler { + + public Fastjson2TypeHandler(Class type) { + super(type); + } + + public Fastjson2TypeHandler(Class type, Field field) { + super(type, field); + } + + @Override + public Object parse(String json) { + return JSON.parseObject(json, this.getFieldType()); + } + + @Override + public String toJson(Object obj) { + return JSON.toJSONString(obj, JSONWriter.Feature.WriteMapNullValue, + JSONWriter.Feature.WriteNullListAsEmpty, JSONWriter.Feature.WriteNullStringAsEmpty); + } + +} diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/FastjsonTypeHandler.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/FastjsonTypeHandler.java index 0c313f71c55f84d2998c12243b3e66b29bc76f79..b8907d84c191a2f4d3bc3078089e48e6206d6c4b 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/FastjsonTypeHandler.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/FastjsonTypeHandler.java @@ -17,40 +17,39 @@ package com.baomidou.mybatisplus.solon.handlers; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; -import com.baomidou.mybatisplus.core.toolkit.Assert; -import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; +import java.lang.reflect.Field; + /** * Fastjson 实现 JSON 字段类型处理器 * * @author hubin * @since 2019-08-25 */ -@Slf4j @MappedTypes({Object.class}) @MappedJdbcTypes(JdbcType.VARCHAR) public class FastjsonTypeHandler extends AbstractJsonTypeHandler { - private final Class type; public FastjsonTypeHandler(Class type) { - if (log.isTraceEnabled()) { - log.trace("FastjsonTypeHandler(" + type + ")"); - } - Assert.notNull(type, "Type argument cannot be null"); - this.type = type; + super(type); + } + + public FastjsonTypeHandler(Class type, Field field) { + super(type, field); } @Override - protected Object parse(String json) { - return JSON.parseObject(json, type); + public Object parse(String json) { + return JSON.parseObject(json, this.getFieldType()); } @Override - protected String toJson(Object obj) { + public String toJson(Object obj) { return JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty); } + } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/GsonTypeHandler.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/GsonTypeHandler.java index 789883d122542fa86e03a828fff6bf4894017e0a..cb57ee5de56755c5a62bd4fc0a346dfe297f4c97 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/GsonTypeHandler.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/GsonTypeHandler.java @@ -17,39 +17,39 @@ package com.baomidou.mybatisplus.solon.handlers; import com.baomidou.mybatisplus.core.toolkit.Assert; import com.google.gson.Gson; -import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; +import java.lang.reflect.Field; + /** * Gson 实现 JSON 字段类型处理器 * * @author hubin * @since 2019-08-25 */ -@Slf4j @MappedTypes({Object.class}) @MappedJdbcTypes(JdbcType.VARCHAR) public class GsonTypeHandler extends AbstractJsonTypeHandler { + private static Gson GSON; - private final Class type; public GsonTypeHandler(Class type) { - if (log.isTraceEnabled()) { - log.trace("GsonTypeHandler(" + type + ")"); - } - Assert.notNull(type, "Type argument cannot be null"); - this.type = type; + super(type); + } + + public GsonTypeHandler(Class type, Field field) { + super(type, field); } @Override - protected Object parse(String json) { - return getGson().fromJson(json, type); + public Object parse(String json) { + return getGson().fromJson(json, this.getFieldType()); } @Override - protected String toJson(Object obj) { + public String toJson(Object obj) { return getGson().toJson(obj); } @@ -64,4 +64,5 @@ public class GsonTypeHandler extends AbstractJsonTypeHandler { Assert.notNull(gson, "Gson should not be null"); GsonTypeHandler.GSON = gson; } + } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/JacksonTypeHandler.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/JacksonTypeHandler.java index 697efb4c117635d315b97e12620a7405abbf162c..f5f181a9c200a9c451eb3327d9348ce03ad68978 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/JacksonTypeHandler.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/JacksonTypeHandler.java @@ -16,14 +16,16 @@ package com.baomidou.mybatisplus.solon.handlers; import com.baomidou.mybatisplus.core.toolkit.Assert; +import com.fasterxml.jackson.core.JacksonException; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.extern.slf4j.Slf4j; +import com.fasterxml.jackson.databind.type.TypeFactory; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; -import java.io.IOException; +import java.lang.reflect.Field; /** * Jackson 实现 JSON 字段类型处理器 @@ -31,35 +33,39 @@ import java.io.IOException; * @author hubin * @since 2019-08-25 */ -@Slf4j @MappedTypes({Object.class}) @MappedJdbcTypes(JdbcType.VARCHAR) public class JacksonTypeHandler extends AbstractJsonTypeHandler { + private static ObjectMapper OBJECT_MAPPER; - private final Class type; public JacksonTypeHandler(Class type) { - if (log.isTraceEnabled()) { - log.trace("JacksonTypeHandler(" + type + ")"); - } - Assert.notNull(type, "Type argument cannot be null"); - this.type = type; + super(type); + } + + public JacksonTypeHandler(Class type, Field field) { + super(type, field); } @Override - protected Object parse(String json) { + public Object parse(String json) { + ObjectMapper objectMapper = getObjectMapper(); + TypeFactory typeFactory = objectMapper.getTypeFactory(); + JavaType javaType = typeFactory.constructType(getFieldType()); try { - return getObjectMapper().readValue(json, type); - } catch (IOException e) { + return objectMapper.readValue(json, javaType); + } catch (JacksonException e) { + log.error("deserialize json: " + json + " to " + javaType + " error ", e); throw new RuntimeException(e); } } @Override - protected String toJson(Object obj) { + public String toJson(Object obj) { try { return getObjectMapper().writeValueAsString(obj); } catch (JsonProcessingException e) { + log.error("serialize " + obj + " to json error ", e); throw new RuntimeException(e); } } @@ -75,4 +81,5 @@ public class JacksonTypeHandler extends AbstractJsonTypeHandler { Assert.notNull(objectMapper, "ObjectMapper should not be null"); JacksonTypeHandler.OBJECT_MAPPER = objectMapper; } + } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/SnackTypeHandler.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/SnackTypeHandler.java index f072207489249b6c52f5f1b879939a39a7f92af7..aa5b1fa7fc04a362e339af9132d946823261aa17 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/SnackTypeHandler.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/handlers/SnackTypeHandler.java @@ -28,27 +28,21 @@ import org.noear.snack.ONode; * @author hubin * @since 2019-08-25 */ -@Slf4j @MappedTypes({Object.class}) @MappedJdbcTypes(JdbcType.VARCHAR) public class SnackTypeHandler extends AbstractJsonTypeHandler { - private final Class type; public SnackTypeHandler(Class type) { - if (log.isTraceEnabled()) { - log.trace("JacksonTypeHandler(" + type + ")"); - } - Assert.notNull(type, "Type argument cannot be null"); - this.type = type; + super(type); } @Override - protected Object parse(String json) { + public Object parse(String json) { return ONode.deserialize(json, type); } @Override - protected String toJson(Object obj) { + public String toJson(Object obj) { return ONode.stringify(obj); } } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/incrementer/LealoneKeyGenerator.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/incrementer/LealoneKeyGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..463c87e5b6a7109cccb5440223ab9b4d5349497c --- /dev/null +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/incrementer/LealoneKeyGenerator.java @@ -0,0 +1,38 @@ +/* + * 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.solon.incrementer; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator; + +/** + * Lealone Sequence + * + * @author zhoujin7 + * @since 2023-05-10 + */ +public class LealoneKeyGenerator implements IKeyGenerator { + + @Override + public String executeSql(String incrementerName) { + return "select nextval('" + incrementerName + "')"; + } + + @Override + public DbType dbType() { + return DbType.LEALONE; + } +} diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/AlwaysUpdateSomeColumnById.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/AlwaysUpdateSomeColumnById.java index cf7e4c439b1e22edc601b2ead524ce2d3e6a09fd..10edadef0d7fd75471d3ccf8516636e60347d43c 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/AlwaysUpdateSomeColumnById.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/AlwaysUpdateSomeColumnById.java @@ -42,7 +42,6 @@ import java.util.function.Predicate; * @author hubin * @since 2019-04-12 */ -@SuppressWarnings("serial") public class AlwaysUpdateSomeColumnById extends AbstractMethod { /** @@ -83,7 +82,7 @@ public class AlwaysUpdateSomeColumnById extends AbstractMethod { sqlSet = SqlScriptUtils.convertSet(sqlSet); String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlSet, tableInfo.getKeyColumn(), ENTITY_DOT + tableInfo.getKeyProperty(), additional); - SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); + SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource); } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/LogicDeleteBatchByIds.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/LogicDeleteBatchByIds.java index cf9dee54d400c1089ef09e673511b799cf1ae532..5829cf4fe64b8fd07e341228a63d9577f65874bd 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/LogicDeleteBatchByIds.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/LogicDeleteBatchByIds.java @@ -16,7 +16,7 @@ package com.baomidou.mybatisplus.solon.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; -import com.baomidou.mybatisplus.core.injector.methods.DeleteBatchByIds; +import com.baomidou.mybatisplus.core.injector.methods.DeleteByIds; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; @@ -29,9 +29,7 @@ import static java.util.stream.Collectors.toList; /** * 试验功能,不做太复杂的功能,逻辑删除增加填充功能 - * * 如果想做的通用点的话,delete的时候如果是主键,在service层转换实体进行删除,这样根据主键删除的也能自动填充 - * * 如果是逻辑删除且标记有填充字段的情况下,以第一条记录的填充字段为准(一切以当前的时间点为基准,如果无法接受记录记录时间不准确请使用循环删除) * 由于本身SQL更新的限制限,这里记录集合不能为空,也不支持第一条记录删除人是A或者时间是A时间,第二条记录的时间是B时间 * update table set (填充字段1,填充字段2,逻辑删除字段) where id in (主键1,主键2,主键3) @@ -48,8 +46,10 @@ import static java.util.stream.Collectors.toList; * * @author nieqiurong * @since 3.5.0 + * @deprecated 3.5.7 {@link DeleteByIds} */ -public class LogicDeleteBatchByIds extends DeleteBatchByIds { +@Deprecated +public class LogicDeleteBatchByIds extends DeleteByIds { public LogicDeleteBatchByIds() { super(); diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/Upsert.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/Upsert.java index 221c6e43c804f9efe48715e4806a298ecd8593bd..48e43facb62f3b528d65918c67e3ba468edc72c3 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/Upsert.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/injector/methods/Upsert.java @@ -31,7 +31,6 @@ import org.apache.ibatis.mapping.SqlSource; * @author fly * @since 2018-04-06 */ -@SuppressWarnings("serial") public class Upsert extends AbstractMethod { public Upsert() { @@ -66,7 +65,7 @@ public class Upsert extends AbstractMethod { } } String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript); - SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); + SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return this.addInsertMappedStatement(mapperClass, modelClass, methodName, sqlSource, keyGenerator, keyProperty, keyColumn); } } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/integration/MybatisAdapterPlusExt.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/integration/MybatisAdapterPlusExt.java index 0eeb1e59d1364d655b8bfefc26274f44267c06e7..1b0a3070771c86ac6023d2da901deb685b7e4051 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/integration/MybatisAdapterPlusExt.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/integration/MybatisAdapterPlusExt.java @@ -25,20 +25,20 @@ public class MybatisAdapterPlusExt extends MybatisAdapterPlus { } @Override - public void injectTo(VarHolder vh) { + public void injectTo(VarHolder varH) { //@Db("db1") IService service; - if (IService.class.isAssignableFrom(vh.getType())) { - vh.context().getWrapAsync(vh.getType(), serviceBw -> { + if (IService.class.isAssignableFrom(varH.getType())) { + varH.context().getWrapAsync(varH.getType(), serviceBw -> { if (serviceBw.raw() instanceof ServiceImpl) { //如果是 ServiceImpl - injectService(vh, serviceBw); + injectService(varH, serviceBw); } else { //如果不是 ServiceImpl - vh.setValue(serviceBw.get()); + varH.setValue(serviceBw.get()); } }); } else { - super.injectTo(vh); + super.injectTo(varH); } } @@ -50,12 +50,12 @@ public class MybatisAdapterPlusExt extends MybatisAdapterPlus { /** * 注入服务 IService */ - private void injectService(VarHolder vh, BeanWrap serviceBw) { + private void injectService(VarHolder varH, BeanWrap serviceBw) { ServiceImpl service = serviceBw.raw(); - if (serviceCached.containsKey(vh.getType())) { + if (serviceCached.containsKey(varH.getType())) { //从缓存获取 - service = serviceCached.get(vh.getType()); + service = serviceCached.get(varH.getType()); } else { Object baseMapperOld = service.getBaseMapper(); @@ -77,11 +77,11 @@ public class MybatisAdapterPlusExt extends MybatisAdapterPlus { service.setBaseMapper(baseMapper); //缓存 - serviceCached.put(vh.getType(), service); + serviceCached.put(varH.getType(), service); } } } - vh.setValue(service); + varH.setValue(service); } } \ No newline at end of file diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/JsqlParserFunction.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/JsqlParserFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..974e41d6cebaabab292cd3640aa6e0443fffa677 --- /dev/null +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/JsqlParserFunction.java @@ -0,0 +1,28 @@ +/* + * 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.solon.parser; + +import net.sf.jsqlparser.JSQLParserException; + +/** + * @author miemie + * @since 2023-08-05 + */ +@FunctionalInterface +public interface JsqlParserFunction { + + R apply(T t) throws JSQLParserException; +} diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/JsqlParserGlobal.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/JsqlParserGlobal.java new file mode 100644 index 0000000000000000000000000000000000000000..303fe2090d8c3ca5dea39b010038050493175765 --- /dev/null +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/JsqlParserGlobal.java @@ -0,0 +1,89 @@ +/* + * 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.solon.parser; + +import com.baomidou.mybatisplus.solon.parser.cache.JsqlParseCache; +import lombok.Setter; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.Statements; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * @author miemie + * @since 2023-08-05 + */ +public class JsqlParserGlobal { + + /** + * 默认线程数大小 + * + * @since 3.5.6 + */ + public static final int DEFAULT_THREAD_SIZE = (Runtime.getRuntime().availableProcessors() + 1) / 2; + + /** + * 默认解析处理线程池 + *

注意: 由于项目情况,机器配置等不一样因素,请自行根据情况创建指定线程池.

+ * + * @see java.util.concurrent.ThreadPoolExecutor + * @since 3.5.6 + */ + public static ExecutorService executorService = new ThreadPoolExecutor(DEFAULT_THREAD_SIZE, DEFAULT_THREAD_SIZE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), r -> { + Thread thread = new Thread(r); + thread.setName("mybatis-plus-jsqlParser-" + thread.getId()); + thread.setDaemon(true); + return thread; + }); + + @Setter + private static JsqlParserFunction parserSingleFunc = sql -> CCJSqlParserUtil.parse(sql, executorService, null); + + @Setter + private static JsqlParserFunction parserMultiFunc = sql -> CCJSqlParserUtil.parseStatements(sql, executorService, null); + + @Setter + private static JsqlParseCache jsqlParseCache; + + public static Statement parse(String sql) throws JSQLParserException { + if (jsqlParseCache == null) { + return parserSingleFunc.apply(sql); + } + Statement statement = jsqlParseCache.getStatement(sql); + if (statement == null) { + statement = parserSingleFunc.apply(sql); + jsqlParseCache.putStatement(sql, statement); + } + return statement; + } + + public static Statements parseStatements(String sql) throws JSQLParserException { + if (jsqlParseCache == null) { + return parserMultiFunc.apply(sql); + } + Statements statements = jsqlParseCache.getStatements(sql); + if (statements == null) { + statements = parserMultiFunc.apply(sql); + jsqlParseCache.putStatements(sql, statements); + } + return statements; + } +} diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/JsqlParserSupport.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/JsqlParserSupport.java index b03feaf5a2e9bee77e942fddebe0be4f86b801b7..385da4f3200f715ed1d924b9a231ffb769daa270 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/JsqlParserSupport.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/JsqlParserSupport.java @@ -18,7 +18,6 @@ package com.baomidou.mybatisplus.solon.parser; import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils; import com.baomidou.mybatisplus.core.toolkit.StringPool; import net.sf.jsqlparser.JSQLParserException; -import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.Statements; import net.sf.jsqlparser.statement.delete.Delete; @@ -46,7 +45,7 @@ public abstract class JsqlParserSupport { logger.debug("original SQL: " + sql); } try { - Statement statement = CCJSqlParserUtil.parse(sql); + Statement statement = JsqlParserGlobal.parse(sql); return processParser(statement, 0, sql, obj); } catch (JSQLParserException e) { throw ExceptionUtils.mpe("Failed to process, Error SQL: %s", e.getCause(), sql); @@ -60,9 +59,9 @@ public abstract class JsqlParserSupport { try { // fixed github pull/295 StringBuilder sb = new StringBuilder(); - Statements statements = CCJSqlParserUtil.parseStatements(sql); + Statements statements = JsqlParserGlobal.parseStatements(sql); int i = 0; - for (Statement statement : statements.getStatements()) { + for (Statement statement : statements) { if (i > 0) { sb.append(StringPool.SEMICOLON); } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/AbstractCaffeineJsqlParseCache.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/AbstractCaffeineJsqlParseCache.java new file mode 100644 index 0000000000000000000000000000000000000000..a21c63bb61f224920f930376ae02dd18fc4e3719 --- /dev/null +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/AbstractCaffeineJsqlParseCache.java @@ -0,0 +1,121 @@ +/* + * 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.solon.parser.cache; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import lombok.Setter; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.Statements; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * jsqlparser 缓存 Caffeine 缓存实现抽象类 + * + * @author miemie hubin + * @since 2023-08-08 + */ +public abstract class AbstractCaffeineJsqlParseCache implements JsqlParseCache { + protected final Log logger = LogFactory.getLog(this.getClass()); + protected final Cache cache; + @Setter + protected boolean async = false; + @Setter + protected Executor executor; + + public AbstractCaffeineJsqlParseCache(Cache cache) { + this.cache = cache; + } + + public AbstractCaffeineJsqlParseCache(Consumer> consumer) { + Caffeine caffeine = Caffeine.newBuilder(); + consumer.accept(caffeine); + this.cache = caffeine.build(); + } + + @Override + public void putStatement(String sql, Statement value) { + this.put(sql, value); + } + + @Override + public void putStatements(String sql, Statements value) { + this.put(sql, value); + } + + @Override + public Statement getStatement(String sql) { + return this.get(sql); + } + + @Override + public Statements getStatements(String sql) { + return this.get(sql); + } + + /** + * 获取解析对象,异常清空缓存逻辑 + * + * @param sql 执行 SQL + * @return 返回泛型对象 + */ + protected T get(String sql) { + byte[] bytes = cache.getIfPresent(sql); + if (null != bytes) { + try { + return (T) deserialize(sql, bytes); + } catch (Exception e) { + cache.invalidate(sql); + logger.error("deserialize error", e); + } + } + return null; + } + + /** + * 存储解析对象 + * + * @param sql 执行 SQL + * @param value 解析对象 + */ + protected void put(String sql, Object value) { + if (async) { + if (executor != null) { + CompletableFuture.runAsync(() -> cache.put(sql, serialize(value)), executor); + } else { + CompletableFuture.runAsync(() -> cache.put(sql, serialize(value))); + } + } else { + cache.put(sql, serialize(value)); + } + } + + /** + * 序列化 + */ + public abstract byte[] serialize(Object obj); + + /** + * 反序列化 + */ + public abstract Object deserialize(String sql, byte[] bytes); + +} diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/FstFactory.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/FstFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..502d5eefcc9da6583c695b1e09de9140fb815e72 --- /dev/null +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/FstFactory.java @@ -0,0 +1,271 @@ +/* + * 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.solon.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.Function.HavingClause.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.LambdaExpression.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.StructType.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.ExcludesExpression.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.IncludesExpression.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/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/FstSerialCaffeineJsqlParseCache.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/FstSerialCaffeineJsqlParseCache.java new file mode 100644 index 0000000000000000000000000000000000000000..829381ce0ba0cd195e1b5aefba7bf60e169ef70e --- /dev/null +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/FstSerialCaffeineJsqlParseCache.java @@ -0,0 +1,49 @@ +/* + * 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.solon.parser.cache; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +import java.util.function.Consumer; + +/** + * jsqlparser 缓存 Fst 序列化 Caffeine 缓存实现 + * + * @author miemie + * @since 2023-08-05 + */ +public class FstSerialCaffeineJsqlParseCache extends AbstractCaffeineJsqlParseCache { + + + public FstSerialCaffeineJsqlParseCache(Cache cache) { + super(cache); + } + + public FstSerialCaffeineJsqlParseCache(Consumer> consumer) { + super(consumer); + } + + @Override + public byte[] serialize(Object obj) { + return FstFactory.getDefaultFactory().asByteArray(obj); + } + + @Override + public Object deserialize(String sql, byte[] bytes) { + return FstFactory.getDefaultFactory().asObject(bytes); + } +} diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/JdkSerialCaffeineJsqlParseCache.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/JdkSerialCaffeineJsqlParseCache.java new file mode 100644 index 0000000000000000000000000000000000000000..ca26a553259b8c662f19b583c8067e44c1efa9b2 --- /dev/null +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/JdkSerialCaffeineJsqlParseCache.java @@ -0,0 +1,50 @@ +/* + * 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.solon.parser.cache; + +import com.baomidou.mybatisplus.core.toolkit.SerializationUtils; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +import java.util.function.Consumer; + +/** + * jsqlparser 缓存 jdk 序列化 Caffeine 缓存实现 + * + * @author miemie + * @since 2023-08-05 + */ +public class JdkSerialCaffeineJsqlParseCache extends AbstractCaffeineJsqlParseCache { + + + public JdkSerialCaffeineJsqlParseCache(Cache cache) { + super(cache); + } + + public JdkSerialCaffeineJsqlParseCache(Consumer> consumer) { + super(consumer); + } + + @Override + public byte[] serialize(Object obj) { + return SerializationUtils.serialize(obj); + } + + @Override + public Object deserialize(String sql, byte[] bytes) { + return SerializationUtils.deserialize(bytes); + } +} diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/JsqlParseCache.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/JsqlParseCache.java new file mode 100644 index 0000000000000000000000000000000000000000..48922d51fbb7587870b100a1876aa136a4f182ee --- /dev/null +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/parser/cache/JsqlParseCache.java @@ -0,0 +1,36 @@ +/* + * 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.solon.parser.cache; + +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.Statements; + +/** + * jsqlparser 缓存接口 + * + * @author miemie + * @since 2023-08-05 + */ +public interface JsqlParseCache { + + void putStatement(String sql, Statement value); + + void putStatements(String sql, Statements value); + + Statement getStatement(String sql); + + Statements getStatements(String sql); +} diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/MybatisPlusInterceptor.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/MybatisPlusInterceptor.java index e203bfbcd2c5ea3ab582d743f9d0427085983ccf..4e43d27e7d9bed531025ddbad540f3e167d1c81b 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/MybatisPlusInterceptor.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/MybatisPlusInterceptor.java @@ -15,6 +15,7 @@ */ package com.baomidou.mybatisplus.solon.plugins; +import com.baomidou.mybatisplus.core.toolkit.ClassUtils; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.solon.plugins.inner.InnerInterceptor; import com.baomidou.mybatisplus.solon.toolkit.PropertyMapper; @@ -28,7 +29,6 @@ import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; -import org.noear.solon.core.util.ClassUtil; import java.sql.Connection; import java.util.*; @@ -139,9 +139,17 @@ public class MybatisPlusInterceptor implements Interceptor { PropertyMapper pm = PropertyMapper.newInstance(properties); Map group = pm.group(StringPool.AT); group.forEach((k, v) -> { - InnerInterceptor innerInterceptor = ClassUtil.tryInstance(k); + InnerInterceptor innerInterceptor = ClassUtils.newInstance(k); innerInterceptor.setProperties(v); addInnerInterceptor(innerInterceptor); }); } + + @Override + public String toString() { + return "MybatisPlusInterceptor{" + + "interceptors=" + interceptors + + '}'; + } + } diff --git a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/inner/BaseMultiTableInnerInterceptor.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/inner/BaseMultiTableInnerInterceptor.java index 60f8d9537087f72752d3da2b8e0bf150049bc37d..2d2ca6a801030e5c4912d7f71e95514cf6d3d32e 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/inner/BaseMultiTableInnerInterceptor.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/inner/BaseMultiTableInnerInterceptor.java @@ -24,9 +24,11 @@ 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.expression.operators.relational.ParenthesedExpressionList; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.select.*; @@ -46,18 +48,18 @@ import java.util.stream.Collectors; @SuppressWarnings({"rawtypes"}) public abstract class BaseMultiTableInnerInterceptor extends JsqlParserSupport implements InnerInterceptor { - protected void processSelectBody(SelectBody selectBody, final String whereSegment) { + protected void processSelectBody(Select selectBody, final String whereSegment) { if (selectBody == null) { return; } if (selectBody instanceof PlainSelect) { processPlainSelect((PlainSelect) selectBody, whereSegment); - } else if (selectBody instanceof WithItem) { - WithItem withItem = (WithItem) selectBody; - processSelectBody(withItem.getSubSelect().getSelectBody(), whereSegment); - } else { + } else if (selectBody instanceof ParenthesedSelect) { + ParenthesedSelect parenthesedSelect = (ParenthesedSelect) selectBody; + processSelectBody(parenthesedSelect.getSelect(), whereSegment); + } else if (selectBody instanceof SetOperationList) { SetOperationList operationList = (SetOperationList) selectBody; - List selectBodyList = operationList.getSelects(); + 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 = SystemMetaObject.forObject(boundSql.getParameterObject()); + MetaObject metaObject = PluginUtils.getMetaObject(boundSql.getParameterObject()); Map propertyValMap = new HashMap<>(boundSql.getParameterMappings().size()); for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) { String propertyName = parameterMapping.getProperty(); @@ -316,7 +518,13 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { 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("\":\""); @@ -334,31 +542,80 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { 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(Select selectStmt, Column pk, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) { + 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()) { - originalObjectDatas.add(prepareOriginalDataObj(resultSet, pk)); + ++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 * @@ -367,14 +624,20 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { * @return * @throws SQLException */ - private DataChangedRecord prepareOriginalDataObj(ResultSet resultSet, Column pk) 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 = DataColumnChangeResult.constrcutByOriginalVal(columnName, resultSet.getObject(i)); + 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 { @@ -390,24 +653,24 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { return changedRecord; } + private Columns2SelectItemsResult buildColumns2SelectItems(String tableName, List columns) { if (columns == null || columns.isEmpty()) { - return Columns2SelectItemsResult.build(Collections.singletonList(new AllColumns()), 0); + return Columns2SelectItemsResult.build(Collections.singletonList(new SelectItem<>(new AllColumns())), 0); } - List selectItems = new ArrayList<>(columns.size()); + List> selectItems = new ArrayList<>(columns.size()); for (Column column : columns) { - selectItems.add(new SelectExpressionItem(column)); + selectItems.add(new SelectItem<>(column)); } - for (TableInfo tableInfo : TableInfoHelper.getTableInfos()) { - if (tableName.equalsIgnoreCase(tableInfo.getTableName())) { - Column pk = new Column(tableInfo.getKeyColumn()); - selectItems.add(new SelectExpressionItem(pk)); - Columns2SelectItemsResult result = Columns2SelectItemsResult.build(selectItems, 1); - result.setPk(pk); - return result; - } + TableInfo tableInfo = getTableInfoByTableName(tableName); + if (tableInfo == null) { + return Columns2SelectItemsResult.build(selectItems, 0); } - 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) { @@ -445,13 +708,11 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { public OperationResult processDelete(Delete deleteStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) { Table table = deleteStmt.getTable(); Expression where = deleteStmt.getWhere(); - Select selectStmt = new Select(); PlainSelect selectBody = new PlainSelect(); selectBody.setFromItem(table); - selectBody.setSelectItems(Collections.singletonList(new AllColumns())); + selectBody.setSelectItems(Collections.singletonList(new SelectItem<>((new AllColumns())))); selectBody.setWhere(where); - selectStmt.setSelectBody(selectBody); - String originalData = buildOriginalData(selectStmt, mappedStatement, boundSql, connection); + String originalData = buildOriginalData(selectBody, mappedStatement, boundSql, connection); OperationResult result = new OperationResult(); result.setOperation("delete"); result.setTableName(table.getName()); @@ -460,6 +721,27 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { 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 * 多个表用分号分隔 @@ -513,30 +795,6 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { */ private long cost; - public String getTableName() { - return tableName; - } - - public void setTableName(String tableName) { - this.tableName = tableName; - } - - public String getOperation() { - return operation; - } - - public void setOperation(String operation) { - this.operation = operation; - } - - public boolean isRecordStatus() { - return recordStatus; - } - - public void setRecordStatus(boolean recordStatus) { - this.recordStatus = recordStatus; - } - public void buildDataStr(List records) { StringBuilder sb = new StringBuilder(); sb.append("["); @@ -554,15 +812,12 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("{"); - sb.append("\"").append("tableName").append("\"").append(":").append("\"").append(tableName).append("\"").append(","); - sb.append("\"").append("operation").append("\"").append(":").append("\"").append(operation).append("\"").append(","); - sb.append("\"").append("recordStatus").append("\"").append(":").append("\"").append(recordStatus).append("\"").append(","); - sb.append("\"").append("changedData").append("\"").append(":").append(changedData).append(","); - sb.append("\"").append("cost(ms)").append("\"").append(":").append(cost); - sb.append("}"); - return sb.toString(); + return "{" + + "\"tableName\":\"" + tableName + "\"," + + "\"operation\":\"" + operation + "\"," + + "\"recordStatus\":\"" + recordStatus + "\"," + + "\"changedData\":" + changedData + "," + + "\"cost(ms)\":" + cost + "}"; } } @@ -573,13 +828,13 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { /** * all column with additional columns: ID, etc. */ - private List selectItems; + private List> selectItems; /** * newly added column count from meta data. */ private int additionalItemCount; - public static Columns2SelectItemsResult build(List selectItems, int additionalItemCount) { + public static Columns2SelectItemsResult build(List> selectItems, int additionalItemCount) { Columns2SelectItemsResult result = new Columns2SelectItemsResult(); result.setSelectItems(selectItems); result.setAdditionalItemCount(additionalItemCount); @@ -608,28 +863,22 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { @SuppressWarnings("rawtypes") public boolean isDataChanged(Object updateValue) { if (!Objects.equals(originalValue, updateValue)) { - if (updateValue instanceof Number && originalValue instanceof Number) { - BigDecimal update = new BigDecimal(updateValue.toString()); - BigDecimal original = new BigDecimal(originalValue.toString()); - return update.compareTo(original) != 0; - } - if (updateValue instanceof Date && originalValue instanceof Date) { - Date update = (Date) updateValue; - Date original = (Date) originalValue; - return update.compareTo(original) != 0; - } 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; } - if (originalValue instanceof Comparable) { - Comparable original = (Comparable) originalValue; - Comparable update = (Comparable) updateValue; - return original.compareTo(update) != 0; - } return false; } @@ -682,6 +931,7 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { @Data public static class DataChangedRecord { + private String pkColumnName; private Object pkColumnVal; private List originalColumnDatas; @@ -728,4 +978,13 @@ public class DataChangeRecorderInnerInterceptor implements InnerInterceptor { 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/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/inner/DataPermissionInterceptor.java b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/inner/DataPermissionInterceptor.java index 728825e04931534b80ae5c6259303290e91bf1e9..974603488f51b2476a6b44cdcba94dbc90a54dc1 100644 --- a/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/inner/DataPermissionInterceptor.java +++ b/solon-plugin-data-sql/mybatis-plus-extension-solon-plugin/src/main/java/com/baomidou/mybatisplus/solon/plugins/inner/DataPermissionInterceptor.java @@ -15,30 +15,37 @@ */ package com.baomidou.mybatisplus.solon.plugins.inner; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; + +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 com.baomidou.mybatisplus.solon.plugins.handler.DataPermissionHandler; import com.baomidou.mybatisplus.solon.plugins.handler.MultiDataPermissionHandler; -import lombok.*; + +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.SelectBody; import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.select.WithItem; import net.sf.jsqlparser.statement.update.Update; -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 java.sql.Connection; -import java.sql.SQLException; -import java.util.List; /** * 数据权限处理器 @@ -53,8 +60,10 @@ import java.util.List; @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())) { @@ -80,13 +89,26 @@ public class DataPermissionInterceptor extends BaseMultiTableInnerInterceptor im @Override protected void processSelect(Select select, int index, String sql, Object obj) { - SelectBody selectBody = select.getSelectBody(); - if (selectBody instanceof PlainSelect) { - this.setWhere((PlainSelect) selectBody, (String) obj); - } else if (selectBody instanceof SetOperationList) { - SetOperationList setOperationList = (SetOperationList) selectBody; - List selectBodyList = setOperationList.getSelects(); - selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) 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