对一段JDBC代码的优化(上)

479 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

1.源码

有JDBC连接PostgreSQL的代码如下:

public class PostgreSQLJdbcUtils {

    private static final String POSTGRES_DRIVER_CLASS = "org.postgresql.Driver";
    private static final String SCHEMA_PATTERN = "public";
    private static final String TABLE_TYPE = "TABLE";
    private static final String COLUMN_LABEL_TABLE = "TABLE_NAME";
    private static final String COLUMN_LABEL_COUNT = "count";
    private static final String POSTGRES_JDBC_PREFIX = "jdbc:postgresql";

    private static final Logger LOG = LoggerFactory.getLogger(PostgreSQLJdbcUtils.class);

    /**
     * Get PostgreSQL connection from the url and user
     */
    public static Connection getConnection(String url, String user, String password)
            throws Exception {
        if (StringUtils.isBlank(url) || !url.startsWith(POSTGRES_JDBC_PREFIX)) {
            throw new Exception("PostgreSQL server URL was invalid, it should start with jdbc:postgresql");
        }
        Connection conn;
        try {
            Class.forName(POSTGRES_DRIVER_CLASS);
            conn = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            String errorMsg = "get postgresql connection error, please check postgresql jdbc url, username or password";
            LOG.error(errorMsg, e);
            throw new Exception(errorMsg + ": " + e.getMessage());
        }
        if (conn == null) {
            throw new Exception("get postgresql connection failed, please contact administrator");
        }
        LOG.info("get postgresql connection success, url={}", url);
        return conn;
    }

    /**
     * Execute SQL command on PostgreSQL
     *
     * @return true if execute successfully
     */
    public static boolean executeSql(String sql, String url, String user, String password) throws Exception {
        try (Connection conn = getConnection(url, user, password)) {
            Statement stmt = conn.createStatement();
            LOG.info("execute sql [{}] success for url: {}", sql, url);
            return stmt.execute(sql);
        }
    }

    /**
     * Execute query SQL on PostgreSQL
     *
     * @return the query result set
     */
    public static ResultSet executeQuerySql(String sql, String url, String user, String password)
            throws Exception {
        try (Connection conn = getConnection(url, user, password)) {
            Statement stmt = conn.createStatement();
            LOG.info("execute sql [{}] success for url: {}", sql, url);
            return stmt.executeQuery(sql);
        }
    }

    /**
     * Execute batch SQL commands on PostgreSQL
     */
    public static void executeSqlBatch(List<String> sql, String url, String user, String password)
            throws Exception {
        try (Connection conn = getConnection(url, user, password)) {
            Statement stmt = conn.createStatement();
            for (String entry : sql) {
                stmt.execute(entry);
            }
            LOG.info("execute sql [{}] success for url: {}", sql, url);
        }
    }

    /**
     * Create PostgreSQL database
     */
    public static void createDb(String url, String user, String password, String dbName) throws Exception {
        String checkDbSql = PostgreSQLSqlBuilder.getCheckDatabase(dbName);
        ResultSet resultSet = executeQuerySql(checkDbSql, url, user, password);
        if (resultSet != null) {
            resultSet.next();
            if (resultSet.getInt(COLUMN_LABEL_COUNT) == 0) {
                String createDbSql = PostgreSQLSqlBuilder.buildCreateDbSql(dbName);
                executeSql(createDbSql, url, user, password);
            }
        }
    }

    /**
     * Create PostgreSQL table
     */
    public static void createTable(String url, String user, String password, PostgreSQLTableInfo tableInfo)
            throws Exception {
        String createTableSql = PostgreSQLSqlBuilder.buildCreateTableSql(tableInfo);
        PostgreSQLJdbcUtils.executeSql(createTableSql, url, user, password);
    }

    /**
     * Get PostgreSQL tables from the PostgreSQL metadata
     */
    public static boolean checkTablesExist(String url, String user, String password, String dbName, String tableName)
            throws Exception {
        boolean result = false;
        try (Connection conn = getConnection(url, user, password)) {
            DatabaseMetaData metaData = conn.getMetaData();
            ResultSet rs = metaData.getTables(conn.getCatalog(), SCHEMA_PATTERN, tableName, new String[]{TABLE_TYPE});
            if (rs != null) {
                rs.next();
                result = rs.getRow() > 0 && tableName.equals(rs.getString(COLUMN_LABEL_TABLE));
                LOG.info("check table exist for db={} table={}, result={}", dbName, tableName, result);
            }
        }
        return result;
    }

    /**
     * Query PostgreSQL columns
     */
    public static List<PostgreSQLColumnInfo> getColumns(String url, String user, String password, String tableName)
            throws Exception {
        String querySql = PostgreSQLSqlBuilder.buildDescTableSql(tableName);
        try (Connection conn = getConnection(url, user, password);
                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery(querySql)) {
            List<PostgreSQLColumnInfo> columnList = new ArrayList<>();

            while (rs.next()) {
                PostgreSQLColumnInfo columnInfo = new PostgreSQLColumnInfo();
                columnInfo.setName(rs.getString(1));
                columnInfo.setType(rs.getString(2));
                columnList.add(columnInfo);
            }
            return columnList;
        }
    }

    /**
     * Add columns for PostgreSQL table
     */
    public static void addColumns(String url, String user, String password, String tableName,
            List<PostgreSQLColumnInfo> columnList) throws Exception {
        List<String> addColumnSql = PostgreSQLSqlBuilder.buildAddColumnsSql(tableName, columnList);
        PostgreSQLJdbcUtils.executeSqlBatch(addColumnSql, url, user, password);
    }

}

拼接sql的代码如下:

public class PostgreSQLSqlBuilder {

    private static final Logger LOGGER = LoggerFactory.getLogger(PostgreSQLSqlBuilder.class);

    /**
     * Build check database exists SQL
     */
    public static String getCheckDatabase(String dbName) {
        String sql = "SELECT datname FROM from pg_catalog.pg_database WHERE datname = '" + dbName + "'";
        LOGGER.info("check database sql: {}", sql);
        return sql;
    }

    /**
     * Build create database SQL
     */
    public static String buildCreateDbSql(String dbName) {
        String sql = "CREATE DATABASE " + dbName;
        LOGGER.info("create db sql: {}", sql);
        return sql;
    }

    /**
     * Build create table SQL
     */
    public static String buildCreateTableSql(PostgreSQLTableInfo table) {
        StringBuilder sql = new StringBuilder();
        // Support _ beginning with underscore
        String dbTableName = table.getTableName();
        sql.append("CREATE TABLE ").append(dbTableName);

        // Construct columns and partition columns
        sql.append(buildCreateColumnsSql(table.getColumns()));

        LOGGER.info("create table sql: {}", sql);
        return sql.toString();
    }

    /**
     * Build add column SQL
     */
    public static List<String> buildAddColumnsSql(String tableName, List<PostgreSQLColumnInfo> columnList) {
        List<String> columnInfoList = getColumnsInfo(columnList);
        List<String> resultList = new ArrayList<>();
        for (String columnInfo : columnInfoList) {
            String sql = "ALTER TABLE " + tableName + " ADD COLUMN " + columnInfo;
            resultList.add(sql);
        }

        LOGGER.info("add columns sql={}", resultList);
        return resultList;
    }

    /**
     * Build create column SQL
     */
    private static String buildCreateColumnsSql(List<PostgreSQLColumnInfo> columns) {
        List<String> columnList = getColumnsInfo(columns);
        String sql = " (" + StringUtils.join(columnList, ",") + ") ";
        LOGGER.info("create columns sql={}", sql);
        return sql;
    }

    /**
     * Build column info
     */
    private static List<String> getColumnsInfo(List<PostgreSQLColumnInfo> columns) {
        List<String> columnList = new ArrayList<>();
        for (PostgreSQLColumnInfo columnInfo : columns) {
            // Construct columns and partition columns
            String columnStr = columnInfo.getName() + " " + columnInfo.getType();
            columnList.add(columnStr);
        }
        return columnList;
    }

    /**
     * Build query table SQL
     */
    public static String buildDescTableSql(String tableName) {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT att.attname as filedName, format_type(att.atttypid, att.atttypmod) as filedType"
                        + " FROM pg_attribute as att, pg_class as clz"
                        + " WHERE att.attrelid = clz.oid and att.attnum > 0 and clz.relname = '")
                .append(tableName)
                .append("';");

        LOGGER.info("desc table sql={}", sql);
        return sql.toString();
    }

}

在上述的代码中,存在不少问题。首先可以通过单元测试代码进行验证,单元测试代码如下:

@Test
public void testDbResource() {
    String url = "jdbc:postgresql://192.168.162.136:5432/testdb";
    String user = "jie_li";
    String password = "123456";
    String dbName = "testdb";
    String tableName = "test001";

    try {
        PostgreSQLJdbcUtils.createDb(url, user, password, dbName);
        List<PostgreSQLColumnInfo> columnInfoList = new ArrayList<>();
        PostgreSQLColumnInfo info = new PostgreSQLColumnInfo();
        info.setType("integer");
        info.setName("id");
        columnInfoList.add(info);
        PostgreSQLColumnInfo info2 = new PostgreSQLColumnInfo();
        info2.setType("integer");
        info2.setName("age");
        columnInfoList.add(info2);

        PostgreSQLColumnInfo info3 = new PostgreSQLColumnInfo();
        info3.setType("integer");
        info3.setName("high");
        columnInfoList.add(info3);

        PostgreSQLTableInfo tableInfo = new PostgreSQLTableInfo();
        tableInfo.setDbName(dbName);
        tableInfo.setColumns(columnInfoList);
        tableInfo.setTableName(tableName);

        boolean tableExists = PostgreSQLJdbcUtils.checkTablesExist(url, user, password, dbName, tableName);
        if (!tableExists) {
            PostgreSQLJdbcUtils.createTable(url, user, password, tableInfo);
        } else {
            List<PostgreSQLColumnInfo> existColumns = PostgreSQLJdbcUtils.getColumns(url, user, password,
                    tableName);
            List<String> columnNameList = new ArrayList<>();
            existColumns.forEach(columnInfo -> columnNameList.add(columnInfo.getName()));

            List<PostgreSQLColumnInfo> needAddColumns = tableInfo.getColumns().stream()
                    .filter((columnInfo) -> !columnNameList.contains(columnInfo.getName()))
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(needAddColumns)) {
                PostgreSQLJdbcUtils.addColumns(url, user, password, tableName, needAddColumns);
            }
        }
    } catch (Exception e) {
        // print to local console
        e.printStackTrace();
    }
}

2. 发现的问题

2.1 检查表是否存在sql拼接错误

执行单元测试案例,出现如下异常:

23:20:44.761 - INFO [ main] .m.s.r.s.p.PostgreSQLJdbcUtils:94 - execute sql [SELECT datname FROM from pg_catalog.pg_database WHERE datname = 'testdb'] success for url: jdbc:postgresql://192.168.162.136:5432/testdb
org.postgresql.util.PSQLException: ERROR: syntax error at or near "from"
  位置:21
	at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2676)
	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2366)
	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:356)
	at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:490)
	at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:408)
	at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:329)
	at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:315)
	at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:291)
	at org.postgresql.jdbc.PgStatement.executeQuery(PgStatement.java:243)
	at org.apache.inlong.manager.service.resource.sink.postgresql.PostgreSQLJdbcUtils.executeQuerySql(PostgreSQLJdbcUtils.java:95)
	at org.apache.inlong.manager.service.resource.sink.postgresql.PostgreSQLJdbcUtils.createDb(PostgreSQLJdbcUtils.java:118)
	at org.apache.inlong.manager.service.sink.PostgreSQLSinkServiceTest.testDbResource(PostgreSQLSinkServiceTest.java:120)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

首先可以看出,jdbc拼接的sql中,在方法createDb的executeQuerySql方法的时候,出现了异常。getCheckDatabase方法出现了两个from:

image.png 修复这个问题非常简单,就是删除多余的from。

2.2 ResultSet 中找不到栏位名称 count

2022-08-07 23:25:48.464 - INFO [ main] .m.s.r.s.p.PostgreSQLJdbcUtils:94 - execute sql [SELECT datname FROM pg_catalog.pg_database WHERE datname = 'testdb'] success for url: jdbc:postgresql://192.168.162.136:5432/testdb
org.postgresql.util.PSQLException: ResultSet 中找不到栏位名称 countat org.postgresql.jdbc.PgResultSet.findColumn(PgResultSet.java:2957)
	at org.postgresql.jdbc.PgResultSet.getInt(PgResultSet.java:2835)
	at org.apache.inlong.manager.service.resource.sink.postgresql.PostgreSQLJdbcUtils.createDb(PostgreSQLJdbcUtils.java:121)
	at org.apache.inlong.manager.service.sink.PostgreSQLSinkServiceTest.testDbResource(PostgreSQLSinkServiceTest.java:120)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
2022-08-07 23:25:48.525 - INFO [SpringApplicationShutdownHook] c.a.d.p.DruidDataSource       :2043 - {dataSource-1} closing ...
2022-08-07 23:25:48.541 - INFO [SpringApplicationShutdownHook] c.a.d.p.DruidDataSource       :2116 - {dataSource-1} closed

Process finished with exit code 0

image.png 在上述代码中,查询的结果集内容中不存在count,由于sql语句中查询的是dbname ,因此在判断ResultSet的过程中存在错误。

image.png 此处修改过程也简单,不需要判断返回的count,直接判断是否存在返回结果即可,修复之后的代码如下:

public static void createDb(String url, String user, String password, String dbName) throws Exception {
    String checkDbSql = PostgreSQLSqlBuilder.getCheckDatabase(dbName);
    ResultSet resultSet = executeQuerySql(checkDbSql, url, user, password);
        if(Objects.nonNull(resultSet) && resultSet.next()) {
            LOG.info("The database [{}] are exists", dbName);
        }else {
            String createDbSql = PostgreSQLSqlBuilder.buildCreateDbSql(dbName);
            executeSql(createDbSql, url, user, password);
        }
}

改为上述代码,即可。

2.3 创建DB权限问题

需要注意的是,如果需要创建数据库,在PostgreSQL中,可能存在用户并没有创建DB权限的问题。 image.png PG的数据库连接字符串必须带DB名,如果创建其他DB,可能会出现问题。而schema在PG中是对同一个库的不同管理手段。因此这个地方可能需要优化为创建schema。而DB默认在连接字符串中,创建DB没有意义。

2.4 连接复用问题

当上述代码执行成功之后,日志如下:

2022-08-07 23:44:53.694 - INFO [ main] m.s.r.s.p.PostgreSQLSqlBuilder:41 - check database sql: SELECT datname FROM pg_catalog.pg_database WHERE datname = 'testdb'
2022-08-07 23:44:53.811 - INFO [ main] .m.s.r.s.p.PostgreSQLJdbcUtils:69 - get postgresql connection success, url=jdbc:postgresql://192.168.162.136:5432/testdb
2022-08-07 23:44:53.811 - INFO [ main] .m.s.r.s.p.PostgreSQLJdbcUtils:95 - execute sql [SELECT datname FROM pg_catalog.pg_database WHERE datname = 'testdb'] success for url: jdbc:postgresql://192.168.162.136:5432/testdb
2022-08-07 23:44:53.843 - INFO [ main] .m.s.r.s.p.PostgreSQLJdbcUtils:121 - The database [testdb] are exists
2022-08-07 23:44:53.875 - INFO [ main] .m.s.r.s.p.PostgreSQLJdbcUtils:69 - get postgresql connection success, url=jdbc:postgresql://192.168.162.136:5432/testdb
2022-08-07 23:44:54.117 - INFO [ main] .m.s.r.s.p.PostgreSQLJdbcUtils:149 - check table exist for db=testdb table=test001, result=true
2022-08-07 23:44:54.117 - INFO [ main] m.s.r.s.p.PostgreSQLSqlBuilder:119 - desc table sql=SELECT att.attname as filedName, format_type(att.atttypid, att.atttypmod) as filedType FROM pg_attribute as att, pg_class as clz WHERE att.attrelid = clz.oid and att.attnum > 0 and clz.relname = 'test001';
2022-08-07 23:44:54.150 - INFO [ main] .m.s.r.s.p.PostgreSQLJdbcUtils:69 - get postgresql connection success, url=jdbc:postgresql://192.168.162.136:5432/testdb
2022-08-07 23:44:54.549 - INFO [SpringApplicationShutdownHook] c.a.d.p.DruidDataSource       :2043 - {dataSource-1} closing ...
2022-08-07 23:44:54.549 - INFO [SpringApplicationShutdownHook] c.a.d.p.DruidDataSource       :2116 - {dataSource-1} closed

可以看到,数据库连接在使用的过程中,创建了至少三次。由于JDBC的数据库连接也是非常宝贵的资源。因此数据库连接是可以复用的。应该只创建一次连接,后面的操作复用,当连接使用完毕之后,将连接回收。