Pārlūkot izejas kodu

优化DDL执行记录表检查.

聂秋荣 1 mēnesi atpakaļ
vecāks
revīzija
a2eee49c5b

+ 1 - 0
.gitignore

@@ -29,3 +29,4 @@ gradlew.bat
 
 # kotlin
 .kotlin
+/mybatis-plus-extension/test.db

+ 3 - 0
mybatis-plus-extension/build.gradle

@@ -20,5 +20,8 @@ dependencies {
     testImplementation "${lib."spring-context-support"}"
     testImplementation "${lib.h2}"
     testImplementation "${lib.mysql}"
+    testImplementation "${lib.postgresql}"
+    testImplementation "${lib.oracle}"
+    testImplementation "${lib.sqlite}"
     testImplementation "${lib.'logback-classic'}"
 }

+ 1 - 10
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/DdlHelper.java

@@ -20,7 +20,6 @@ import com.baomidou.mybatisplus.core.toolkit.StringPool;
 import com.baomidou.mybatisplus.extension.ddl.history.*;
 import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
 import org.apache.ibatis.io.Resources;
-import org.apache.ibatis.jdbc.RuntimeSqlException;
 import org.apache.ibatis.jdbc.ScriptRunner;
 import org.apache.ibatis.jdbc.SqlRunner;
 import org.apache.ibatis.logging.Log;
@@ -108,7 +107,6 @@ public class DdlHelper {
     public static void runScript(IDdlGenerator ddlGenerator, Connection connection, List<String> sqlFiles, Consumer<ScriptRunner> scriptRunnerConsumer,
                                  boolean autoCommit, DdlScriptErrorHandler ddlScriptErrorHandler) throws SQLException {
         final String jdbcUrl = connection.getMetaData().getURL();
-        final String schema = DdlHelper.getDatabase(jdbcUrl);
         SqlRunner sqlRunner = new SqlRunner(connection);
         ScriptRunner scriptRunner = getScriptRunner(connection, autoCommit);
         if (scriptRunnerConsumer != null) {
@@ -117,14 +115,7 @@ public class DdlHelper {
         if (null == ddlGenerator) {
             ddlGenerator = getDdlGenerator(jdbcUrl);
         }
-        if (!ddlGenerator.existTable(schema, sql -> {
-            try {
-                Map<String, Object> resultMap = sqlRunner.selectOne(sql);
-                return null != resultMap && !StringPool.ZERO.equals(String.valueOf(resultMap.get(StringPool.NUM)));
-            } catch (SQLException e) {
-                throw new RuntimeSqlException("Check exist table error:", e);
-            }
-        })) {
+        if (!ddlGenerator.existTable(connection)) {
             scriptRunner.runScript(new StringReader(ddlGenerator.createDdlHistory()));
         }
         // 执行 SQL 脚本

+ 24 - 1
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/IDdlGenerator.java

@@ -15,6 +15,10 @@
  */
 package com.baomidou.mybatisplus.extension.ddl.history;
 
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
 import java.util.function.Function;
 
 /**
@@ -31,8 +35,27 @@ public interface IDdlGenerator {
      * @param databaseName    数据库名称
      * @param executeFunction 执行判断函数
      * @return exist or no
+     * @deprecated 3.5.13 {@link #existTable(Connection)}
      */
-    boolean existTable(String databaseName, Function<String, Boolean> executeFunction);
+    @Deprecated
+    default boolean existTable(String databaseName, Function<String, Boolean> executeFunction) {
+        return false;
+    }
+
+    /**
+     *
+     * 检查{@link #getDdlHistory()}表是否存在
+     * @since 3.5.13
+     * @param connection 数据库连接
+     * @return 是否存在
+     * @throws SQLException SQLException
+     */
+    default boolean existTable(Connection connection) throws SQLException {
+        DatabaseMetaData metaData = connection.getMetaData();
+        try (ResultSet resultSet = metaData.getTables(connection.getCatalog(), connection.getSchema(), getDdlHistory(), new String[]{"TABLE"})) {
+            return resultSet.next();
+        }
+    }
 
     /**
      * 返回 DDL_HISTORY 表名

+ 82 - 3
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/PostgreDdlGenerator.java

@@ -15,8 +15,14 @@
  */
 package com.baomidou.mybatisplus.extension.ddl.history;
 
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import lombok.Setter;
 
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
 import java.util.function.Function;
 
 
@@ -28,10 +34,58 @@ import java.util.function.Function;
  */
 public class PostgreDdlGenerator implements IDdlGenerator {
 
+    /**
+     * schema模式(默认:public)
+     * <p>为了兼容,默认使用public,但指定为null时,使用jdbc指定的schema</p>
+     * @since 3.5.13
+     */
+    @Setter
+    private String schema = "public";
+
+    /**
+     * @deprecated 3.5.13 {@link #newInstance}
+     */
+    @Deprecated
+    public PostgreDdlGenerator() {
+    }
+
+    /**
+     * 创建PostgreDdlGenerator实例
+     * @since 3.5.13
+     * @param schema schema (可为null,当为null时为自动识别数据库连接的schema)
+     */
+    public PostgreDdlGenerator(String schema) {
+        this.schema = schema;
+    }
+
+    /**
+     * 默认实例 (基于public模式)
+     * @return PostgreDdlGenerator
+     */
     public static IDdlGenerator newInstance() {
-        return new PostgreDdlGenerator();
+        return newInstanceWithSchema("public");
+    }
+
+    /**
+     * 手动指定schema
+     * @param schema schema
+     * @since 3.5.13
+     *  @return PostgreDdlGenerator
+     */
+    public static IDdlGenerator newInstanceWithSchema(String schema) {
+        return new PostgreDdlGenerator(schema);
+    }
+
+    /**
+     * 基于数据库连接自动识别schema
+     * @since 3.5.13
+     * @return PostgreDdlGenerator
+     */
+    public static IDdlGenerator newInstanceWithAutoSchema() {
+        return new PostgreDdlGenerator(StringPool.EMPTY);
     }
 
+
     @Override
     public boolean existTable(String databaseName, Function<String, Boolean> executeFunction) {
         StringBuffer sql = new StringBuffer();
@@ -42,9 +96,28 @@ public class PostgreDdlGenerator implements IDdlGenerator {
         return executeFunction.apply(sql.toString());
     }
 
+    @Override
+    public boolean existTable(Connection connection) throws SQLException {
+        DatabaseMetaData metaData = connection.getMetaData();
+        String tableName = getDdlHistory();
+        int index = tableName.lastIndexOf(StringPool.DOT);
+        if (index > 0) {
+            tableName = tableName.substring(index + 1);
+        }
+        tableName = tableName.replace(StringPool.QUOTE, StringPool.EMPTY);
+        try (ResultSet resultSet = metaData.getTables(connection.getCatalog(),
+            StringUtils.isBlank(getSchema()) ? connection.getSchema() : getSchema(), tableName, new String[]{"TABLE"})) {
+            return resultSet.next();
+        }
+    }
+
     @Override
     public String getDdlHistory() {
-        return "\"" + this.getSchema() + "\".\"ddl_history\"";
+        String schema = getSchema();
+        if (StringUtils.isNotBlank(schema)) {
+            return "\"" + schema + "\".\"ddl_history\"";
+        }
+        return "\"ddl_history\"";
     }
 
     @Override
@@ -63,7 +136,13 @@ public class PostgreDdlGenerator implements IDdlGenerator {
         return sql.toString();
     }
 
+    /**
+     * @return scheme
+     * @deprecated 3.5.13 指定请使用 {@link #setSchema(String)}
+     */
+    @Deprecated
     protected String getSchema() {
-        return "public";
+        return schema;
     }
+
 }

+ 120 - 0
mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/DdlHelperTest.java

@@ -0,0 +1,120 @@
+package com.baomidou.mybatisplus.test;
+
+import com.baomidou.mybatisplus.extension.ddl.DdlHelper;
+import com.baomidou.mybatisplus.extension.ddl.DdlScriptErrorHandler;
+import com.baomidou.mybatisplus.extension.ddl.history.IDdlGenerator;
+import com.baomidou.mybatisplus.extension.ddl.history.MysqlDdlGenerator;
+import com.baomidou.mybatisplus.extension.ddl.history.OracleDdlGenerator;
+import com.baomidou.mybatisplus.extension.ddl.history.PostgreDdlGenerator;
+import com.baomidou.mybatisplus.extension.ddl.history.SQLiteDdlGenerator;
+import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ *
+ * @author nieqiurong
+ */
+public class DdlHelperTest {
+
+    @Test
+    @Disabled
+    void testForMysql() throws SQLException {
+        var dataSource = new UnpooledDataSource(com.mysql.cj.jdbc.Driver.class.getName(),
+            "jdbc:mysql://127.0.0.1:3306/baomidou?serverTimezone=Asia/Shanghai",
+            "root", "123456");
+        var ddlGenerator = new MysqlDdlGenerator();
+        DdlHelper.runScript(ddlGenerator, dataSource, List.of("ddl/test.sql"),
+            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);
+    }
+
+    @Test
+    @Disabled
+    void testForPostgresql() throws SQLException {
+        var dataSource = new UnpooledDataSource(org.postgresql.Driver.class.getName(),
+            "jdbc:postgresql://localhost:5432/postgres",
+            "postgres", "123456");
+        IDdlGenerator ddlGenerator = new PostgreDdlGenerator();
+        DdlHelper.runScript(ddlGenerator, dataSource, List.of("ddl/test.sql"),
+            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);
+        // 指定scheme运行 旧版本的模式是指定 public,为了兼容当使用默认的示例是无法根据指定的模式走的
+        dataSource = new UnpooledDataSource(org.postgresql.Driver.class.getName(),
+            "jdbc:postgresql://localhost:5432/postgres?currentSchema=baomidou",
+            "postgres", "123456");
+        DdlHelper.runScript(ddlGenerator, dataSource, List.of("ddl/test.sql"),
+            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);
+    }
+
+    @Test
+    @Disabled
+    void testForPostgresqlForAuto() throws SQLException {
+        var dataSource = new UnpooledDataSource(org.postgresql.Driver.class.getName(),
+            "jdbc:postgresql://localhost:5432/postgres",
+            "postgres", "123456");
+        IDdlGenerator ddlGenerator = PostgreDdlGenerator.newInstanceWithAutoSchema();
+        DdlHelper.runScript(ddlGenerator, dataSource, List.of("ddl/test.sql"),
+            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);
+
+        // 指定scheme运行
+        dataSource = new UnpooledDataSource(org.postgresql.Driver.class.getName(),
+            "jdbc:postgresql://localhost:5432/postgres?currentSchema=baomidou",
+            "postgres", "123456");
+        DdlHelper.runScript(ddlGenerator, dataSource, List.of("ddl/test.sql"),
+            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);
+
+    }
+
+    @Test
+    @Disabled
+    void testForOracle() throws SQLException {
+        var dataSource = new UnpooledDataSource(oracle.jdbc.driver.OracleDriver.class.getName(),
+            "jdbc:oracle:thin:@127.0.0.1:1521:orcl",
+            "system", "123456");
+        var ddlGenerator = new OracleDdlGenerator();
+        DdlHelper.runScript(ddlGenerator, dataSource, List.of("ddl/test.sql"),
+            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);
+    }
+
+    @Test
+    void testForSQLite() throws SQLException {
+        var dataSource = new UnpooledDataSource(org.sqlite.JDBC.class.getName(),
+            "jdbc:sqlite:test.db",
+            "", "");
+        var ddlGenerator = new SQLiteDdlGenerator();
+        DdlHelper.runScript(ddlGenerator, dataSource, List.of("ddl/test.sql"),
+            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);
+    }
+
+    @Test
+    void testForH2() throws SQLException {
+        var dataSource = new UnpooledDataSource(org.sqlite.JDBC.class.getName(),
+            "jdbc:h2:mem:test;DATABASE_TO_LOWER=TRUE",
+            "sa", "");
+        DdlHelper.runScript(null, dataSource, List.of("ddl/test.sql"),
+            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);
+    }
+
+    @Test
+    void testForH2Mysql() throws SQLException {
+        var dataSource = new UnpooledDataSource(org.sqlite.JDBC.class.getName(),
+            "jdbc:h2:mem:test;MODE=MySQL",
+            "sa", "");
+        var ddlGenerator = new MysqlDdlGenerator();
+        DdlHelper.runScript(ddlGenerator, dataSource, List.of("ddl/test.sql"),
+            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);
+    }
+
+    @Test
+    void testForH2Postgresql() throws SQLException {
+        var dataSource = new UnpooledDataSource(org.sqlite.JDBC.class.getName(),
+            "jdbc:h2:mem:test;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE",
+            "sa", "");
+        var ddlGenerator = new PostgreDdlGenerator();
+        DdlHelper.runScript(ddlGenerator, dataSource, List.of("ddl/test.sql"),
+            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);
+    }
+
+}

+ 1 - 0
mybatis-plus-extension/src/test/resources/ddl/test.sql

@@ -0,0 +1 @@
+-- run sql test;