1. 程式人生 > >讓myql中的資料庫表自動生成javaBean的模板

讓myql中的資料庫表自動生成javaBean的模板

不積跬步,無以至千里

問題所在

今天重構以前的程式碼,因為資料庫有比較大的改動,所以需要寫很多javaBean!

作為一個程式設計師,重複意味著沒有效率,更意味著無聊,一旦無聊便沒有想程式設計的慾望,程式碼質量肯定刷刷刷的下降,質量下降了就意味著重寫。。。。

so,為了集中精力在業務邏輯的程式碼上,減少不必要的時間浪費,於是花了一點時間寫了一個把資料庫中表的欄位自動對映到 javaBean 中。

需求說明

輸入所生成 檔案的存放路徑、檔案的名稱(預設是表名)、包名(生成的是java檔案,一定要指定包名)、是否使用lombok外掛(考慮到自己實現getter/setter方法有一定的困難,直接藉助lombok外掛),判斷每一個表中的欄位的型別,生成一個標準的javaBean檔案

實現

依賴

  1. jdk1.8
  2. mysql-connector-java 驅動包

實現方法

  1. 使用mysql-connector-java連線需要操作的資料庫;
  2. 得到資料庫中所有的表名;
  3. 根據表名和資料庫名稱得到表的欄位以及欄位的型別;
  4. 根據表名和欄位的型別,生成目標檔案最重要的部分內容;
  5. 一個建立檔案並向檔案輸入內容的方法;
  6. 對上述方法進行封裝!
  7. 測試+完工;
  8. 記錄+完善。

假想的條件

  1. 資料庫的表名使用 下劃線區分各個單詞 來命名,eg: registed_user , approved_user;
  2. 資料庫中的欄位也如同表名的命名一樣;

show程式碼

檔案結構說明

在這裡插入圖片描述

ConnectionUtil.java

得到資料庫的連線

package connection;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionUtil {
    public static Connection getConn(String dbName, String username, String password) {
        String driver = "com.mysql.jdbc.Driver"
; String url = "jdbc:mysql://localhost:3306/"+dbName+"?useUnicode=true&characterEncoding=utf-8&useSSL=false"; Connection conn = null; try { Class.forName(driver); conn = (Connection) DriverManager.getConnection(url, username, password); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return conn; } }

MapperUtil.java

這裡寫的是上述需要封裝的所有方法

package mapper;

import connection.ConnectionUtil;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.sql.*;
import java.util.*;

public class MapperUtil {
    private DatabaseMetaData databaseMetaData;
    Connection connection;

    public MapperUtil(String dbName, String username, String password) throws SQLException {
        connection = ConnectionUtil.getConn(dbName, username, password);
        databaseMetaData = connection.getMetaData();
    }

    /**
     * 只是檔名(不包含副檔名)
     * @param fileName 檔名稱
     * @param dir 檔案所處的目錄
     * @param mainContent 檔案的主要內容
     * @param isLombok 是否包含lombok常見的一些方法?包含:不包含
     */
    public void createFile(String fileName, String dir, String packagePath, String mainContent, boolean isLombok) throws Exception {
        if (fileName == null || fileName.trim().equals("") || dir == null || dir.trim().equals("")){
            throw new Exception("傳入引數不正確");
        }
        /**
         * 1.首先檢測是否有這個資料夾路徑
         * 2.如果不存在則新建
         */
        File fileDir = new File(dir);
        if (!fileDir.exists()){
            fileDir.setWritable(true);
            fileDir.mkdirs();
        }

        /**
         * 如果沒有出錯,那麼建立檔案,這裡統一建立java檔案
         */
        File javaFile = new File(fileDir, fileName+".java");
        javaFile.createNewFile();
        
        /**
         * 向檔案中輸送內容
         * 使用
         */
        DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(javaFile)));
        dataOutputStream.writeBytes("package " + packagePath + ";\n");
        dataOutputStream.writeBytes("\n");
        if (isLombok){
            dataOutputStream.writeBytes(
                    "import lombok.*;\n\n"+
                    "@Data\n" +
                    "@ToString\n" +
                    "@Builder\n" +
                    "@AllArgsConstructor\n" +
                    "@NoArgsConstructor\n");
        }
        dataOutputStream.writeBytes("public class " + fileName + "{\n");
        dataOutputStream.writeBytes(mainContent);
        dataOutputStream.writeBytes("}");
        dataOutputStream.close();
    }

    /**
     * 構造對應的字串。
     * @param map
     * @return
     */
    public String generateString(Map<String, String> map){
        String result = "";
        Set set = map.entrySet();
        /**
         * 對map進行遍歷
         */
        for(Iterator iter = set.iterator(); iter.hasNext();)
        {
            Map.Entry entry = (Map.Entry)iter.next();
            String key = (String)entry.getKey();

            //把欄位的下劃線去掉,下劃線後一位變為大寫
            StringBuilder sb = new StringBuilder(key);
            for (int i = 1; i < key.length(); i++) {
                if (i+1 < key.length() && key.substring(i,i+1).equals("_")){
                    sb.replace(i, i+2, key.substring(i+1,i+2).toUpperCase());
                }
            }

            String value = (String)entry.getValue();
            System.out.println("key: " + key + " sb: " + sb +" :" + value);
            result += "    private " + value + " " + sb + ";\n";
        }
        return result;
    }



    /**
     * 返回欄位名稱和屬性
     * @param tableName
     * @return
     */
    public Map<String, String> getFiledAndType(String tableName) throws SQLException {
        if (tableName == null || tableName.trim().equals("")){return null;}

        ResultSet resultSet = connection.prepareStatement("select * from " + tableName).executeQuery();
        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
        //得到所有的列名資訊
        int count = resultSetMetaData.getColumnCount();
        Map<String, String> map = new HashMap<String, String>();
        for (int i = 1; i <= count; i++) {
            String type = resultSetMetaData.getColumnTypeName(i);
            type = typeTransform(type);
            map.put(resultSetMetaData.getColumnName(i), type);
        }
        return map;
    }

    /**
     * 得到所有表的名稱
     * @return 含有所有表名的列表
     */
    public List<String> getTableNames() throws SQLException {
        List<String> tables = new ArrayList<String>();
        ResultSet resultSet = databaseMetaData.getTables(null, null, "%", null);
        while (resultSet.next()){
            String tableName = resultSet.getString("TABLE_NAME");
            tables.add(tableName);
            System.out.println(tableName);
        }
        return tables;
    }

    /**
     * 型別轉換: 目前針對常見的幾個型別
     * @return
     */
    public String typeTransform(String sqlType){
        if (sqlType == null || sqlType.trim().equals("")){
            return null;
        }
        sqlType = sqlType.toLowerCase();
        if (matchFloatNumber(sqlType)){
            return "double";
        }else if (matchInt(sqlType)){
            return "int";
        }else if (matchString(sqlType)){
            return "String";
        }
        return "String";
    }

    public boolean match(String target, String[] datas) {
        if (target == null || "".equals(target.trim()) || datas.length == 0){
            return false;
        }
        for (String a:datas) {
            if (a.equals(target)){
                return true;
            }
        }
        return false;
    }

    public boolean matchString(String target){
        String[] datas = {"char", "varchar", "date", "text", "timestamp", "datetime", "tinytext", "longtext"};
        return match(target, datas);
    }

    public boolean matchFloatNumber(String target){
        String[] datas = {"float", "double"};
        return match(target, datas);
    }

    public boolean matchInt(String target){
        String[] datas = {"int", "tinyint"};
        return match(target, datas);
    }
}

MapperDo.java

封裝MapperUtil.java當中存在的方法

package mapper;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

public class MapperDo {
    private String dbName;
    private String username;
    private String password;
    private String baseDir;
    private String packagePath;
    private boolean isLombok;
    MapperUtil mapperUtil;

    public MapperDo(String dbName, String username, String password) throws SQLException {
        this.dbName = dbName;
        this.username = username;
        this.password = password;
        mapperUtil = new MapperUtil(dbName, username, password);
    }

    /**
     * 建立單個文字檔案
     * @param baseDir
     * @param packagePath
     * @param isLombok
     * @param tableName
     * @throws Exception
     */
    public void generateFileModel(String baseDir, String packagePath, boolean isLombok, final String tableName) throws Exception {
        List<String> list = mapperUtil.getTableNames();
        boolean isExist = list.stream().noneMatch((s) -> s.equals(tableName));
        if (isExist){
            return;
        }
        String fileName = toCamelCase(tableName);
        Map<String, String> map = mapperUtil.getFiledAndType(tableName);
        String mainContent = mapperUtil.generateString(map);
        mapperUtil.createFile(fileName, baseDir, packagePath, mainContent, true);
    }

    /**
     * 建立這個資料庫中所有的模板檔案、
     * @param baseDir
     * @param packagePath
     * @param isLombok
     */
    public void generateFileModels( String baseDir, String packagePath, boolean isLombok) throws Exception {
        List<String> list = mapperUtil.getTableNames();
        for (String tableName:list) {
            String fileName = toCamelCase(tableName);
            Map<String, String> map = mapperUtil.getFiledAndType(tableName);
            String mainContent = mapperUtil.generateString(map);
            mapperUtil.createFile(fileName, baseDir, packagePath, mainContent, true);
        }
    }

    public String toCamelCase(String s){
        if (s == null){
            return null;
        }
        /**
         * 1. 把全部轉成小寫
         * 2. 把下劃線全部去掉
         * 3. 把下劃線之後的一個字母變成大寫
         * 4. 首個字母變成大寫
         */
        StringBuilder sb = new StringBuilder(s.toLowerCase());
        for (int i = 1; i < sb.length(); i++) {
            if (i+1 < sb.length() && sb.substring(i,i+1).equals("_")){
                sb.replace(i, i+2, sb.substring(i+1,i+2).toUpperCase());
            }
        }
        sb.replace(0,1, s.substring(0,1).toUpperCase());
        return sb.toString();
    }
    /**
    *------------------此處省略getter/setter方法
    **/
}

Main.java

測試程式碼

package run;

import mapper.MapperDo;


public class Main {
    public static void main(String[] args) throws Exception {
        MapperDo mapperDo = new MapperDo("bonetest","root","root");
//測試生成資料庫中所有表的javaBean     	
		mapperDo.generateFileModels("/home/liudong/code/evaluate_bone/src/test/java/cn/edu/cqupt/mis/evaluate_bone/test", "liudong", false);
//測試生成advices一個表的javaBean
     	mapperDo.generateFileModel("/home/liudong/code/evaluate_bone/src/test/java/cn/edu/cqupt/mis/evaluate_bone/test", "liudong", false,"advices");
    }
}

測試的效果圖如下:

  1. 資料庫的結構 在這裡插入圖片描述
  2. advices表的結構 在這裡插入圖片描述
  3. 首先執行Main.java方法中單獨生成一個javaBean.java的方法 generateFileModel 在這裡插入圖片描述 4.執行另一個生成所有表的javaBean方法generateFileModels 在這裡插入圖片描述

總結

程式碼和邏輯雖然簡單,但是實現之後再次記錄,就會溫故而知新。 程式碼我會繼續完善,希望有問題大家可以一起探討探討。