【Java】数据库操作工具类

jdk 1.8
springboot 2.3.10

maven 用到的依赖

 <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.3.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>2.3.10.RELEASE</version>
        </dependency>

        <!-- mysql驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>compile</scope>
        </dependency>

首先用到获取数据表的类

import lombok.Data;

/**
 * 表格字段数据
 */
@Data
public class DatabaseTableField {
    private String tableCatalog;
    private String tableSchema;
    private String tableName;
    private String columnName;
    private Long ordinalPosition;
    private String columnDefault;
    private String isNullable;
    private String dataType;
    private Long characterMaximumLength;
    private Long characterOctetLength;
    private Long numericPrecision;
    private Long numericScale;
    private String datetimePrecisopm;
    private String characterSetName;
    private String collationName;
    private String columnType;
    private String columnKey;
    private String extra;
    private String privileges;
    private String columnComment;
}

关键的数据库工具类:DataBaseUtil

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 数据库工具,用于对数据库相关数据进行处理
 *
 * @author zhangxuetu
 * @datetime 2023-03-20
 */
@Component
public class DataBaseUtil {

    private static JdbcTemplate jdbcTemplate;

    @Autowired
    public DataBaseUtil(JdbcTemplate jdbcTemplate) {
        DataBaseUtil.jdbcTemplate = jdbcTemplate;
    }

    public static JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    /**
     * 如果没有使用 Spring 启动的话需要手动进行JDBC初始化配置
     *
     * @param configPath 配置文件路径
     */
    public static void initJdbcConfig(String configPath) {
        try {
            DataBaseUtil.jdbcTemplate = createJdbcTemplateByConfigFile(configPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据配置文件获取 JDBCTemplate 实例
     *
     * @param configPath 配置文件路径。例如 resources 文件夹下的 application.properties,直接传入 application.properties
     * @return 返回这个配置的 jdbc 代码
     */
    public static JdbcTemplate createJdbcTemplateByConfigFile(String configPath) throws IOException {
        Properties properties = new Properties();
        // 使用ClassLoader加载properties配置文件生成对应的输入流
        InputStream in = DataBaseUtil.class.getClassLoader().getResourceAsStream(configPath);
        if (in == null) {
            throw new IllegalArgumentException(configPath + "文件不存在,请检查文件路径是否正确!");
        }
        // 使用properties对象加载输入流
        properties.load(in);
        //获取key对应的value值
        String url = properties.getProperty("spring.data.url");
        String username = properties.getProperty("spring.data.username");
        String password = properties.getProperty("spring.data.password");
        String driverClassName = properties.getProperty("spring.data.driverClassName");

        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return new JdbcTemplate(dataSource);
    }

    /**
     * Sql 类型转换为 Java 类型
     *
     * @param sqlType sql数据类型
     * @return 返回对应的 Java 中的类型
     */
    public static String toJavaFormatType(String sqlType) {
        String type;
        switch (sqlType.toLowerCase(Locale.ROOT)) {
            case "int":
            case "tinyint":
            case "smallint":
                return "Integer";
            case "varchar":
            case "text":
            case "xmltype":
            case "longtext":
            case "char":
            case "varchar2":
                return "String";
            case "double":
                return "Double";
            case "float":
                return "Float";
            case "timestamp":
            case "datetime":
            case "date":
            case "time":
                return "Date";
            case "bigint":
                return "BigInteger";
            case "decimal":
                return "BigDecimal";
            default:
                return "Object";
        }
    }

    /**
     * 列表转为 Map。用于创建 key 和 value 值都相同的 Map
     *
     * @param list 字段列表。
     *             <p>字符串中如果有 “<code>:</code>” 英文冒号则会进行切分,冒号左边的为 key,冒号右边的为 value。</p>
     *             <p>没有则 key 和 value 都是这个值</p>
     */
    @SafeVarargs
    public static Map<String, String> listToMap(String... list) {
        return listToMap(Arrays.asList(list));
    }

    /**
     * 列表转为 Map。用于创建 key 和 value 值都相同的 Map
     *
     * @param list 字段列表。
     *             <p>字符串中如果有 “<code>:</code>” 英文冒号则会进行切分,冒号左边的为 key,冒号右边的为 value。</p>
     *             <p>没有则 key 和 value 都是这个值</p>
     */
    public static Map<String, String> listToMap(Collection<String> list) {
        Map<String, String> hashMap = new LinkedHashMap<>();
        for (String item : list) {
            String[] split = item.split(":");
            if (split.length == 1) {
                hashMap.put(item, item);
            } else {
                hashMap.put(split[0].trim(), split[1].trim());
            }
        }
        return hashMap;
    }

    /**
     * 对每行进行缩进
     *
     * @param code       代码
     * @param spaceChara 缩进空格字符串
     * @return 返回缩进后的字符串
     */
    public static String indent(String code, String spaceChara) {
        String[] split = code.split("\n");
        for (int i = 0; i < split.length; i++) {
            split[i] = spaceChara + split[i];
        }
        return String.join("\n", split);
    }


    //=======================================================================
    //            处理 JSON
    //=======================================================================

    /**
     * 对象转为 JSON 字符串
     */
    public static String objectToJson(Object object) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 字符串转为 Map 对象
     */
    public static <K, V> Map<K, V> jsonToMap(String json) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.readValue(json, Map.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }


    //=======================================================================
    //            数据库相关
    //=======================================================================

    /**
     * 查询表结构
     *
     * @param databaseName 数据库名
     * @param tableName    表名
     * @return 返回这个数据库中这个表的字段信息列表
     */
    public static List<DatabaseTableField> queryTablesStructure(String databaseName, String tableName) {
        String sql = "SELECT * " +
                "FROM INFORMATION_SCHEMA.COLUMNS " +
                "WHERE  TABLE_NAME='" + tableName + "' AND TABLE_SCHEMA='" + databaseName + "';";
        RowMapper<DatabaseTableField> rowMapper = new BeanPropertyRowMapper<>(DatabaseTableField.class);
        return jdbcTemplate.query(sql, rowMapper);
    }


    /**
     * 创建 Table SQL 代码
     *
     * @param tableName 表名
     * @param map       根据这个Map创建
     * @return 返回创建表SQL代码
     */
    public static String generateCreateTable(String tableName, Map<?, ?> map) {
        String code = "CREATE TABLE " + tableName + "(\n";
        code += map.keySet().stream()
                .map(key -> String.format("\t%s VARCHAR(100)", key.toString()))
                .collect(Collectors.joining(",\n"));
        code += "\n);\n";
        return code;
    }

    /**
     * 创建 Table SQL 代码
     *
     * @param tableName 表名
     * @param json      根据这个 Map 格式的 JSON 创建
     * @return 返回创建表SQL代码
     */
    public static String generateCreateTable(String tableName, String json) {
        Map<Object, Object> map = jsonToMap(json);
        if (map != null) {
            return generateCreateTable(tableName, map);
        }
        return "";
    }


    //=======================================================================
    //            Java 相关
    //=======================================================================

    /**
     * 生成实体类
     *
     * @param entityName          生成的实体类的名称
     * @param databaseTableFields 数据表字段信息列表
     * @return 返回生成的 Java 实体类代码
     */
    public static String generateEntityClass(String entityName, List<DatabaseTableField> databaseTableFields) {
        // 注解
        String entityCode = String.join("\n", Arrays.asList(
                "@Data", "@ApiModel(\"\")"
        )) + "\n";

        entityCode += String.format("public class %s {\n", entityName);
        // 字段列表
        entityCode += databaseTableFields.stream()
                .map(databaseTableField -> {
                    // 每个字段的格式
                    String code = "";
                    code += String.format("\t@ApiModelProperty(\"%s\")\n", databaseTableField.getColumnComment());
                    code += String.format("\tprivate %s %s;\n", toJavaFormatType(databaseTableField.getDataType()), databaseTableField.getColumnName());
                    return code;
                })
                .collect(Collectors.joining(""));
        entityCode += "}\n";

        // 导入包
        String importCode = String.join("\n", Arrays.asList(
                "import io.swagger.annotations.ApiModel;",
                "import io.swagger.annotations.ApiModelProperty;"
        )) + "\n";
        if (entityCode.contains("BigInteger") || entityCode.contains("BigDecimal")) {
            importCode += "import java.math.*;\n";
        }
        if (entityCode.contains("Date")) {
            importCode += "import java.util.*;\n";
        }

        return importCode + "\n" + entityCode;
    }


    //=======================================================================
    //            MyBatis相关
    //=======================================================================

    /**
     * 创建标签
     *
     * @param tagName 标签名
     * @param body    标签内容
     * @return 返回生成的标签
     */
    public static String createTag(String tagName, String body) {
        return createTag(tagName, null, body);
    }

    /**
     * 创建标签
     *
     * @param tagName 标签名
     * @param params  标签参数
     * @param body    标签内容
     * @return 返回生成的标签
     */
    public static String createTag(String tagName, Map<String, String> params, String body) {
        String paramsString = "";
        if (params != null && !params.isEmpty()) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                paramsString += String.format("%s=\"%s\" ", entry.getKey(), entry.getValue());
            }
        }
        return String.format("<%s %s>\n%s\n</%s>", tagName, paramsString, body, tagName);
    }

    /**
     * 生成 where 标签
     *
     * @param map 数据库字段对应的 Java 字段
     * @return 返回生成的 where 类型的 trim 标签
     */
    private static String generateWhereTag(Map<?, ?> map) {
        Map<String, String> tagParams = new LinkedHashMap<>();
        tagParams.put("prefix", "WHERE");
        tagParams.put("prefixOverrides", "AND");
        String body = String.join("", map.entrySet()
                .stream()
                .map(v -> "\t<if test=\"" + v.getValue() + "!=null\">AND " + v.getKey() + "=#{" + v.getValue() + "}</if>\n")
                .collect(Collectors.toList())
        );
        // 创建 where 版 trim 标签
        return createTag("trim", tagParams, "\t" + body.trim());
    }

    /**
     * 生成 set 标签
     *
     * @param map 表字段对应的Java类字段
     * @return 返回生成的 set 类型的 trim 标签
     */
    private static String generateSetTag(Map<String, String> map) {
        Map<String, String> tagParams = new LinkedHashMap<>();
        tagParams.put("prevfix", "SET");
        tagParams.put("suffixOverrides", ",");
        String body = String.join("", map.entrySet()
                .stream()
                .map(v -> "\t<if test=\"" + v.getValue() + "!=null\">" + v.getKey() + "=#{" + v.getValue() + "},</if>\n")
                .collect(Collectors.toList())
        );
        // 创建 set 版 trim 标签
        return createTag("trim", tagParams, "\t" + body.trim());
    }


    /**
     * 生成 Select Mybatis 代码
     *
     * @param tableName 表名
     * @return 返回生成的 select 标签代码
     */
    public static String generateMapperSelectTag(String tableName) {
        return createTag("select", "SELECT * FROM " + tableName);
    }

    /**
     * 生成 Select Mybatis 代码
     *
     * @param id         标签ID,即Mapper的方法名
     * @param resultType 返回结果 Java 类的类型
     * @param tableName  表名
     * @param where      WHERE条件中表字段对应Java类字段的名字,例如:{"role_name": "roleName"}
     * @return 返回生成的 select 标签代码
     */
    public static String generateMapperSelectTag(String id, String resultType, @NonNull String tableName, Map<String, String> where) {
        String sql = "SELECT * FROM " + tableName + " \n";
        if (!where.isEmpty()) {
            sql += generateWhereTag(where);
        }
        sql = indent(sql, "    ");

        Map<String, String> tagParams = new HashMap<>();
        tagParams.put("id", id);
        tagParams.put("resultType", resultType);
        return createTag("select", tagParams, sql);
    }

    /**
     * 生成 insert 标签代码
     *
     * @param id             标签ID,即Mapper的方法名
     * @param tableName      表名
     * @param sqlFieldNames  字段名列表
     * @param javaFieldNames 对应插入的Java字段列表
     * @return 返回生成的 insert 标签代码
     */
    public static String generateInsertTag(String id, String tableName, List<String> sqlFieldNames, List<String> javaFieldNames) {
        String sql = "INSERT INTO " + tableName + "( " + String.join(", ", sqlFieldNames) + " ) \n";
        sql += "VALUES ( ";
        sql += javaFieldNames.stream()
                .map(v -> "#{" + v + "}")
                .collect(Collectors.joining(", "));
        sql += " )";
        sql = indent(sql, "    ");

        Map<String, String> tagParams = new HashMap<>();
        tagParams.put("id", id);
        return createTag("insert", tagParams, sql);
    }

    /**
     * 生成 update 标签代码
     *
     * @param id        标签ID,即Mapper的方法名
     * @param tableName 表名
     * @param setMap    SET 字段列表
     * @param whereMap  WHERE 条件列表
     * @return 返回生成的 update 标签代码
     */
    public static String generateUpdateTag(String id, String tableName, Map<String, String> setMap, Map<String, String> whereMap) {
        String sql = "    UPDATE " + tableName + "\n";
        sql += indent(generateSetTag(setMap), "    ");
        sql += "\n" + indent(generateWhereTag(whereMap), "    ");

        Map<String, String> tagParams = new HashMap<>();
        tagParams.put("id", id);
        return createTag("update", tagParams, sql);
    }

    /**
     * 生成 delete 标签代码
     *
     * @param id        标签ID,即Mapper的方法名
     * @param tableName 表名
     * @param where     WHERE 条件列表
     * @return 返回生成的 delete 标签代码
     */
    public static String generateDeleteTag(String id, String tableName, Map<String, String> where) {
        String sql = "    DELETE FROM " + tableName + "\n";
        sql += indent(generateWhereTag(where), "    ");

        Map<String, String> tagParams = new HashMap<>();
        tagParams.put("id", id);
        return createTag("delete", tagParams, sql);
    }


    /**
     * 执行查询
     *
     * @param sql    执行SQL
     * @param params SQL里对应的 ? 字符串的值
     * @return 返回执行结果
     */
    public static List<Map<String, Object>> query(String sql, Object... params) {
        if (params.length == 0) {
            return jdbcTemplate.queryForList(sql);
        }
        return jdbcTemplate.queryForList(sql, params);
    }

    /**
     * 查询一个值
     *
     * @param sql    执行SQL
     * @param params SQL里对应的 ? 字符串的值
     * @return 返回执行结果
     */
    public static Map<String, Object> queryOne(String sql, Object... params) {
        return jdbcTemplate.queryForMap(sql, params);
    }


    /**
     * 执行更新
     *
     * @param sql    执行SQL
     * @param params SQL里对应的 ? 字符串的值
     * @return 返回执行结果
     */
    public static int update(String sql, Object... params) {
        if (params.length == 0) {
            return jdbcTemplate.update(sql);
        }
        return jdbcTemplate.update(sql, params);
    }

}

application.properties 配置文件

spring.data.driverClassName=com.mysql.cj.jdbc.Driver
spring.data.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.data.username=root
spring.data.password=123456

简单使用一下,测试用例:

import org.junit.Test;

import java.util.*;

/**
 * 数据库工具测试用例
 *
 * @author zhangxuetu
 * @date 2023-03-20
 */
public class DBTestCase {

    static {
        DataBaseUtil.initJdbcConfig("application.properties");
    }

    String databaseName = "test";
    String tableName = "location_info_863659043180750";
    String entityName = "location_info_863659043180750";

    @Test
    public void testGenerateEntity() {
        // 生成实体类
        List<DatabaseTableField> databaseTableFields = DataBaseUtil.queryTablesStructure(databaseName, databaseName);
        String code = DataBaseUtil.generateEntityClass(entityName, databaseTableFields);
        System.out.println(code);
    }

    @Test
    public void testCreateTable() {
        // 生成创建表SQL
        String sql = DataBaseUtil.generateCreateTable(databaseName + "." + tableName, "{\"a\": 1}");
        int count = DataBaseUtil.update(sql);
        System.out.println(count);

    }

    @Test
    public void testSelectTag() {
        // 生成 mybatis 的 select 标签
        Map<String, String> where = new HashMap<>();
        where.put("name", "userName");
        where.put("age", "age");
        String sql = DataBaseUtil.generateMapperSelectTag("queryTestData", entityName, tableName, where);
        System.out.println(sql);

    }

    @Test
    public void testInsertTag() {
        // 生成 mybatis 的 insert 标签
        String code = DataBaseUtil.generateInsertTag("insertTest", tableName,
                Arrays.asList("name", "aa", "fas"),
                Arrays.asList("name", "aa", "ccc")
        );
        System.out.println(code);
    }

    @Test
    public void testUpdateTag() {
        // 生成 mybatis 的 update 标签
        Map<String, String> setMap = DataBaseUtil.listToMap("username:userName", "create_time:createTime", "desc");
        Map<String, String> where = DataBaseUtil.listToMap("id");
        String code = DataBaseUtil.generateUpdateTag("updateTest", tableName, setMap, where);
        System.out.println(code);
    }

    @Test
    public void testDeleteTag() {
        // 生成 mybatis 的 delete 标签
        Map<String, String> where = new HashMap<>();
        where.put("id", "id");
        String code = DataBaseUtil.generateDeleteTag("deleteTag", tableName, where);
        System.out.println(code);
    }

    @Test
    public void testExecuteSql() {
        // 执行SQL
        List<Map<String, Object>> list = DataBaseUtil.query(
                "SELECT tag_code " +
                        "FROM test.location_info_863659043180750 " +
                        "WHERE tag_code=?",
                "863659043180750"
        );
        System.out.println(list);
    }

    @Test
    public void testExecuteSqlQueryOne() {
        // 执行查询单个对象 SQL
        String sql = "SELECT COUNT(0) AS 'count' " +
                "FROM test.location_info_863659043180750 " +
                "WHERE tag_code = ? " +
                "GROUP BY tag_code ";

        Map<String, Object> objectMap = DataBaseUtil.queryOne(sql, "863659043180750");

        // 转为 JSON 打印输出
        System.out.println(DataBaseUtil.objectToJson(objectMap));
    }

}

发表评论