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

103 阅读5分钟

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

1.优化之后的代码

1.1 PostgreSQLJdbcUtils

public class PostgreSQLJdbcUtils {
    //pg 驱动
    private static final String POSTGRES_DRIVER_CLASS = "org.postgresql.Driver";
    //pg url前缀
    private static final String POSTGRES_JDBC_PREFIX = "jdbc:postgresql";
    //pg的默认schema
    private static final String POSTGRESQL_DEFAULT_SCHEMA = "public";
    //日志
    private static final Logger LOG = LoggerFactory.getLogger(PostgreSQLJdbcUtils.class);

    /**
     * Get PostgreSQL connection from the url and user.
     *
     * @param url jdbc url, such as jdbc:mysql://host:port/database
     * @param user Username for JDBC URL
     * @param password User password
     * @return {@link Connection}
     * @throws Exception on get connection error
     */
    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.
     *
     * @param conn JDBC Connection  {@link Connection}
     * @param sql SQL string to be executed
     * @throws Exception on execute SQL error
     */
    public static void executeSql(final Connection conn, final String sql) throws Exception {
        try (Statement stmt = conn.createStatement()) {
            stmt.execute(sql);
            LOG.info("execute sql [{}] success", sql);
        }
    }

    /**
     * Execute batch query SQL on PostgreSQL.
     *
     * @param conn JDBC Connection  {@link Connection}
     * @param sqls SQL string to be executed
     * @throws Exception on get execute SQL batch error
     */
    public static void executeSqlBatch(final Connection conn, final List<String> sqls) throws Exception {
        conn.setAutoCommit(false);
        try (Statement stmt = conn.createStatement()) {
            for (String entry : sqls) {
                stmt.execute(entry);
            }
            conn.commit();
            LOG.info("execute sql [{}] success", sqls);
        } finally {
            conn.setAutoCommit(true);
        }
    }

    /**
     * Create PostgreSQL schema by schemaNama
     *
     * @param conn JDBC Connection  {@link Connection}
     * @param schemaName PostgreSQL schema name
     * @param userName PostgreSQL user name
     * @throws Exception on create schema error
     */
    public static void createSchema(final Connection conn, final String schemaName, final String userName)
            throws Exception {
        if (checkSchemaExist(conn, schemaName)) {
            LOG.info("the schema [{}] are exists", schemaName);
        } else {
            final String sql = PostgreSQLSqlBuilder.buildCreateSchema(schemaName, userName);
            executeSql(conn, sql);
            LOG.info("execute create schema sql [{}] success", sql);
        }
    }

    /**
     * Check whether the schema exists.
     *
     * @param conn JDBC Connection  {@link Connection}
     * @param schemaName PostgreSQL schema name
     * @return true if schema exist in the table, otherwise false
     * @throws Exception on check column exist error
     */
    public static boolean checkSchemaExist(final Connection conn, final String schemaName) throws Exception {
        boolean result = false;
        if (POSTGRESQL_DEFAULT_SCHEMA.equals(schemaName)) {
            result = true;
        } else {
            final String checkColumnSql = PostgreSQLSqlBuilder.getCheckSchema(schemaName);
            try (Statement statement = conn.createStatement();
                    ResultSet resultSet = statement.executeQuery(checkColumnSql)) {
                if (Objects.nonNull(resultSet) && resultSet.next()) {
                    int count = resultSet.getInt(1);
                    if (count > 0) {
                        result = true;
                    }
                }
            }
        }
        LOG.info("check schema exist for schema={}, result={}", schemaName, result);
        return result;
    }

    /**
     * Check whether the column exists in the table.
     *
     * @param conn JDBC Connection  {@link Connection}
     * @param schemaName PostgreSQL schema name
     * @param tableName PostgreSQL table name
     * @param column PostgreSQL table column name
     * @return true if column exist in the table, otherwise false
     * @throws Exception on check column exist error
     */
    public static boolean checkColumnExist(final Connection conn, final String schemaName, final String tableName,
            final String column) throws Exception {
        boolean result = false;
        final String checkColumnSql = PostgreSQLSqlBuilder.getCheckColumn(schemaName, tableName, column);
        try (Statement statement = conn.createStatement();
                ResultSet resultSet = statement.executeQuery(checkColumnSql)) {
            if (Objects.nonNull(resultSet) && resultSet.next()) {
                int count = resultSet.getInt(1);
                if (count > 0) {
                    result = true;
                }
            }
        }
        LOG.info("check column exist for table={}, column={}, result={}", tableName, column, result);
        return result;
    }

    /**
     * Create Greenplum table by GreenplumTableInfo
     *
     * @param conn JDBC Connection  {@link Connection}
     * @param tableInfo Greenplum table info  {@link PostgreSQLTableInfo}
     * @throws Exception on create table error
     */
    public static void createTable(final Connection conn, final PostgreSQLTableInfo tableInfo)
            throws Exception {
        if (checkTablesExist(conn, tableInfo.getSchemaName(), tableInfo.getTableName())) {
            LOG.info("the table [{}] are exists", tableInfo.getTableName());
        } else {
            final List<String> createTableSqls = PostgreSQLSqlBuilder.buildCreateTableSql(tableInfo);
            executeSqlBatch(conn, createTableSqls);
            LOG.info("execute sql [{}] success", createTableSqls);
        }
    }

    /**
     * Check tables from the Greenplum information_schema.
     *
     * @param conn JDBC Connection  {@link Connection}
     * @param schemaName PostgreSQL database name
     * @param tableName PostgreSQL table name
     * @return true if table exist, otherwise false
     * @throws Exception on check table exist error
     */
    public static boolean checkTablesExist(final Connection conn, final String schemaName, final String tableName)
            throws Exception {
        boolean result = false;
        final String checkTableSql = PostgreSQLSqlBuilder.getCheckTable(schemaName, tableName);
        try (Statement statement = conn.createStatement();
                ResultSet resultSet = statement.executeQuery(checkTableSql)) {
            if (null != resultSet && resultSet.next()) {
                int size = resultSet.getInt(1);
                if (size > 0) {
                    result = true;
                }
            }
        }
        LOG.info("check table exist for username={} table={}, result={}", schemaName, tableName, result);
        return result;
    }

    /**
     * Query all columns of the tableName.
     *
     * @param conn JDBC Connection  {@link Connection}
     * @param schemaName PostgreSQL schema name
     * @param tableName PostgreSQL table name
     * @return {@link List}
     * @throws Exception on get columns error
     */
    public static List<PostgreSQLColumnInfo> getColumns(final Connection conn, final String schemaName,
            final String tableName) throws Exception {
        final List<PostgreSQLColumnInfo> columnList = new ArrayList<>();
        final String querySql = PostgreSQLSqlBuilder.buildDescTableSql(schemaName, tableName);

        try (Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery(querySql)) {
            while (rs.next()) {
                columnList.add(new PostgreSQLColumnInfo(rs.getString(1), rs.getString(2),
                        rs.getString(3)));
            }
        }
        return columnList;
    }

    /**
     * Add columns for Greenpluum table.
     *
     * @param conn JDBC Connection  {@link Connection}
     * @param schemaName PostgreSQL schema name
     * @param tableName PostgreSQL table name
     * @param columns PostgreSQL columns to be added
     * @throws Exception on add columns error
     */
    public static void addColumns(final Connection conn, final String schemaName, final String tableName,
            final List<PostgreSQLColumnInfo> columns) throws Exception {
        final List<PostgreSQLColumnInfo> columnInfos = new ArrayList<>();

        for (PostgreSQLColumnInfo columnInfo : columns) {
            if (!checkColumnExist(conn, schemaName, tableName, columnInfo.getName())) {
                columnInfos.add(columnInfo);
            }
        }
        final List<String> addColumnSql = PostgreSQLSqlBuilder.buildAddColumnsSql(schemaName, tableName, columnInfos);
        executeSqlBatch(conn, addColumnSql);
        LOG.info("execute add columns sql [{}] success", addColumnSql);
    }
}

1.2 PostgreSQLSqlBuilder

public class PostgreSQLSqlBuilder {

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

    /**
     * Build SQL to check whether the table exists.
     *
     * @param schemaName PostgreSQL schema name
     * @param tableName PostgreSQL table name
     * @return the check table SQL string
     */
    public static String getCheckTable(final String schemaName, final String tableName) {
        final StringBuilder sqlBuilder = new StringBuilder()
                .append("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES  WHERE TABLE_SCHEMA = '")
                .append(schemaName)
                .append("' AND TABLE_TYPE = 'BASE TABLE' ")
                .append(" AND TABLE_NAME = '")
                .append(tableName)
                .append("' ;");
        LOGGER.info("check table sql: {}", sqlBuilder);
        return sqlBuilder.toString();
    }

    /**
     * Build SQL to check whether the column exists.
     *
     * @param schemaName PostgreSQL table name
     * @param columnName PostgreSQL column name
     * @return the check column SQL string
     */
    public static String getCheckColumn(final String schemaName, final String tableName, final String columnName) {
        final StringBuilder sqlBuilder = new StringBuilder()
                .append("SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS  WHERE TABLE_SCHEMA = '")
                .append(schemaName)
                .append("' AND TABLE_NAME = '")
                .append(tableName)
                .append("' AND COLUMN_NAME = '")
                .append(columnName)
                .append("' ;");
        LOGGER.info("check table sql: {}", sqlBuilder);
        return sqlBuilder.toString();
    }

    /**
     * Build SQL to check whether the schema exists.
     *
     * @param schemaName
     * @return
     */
    public static String getCheckSchema(final String schemaName) {
        return new StringBuilder()
                .append("SELECT COUNT(1) FROM INFORMATION_SCHEMA.SCHEMATA ")
                .append(" WHERE SCHEMA_NAME = '")
                .append(schemaName)
                .append("';")
                .toString();
    }

    /**
     * Build create PostgreSQL schema SQL String
     *
     * @param schemaName schema name
     * @param user user name
     * @return SQL String
     */
    public static String buildCreateSchema(final String schemaName, final String user) {
        return new StringBuilder()
                .append(" CREATE SCHEMA "")
                .append(schemaName)
                .append("" AUTHORIZATION "")
                .append(user)
                .append("";")
                .toString();
    }

    /**
     * Build create table SQL by PostgreSQLTableInfo.
     *
     * @param table PostgreSQL table info {@link PostgreSQLTableInfo}
     * @return the create table SQL String
     */
    public static List<String> buildCreateTableSql(final PostgreSQLTableInfo table) {
        final List<String> sqls = Lists.newArrayList();
        final StringBuilder createSql = new StringBuilder()
                .append("CREATE TABLE ").append(table.getSchemaName())
                .append("."")
                .append(table.getTableName())
                .append(""")
                .append(buildCreateColumnsSql(table));
        sqls.add(createSql.toString());

        // column comments
        sqls.addAll(getColumnsComment(table.getSchemaName(), table.getTableName(), table.getColumns()));
        // table comment
        if (StringUtils.isNotEmpty(table.getComment())) {
            sqls.add(getTableComment(table));
        }
        LOGGER.info("create table sql : {}", sqls);
        return sqls;
    }

    /**
     * Build columns comment SQLs
     *
     * @param tableName PostgreSQL table name
     * @param columns PostgreSQL colum list {@link PostgreSQLColumnInfo}
     * @return the SQL String list
     */
    private static List<String> getColumnsComment(final String schemaName, final String tableName,
            List<PostgreSQLColumnInfo> columns) {
        final List<String> commentList = new ArrayList<>();
        for (PostgreSQLColumnInfo columnInfo : columns) {
            if (StringUtils.isNoneBlank(columnInfo.getComment())) {
                StringBuilder commSql = new StringBuilder();
                commSql.append("COMMENT ON COLUMN "")
                        .append(schemaName)
                        .append(""."")
                        .append(tableName)
                        .append(""."")
                        .append(columnInfo.getName())
                        .append("" IS '")
                        .append(columnInfo.getComment())
                        .append("' ;");
                commentList.add(commSql.toString());
            }
        }
        return commentList;
    }

    /**
     * Build table comment SQL
     *
     * @param tableInfo PostgreSQL table info {@link PostgreSQLTableInfo}
     * @return the SQL String
     */
    private static String getTableComment(final PostgreSQLTableInfo tableInfo) {
        return new StringBuilder()
                .append("COMMENT ON TABLE "")
                .append(tableInfo.getSchemaName())
                .append(""."")
                .append(tableInfo.getTableName())
                .append("" IS '")
                .append(tableInfo.getComment())
                .append("';")
                .toString();
    }

    /**
     * Build add columns SQL.
     *
     * @param schemaName PostgreSQL schema name
     * @param tableName PostgreSQL table name
     * @param columnList PostgreSQL column list {@link List}
     * @return add column SQL string list
     */
    public static List<String> buildAddColumnsSql(final String schemaName, final String tableName,
            List<PostgreSQLColumnInfo> columnList) {
        final List<String> resultList = Lists.newArrayList();
        final StringBuilder sqlBuilder = new StringBuilder();

        columnList.forEach(columnInfo -> {
            sqlBuilder.append("ALTER TABLE "")
                    .append(schemaName)
                    .append(""."")
                    .append(tableName)
                    .append("" ADD "")
                    .append(columnInfo.getName())
                    .append("" ")
                    .append(columnInfo.getType())
                    .append(" ");
            resultList.add(sqlBuilder.toString());
            sqlBuilder.delete(0, sqlBuilder.length());
        });
        resultList.addAll(getColumnsComment(schemaName, tableName, columnList));
        LOGGER.info("add columns sql={}", resultList);
        return resultList;
    }

    /**
     * Build create column SQL.
     *
     * @param table PostgreSQL table info {@link PostgreSQLTableInfo}
     * @return create column SQL string
     */
    private static String buildCreateColumnsSql(final PostgreSQLTableInfo table) {
        final List<String> columnList = getColumnsInfo(table.getColumns());
        final StringBuilder sql = new StringBuilder()
                .append(" (")
                .append(StringUtils.join(columnList, ","));
        if (!StringUtils.isEmpty(table.getPrimaryKey())) {
            sql.append(", PRIMARY KEY (")
                    .append(table.getPrimaryKey())
                    .append(")");
        }
        sql.append(") ");
        return sql.toString();
    }

    /**
     * Build column info by PostgreSQLColumnInfo list.
     *
     * @param columns PostgreSQL column info {@link PostgreSQLColumnInfo} list
     * @return the SQL list
     */
    private static List<String> getColumnsInfo(final List<PostgreSQLColumnInfo> columns) {
        final List<String> columnList = new ArrayList<>();
        final StringBuilder columnBuilder = new StringBuilder();

        columns.forEach(columnInfo -> {
            columnBuilder.append(""")
                    .append(columnInfo.getName())
                    .append("" ")
                    .append(columnInfo.getType());
            columnList.add(columnBuilder.toString());
            columnBuilder.delete(0, columnBuilder.length());
        });
        return columnList;
    }

    /**
     * Build query table's all cloumn SQL.
     *
     * @param schemaName PostgreSQL schema name
     * @param tableName PostgreSQL table name
     * @return desc table SQL string
     */
    public static String buildDescTableSql(final String schemaName, final String tableName) {
        StringBuilder sql = new StringBuilder().append(
                        "SELECT A.COLUMN_NAME,A.UDT_NAME,C.DESCRIPTION FROM INFORMATION_SCHEMA.COLUMNS A")
                .append(" LEFT JOIN   (SELECT PC.OID AS OOID,PN.NSPNAME,PC.RELNAME")
                .append(" FROM PG_CLASS PC LEFT OUTER JOIN PG_NAMESPACE PN ON PC.RELNAMESPACE = PN.OID ")
                .append(" WHERE PN.NSPNAME ='")
                .append(schemaName)
                .append("' AND PC.RELNAME = '")
                .append(tableName)
                .append("') B   ON A.TABLE_SCHEMA = B.NSPNAME AND A.TABLE_NAME = B.RELNAME")
                .append(" LEFT JOIN PG_CATALOG.PG_DESCRIPTION C ")
                .append("ON B.OOID = C.OBJOID AND A.ORDINAL_POSITION = C.OBJSUBID")
                .append(" WHERE A.TABLE_SCHEMA = '")
                .append(schemaName)
                .append("' AND A.TABLE_NAME = '")
                .append(tableName)
                .append("'  ORDER BY  C.OBJSUBID ;");
        LOGGER.info("desc table sql={}", sql);
        return sql.toString();
    }
}

2.主要改进

对之前代码的主要改进在于,首先定义了一个获取JDBC连接的方法。调用其他的方法时,必须首先获取连接。使用完毕之后,通过try-with-resource自动关闭连接。这样连接可以复用。 调用方式如下:

 public void testDbResource() {
        String url = "jdbc:postgresql://localhost:5432/testdb";
        String username = "pguser";
        String password = "123456";
        String tableName = "test01";
        String schemaName = "public";

        try (Connection connection = PostgreSQLJdbcUtils.getConnection(url, username, password)) {
            PostgreSQLTableInfo tableInfo = bulidTestPostgreSQLTableInfo(username, schemaName, tableName);
            PostgreSQLJdbcUtils.createTable(connection, tableInfo);
            List<PostgreSQLColumnInfo> addColumns = buildAddColumns();
            PostgreSQLJdbcUtils.addColumns(connection, schemaName, tableName, addColumns);
            List<PostgreSQLColumnInfo> columns = PostgreSQLJdbcUtils.getColumns(connection, schemaName, tableName);
            Assertions.assertEquals(columns.size(), tableInfo.getColumns().size() + addColumns.size());
        } catch (Exception e) {
            // print to local console
            e.printStackTrace();
        }
    }

此外,考虑到对于PostgreSQL数据库而言,数据库的dbName在连接字符串中必须携带,否则不能创建连接。 因此不用考虑db是否已创建,只需要考虑schema的创建情况。如果不使用默认的public schema的话,PostgreSQL也可以自定义schema,对于PostgreSQL而言,表建立在schema之下。对于不同的schema可以创建相同的表。