通過jdbc獲取數據庫中的表結構 主鍵 各個表字段類型及應用生成實體類
1、JDBC中通過MetaData來獲取具體的表的相關信息。可以查詢數據庫中的有哪些表,表有哪些字段,字段的屬性等等。MetaData中通過一系列getXXX函數,將這些信息存放到ResultSet里面,然后返回給用戶。關於MetaData的說明網上也有不少,這里我只是從我自身學習的角度來記錄一下簡單使用JDBC以及獲取數據表相關信息的方法。
DatabaseMetaData dbmd = con.getMetaData(); rs = dbmd.getColumns(con.getCatalog(), schema, tableName, null); rs.getString(DATA_TYPE) // java.sql.Types 的 SQL 類型 rs.getString(COLUMN_SIZE) //列的大小。對於 char 或 date 類型,列的大小是最大字符數,對於 numeric 和 decimal 類型,列的大小就是精度。 rs.getString(DECIMAL_DIGITS) //小數部分的位數
2、下面就是我的JDBC下的獲取表信息的代碼了。我是以MySQL 5.0作為測試平台的。可以通過下面四個步驟來實現:
//1. JDBC連接MYSQL的代碼很標准。 class.forName("com.mysql.jdbc.Driver").newInstance(); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test?user=root&password=123456"); //2. 下面就是獲取表的信息。 m_DBMetaData = m_Connection.getMetaData(); ResultSet tableRet = m_DBMetaData.getTables(null, "%",m_TableName,new String[]{"TABLE"}); /*其中"%"就是表示*的意思,也就是任意所有的意思。其中m_TableName就是要獲取的數據表的名字,如果想獲取所有的表的名字,就可以使用"%"來作為參數了。*/ //3. 提取表的名字。 while(tableRet.next) System.out.println(tableRet.getString("TABLE_NAME")); /*通過getString("TABLE_NAME"),就可以獲取表的名字了。 從這里可以看出,前面通過getTables的接口的返回,JDBC是將其所有的結果,保存在一個類似table的內存結構中,而其中TABLE_NAME這個名字的字段就是每個表的名字。*/ //4. 提取表內的字段的名字和類型 String columnName; String columnType; ResultSet colRet = m_DBMetaData.getColumns(null,"%", m_TableName,"%"); while(colRet.next()) { columnName = colRet.getString("COLUMN_NAME"); columnType = colRet.getString("TYPE_NAME"); int datasize = colRet.getInt("COLUMN_SIZE"); int digits = colRet.getInt("DECIMAL_DIGITS"); int nullable = colRet.getInt("NULLABLE"); System.out.println(columnName+" "+columnType+" "+datasize+" "+digits+" "+ nullable); } /*JDBC里面通過getColumns的接口,實現對字段的查詢。跟getTables一樣,"%"表示所有任意的(字段),而m_TableName就是數據表的名字。 getColumns的返回也是將所有的字段放到一個類似的內存中的表,而COLUMN_NAME就是字段的名字,TYPE_NAME就是數據類型,比如"int","int unsigned"等等,COLUMN_SIZE返回整數,就是字段的長度,比如定義的int(8)的字段,返回就是8,最后NULLABLE,返回1就表示可以是Null,而0就表示Not Null。*/
每個列描述都有以下列: TABLE_CAT String => 表類別(可為 null) TABLE_SCHEM String => 表模式(可為 null) TABLE_NAME String => 表名稱 COLUMN_NAME String => 列名稱 DATA_TYPE int => 來自 java.sql.Types 的 SQL 類型 TYPE_NAME String => 數據源依賴的類型名稱,對於 UDT,該類型名稱是完全限定的 COLUMN_SIZE int => 列的大小。 BUFFER_LENGTH 未被使用。 DECIMAL_DIGITS int => 小數部分的位數。對於 DECIMAL_DIGITS 不適用的數據類型,則返回 Null。 NUM_PREC_RADIX int => 基數(通常為 10 或 2) NULLABLE int => 是否允許使用 NULL。 columnNoNulls - 可能不允許使用 NULL 值 columnNullable - 明確允許使用 NULL 值 columnNullableUnknown - 不知道是否可使用 null REMARKS String => 描述列的注釋(可為 null) COLUMN_DEF String => 該列的默認值,當值在單引號內時應被解釋為一個字符串(可為 null) SQL_DATA_TYPE int => 未使用 SQL_DATETIME_SUB int => 未使用 CHAR_OCTET_LENGTH int => 對於 char 類型,該長度是列中的最大字節數 ORDINAL_POSITION int => 表中的列的索引(從 1 開始) IS_NULLABLE String => ISO 規則用於確定列是否包括 null。 YES --- 如果參數可以包括 NULL NO --- 如果參數不可以包括 NULL 空字符串 --- 如果不知道參數是否可以包括 null SCOPE_CATLOG String => 表的類別,它是引用屬性的作用域(如果 DATA_TYPE 不是 REF,則為 null) SCOPE_SCHEMA String => 表的模式,它是引用屬性的作用域(如果 DATA_TYPE 不是 REF,則為 null) SCOPE_TABLE String => 表名稱,它是引用屬性的作用域(如果 DATA_TYPE 不是 REF,則為 null) SOURCE_DATA_TYPE short => 不同類型或用戶生成 Ref 類型、來自 java.sql.Types 的 SQL 類型的源類型(如果 DATA_TYPE 不是 DISTINCT 或用戶生成的 REF,則為 null) IS_AUTOINCREMENT String => 指示此列是否自動增加 YES --- 如果該列自動增加 NO --- 如果該列不自動增加 空字符串 --- 如果不能確定該列是否是自動增加參數 COLUMN_SIZE 列表示給定列的指定列大小。對於數值數據,這是最大精度。對於字符數據,這是字符長度。對於日期時間數據類型,這是 String 表示形式的字符長度(假定允許的最大小數秒組件的精度)。對於二進制數據,這是字節長度。對於 ROWID 數據類型,這是字節長度。對於列大小不適用的數據類型,則返回 Null。 參數: catalog - 類別名稱;它必須與存儲在數據庫中的類別名稱匹配;該參數為 "" 表示獲取沒有類別的那些描述;為 null 則表示該類別名稱不應該用於縮小搜索范圍 schemaPattern - 模式名稱的模式;它必須與存儲在數據庫中的模式名稱匹配;該參數為 "" 表示獲取沒有模式的那些描述;為 null 則表示該模式名稱不應該用於縮小搜索范圍 tableNamePattern - 表名稱模式;它必須與存儲在數據庫中的表名稱匹配 columnNamePattern - 列名稱模式;它必須與存儲在數據庫中的列名稱匹配
3、獲取所有表
String catalog = conn.getCatalog(); //catalog 其實也就是數據庫名 ResultSet tablesResultSet = dbMetaData.getTables(catalog,null,null,new String[]{"TABLE"}); while(tablesResultSet.next()){ String tableName = tablesResultSet.getString("TABLE_NAME"); }
tablesResultSet 中有以下列:
TABLE_CAT String => 表類別(可為 null) TABLE_SCHEM String => 表模式(可為 null) TABLE_NAME String => 表名稱 TABLE_TYPE String => 表類型。典型的類型是 "TABLE"、"VIEW"、"SYSTEM TABLE"、"GLOBAL TEMPORARY"、"LOCAL TEMPORARY"、"ALIAS" 和 "SYNONYM"。 REMARKS String => 表的解釋性注釋 TYPE_CAT String => 類型的類別(可為 null) TYPE_SCHEM String => 類型模式(可為 null) TYPE_NAME String => 類型名稱(可為 null) SELF_REFERENCING_COL_NAME String => 有類型表的指定 "identifier" 列的名稱(可為 null) REF_GENERATION String => 指定在 SELF_REFERENCING_COL_NAME 中創建值的方式。這些值為 "SYSTEM"、"USER" 和 "DERIVED"。(可能為 null)
4、某個表的主鍵
String tableName = ...; ResultSet primaryKeyResultSet = dbMetaData.getPrimaryKeys(catalog,null,tableName); while(primaryKeyResultSet.next()){ String primaryKeyColumnName = primaryKeyResultSet.getString("COLUMN_NAME"); }
primayKeyResultSet 有以下幾列: TABLE_CAT String => 表類別(可為 null) TABLE_SCHEM String => 表模式(可為 null) TABLE_NAME String => 表名稱 COLUMN_NAME String => 列名稱 KEY_SEQ short => 主鍵中的序列號(值 1 表示主鍵中的第一列,值 2 表示主鍵中的第二列)。 PK_NAME String => 主鍵的名稱(可為 null)
5、某個表的外鍵
ResultSet foreignKeyResultSet = dbMetaData.getImportedKeys(catalog,null,tableName); while(foreignKeyResultSet.next()){ String fkColumnName = foreignKeyResultSet.getString("FKCOLUMN_NAM"); String pkTablenName = foreignKeyResultSet.getString("PKTABLE_NAME"); String pkColumnName = foreignKeyResultSet.getString("PKCOLUMN_NAME"); }
foreignKeyResultSet 有以下幾列: PKTABLE_CAT String => 被導入的主鍵表類別(可為 null) PKTABLE_SCHEM String => 被導入的主鍵表模式(可為 null) PKTABLE_NAME String => 被導入的主鍵表名稱 PKCOLUMN_NAME String => 被導入的主鍵列名稱 FKTABLE_CAT String => 外鍵表類別(可為 null) FKTABLE_SCHEM String => 外鍵表模式(可為 null) FKTABLE_NAME String => 外鍵表名稱 FKCOLUMN_NAME String => 外鍵列名稱 KEY_SEQ short => 外鍵中的序列號(值 1 表示外鍵中的第一列,值 2 表示外鍵中的第二列) UPDATE_RULE short => 更新主鍵時外鍵發生的變化 DELETE_RULE short => 刪除主鍵時外鍵發生的變化 PK_NAME String => 主鍵的名稱(可為 null) FK_NAME String => 外鍵的名稱(可為 null) DEFERRABILITY short => 是否可以將對外鍵約束的評估延遲到提交時間
6、應用:
大多數數據庫有許多主鍵,但是在一個表中不允許兩條記錄的同一個主鍵具有相同的值。可以使用Java Database Connectivity(JDBC)來判斷一個數據表的主鍵。 JDBC具有強大的元數據處理能力。java.sql.Connection類和java.sql.ResultSet類可以通過調用其getMetaData方法進行反射。可以通過下面兩個方法:
//這兩個方法都可以獲取主外鍵信息,只是參照物不同 metaData.getExportedKeys("數據庫名稱", "schema", "表名"); metaData.getImportedKeys(catalog, null, tablename);
//Sql Server private static String url = "jdbc:sqlserver://192.168.1.220:1433;User=admin;Password=123456;DatabaseName=Person";
package cn.test; import java.io.File; import java.io.FileOutputStream; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.ResultSet; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; public class TestAll { private static String path = "D:\\tool\\project\\DynamicTable\\src\\cn\\test\\entity"; private static String pkname = "com.mysql.jdbc.Driver"; private static String url = "jdbc:mysql://192.168.1.220:3306/Person"; private static String[] classNames = new String[] { "ShipStopping", "ArriveShip", "TBLUserType" }; private static Map<String, String> fkTableNamesAndPk = new HashMap<String, String>(); public static void main(String[] args) { test(); } public static void test() { Connection conn = null; DatabaseMetaData metaData = null; ResultSet rs = null; ResultSet crs = null; try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); conn = DriverManager.getConnection(url, "admin", "123"); String catalog = conn.getCatalog(); // catalog 其實也就是數據庫名 metaData = conn.getMetaData(); File dirFile = new File(path); if (!dirFile.exists()) { dirFile.mkdirs(); } // 獲取表 rs = metaData.getTables(null, "%", "%", new String[] { "TABLE" }); while (rs.next()) { String tablename = rs.getString("TABLE_NAME"); String classname = getClassNameByTableName(tablename); StringBuffer sb = new StringBuffer(); StringBuffer sbpackage = new StringBuffer(); sbpackage.append("package cn.test.entity;\r\n\r\n"); sbpackage.append("import javax.persistence.Column;\r\n"); sbpackage.append("import javax.persistence.Entity;\r\n"); sbpackage.append("import javax.persistence.GeneratedValue;\r\n"); sbpackage.append("import javax.persistence.Id;\r\n"); sbpackage.append("import javax.persistence.Table;\r\n\r\n"); sb.append("\r\n@Entity\r\n"); sb.append("@Table(name = \"" + tablename + "\")\r\n"); sb.append("public class " + classname + " implements java.io.Serializable {\r\n"); // 獲取當前表的列 crs = metaData.getColumns(null, "%", tablename, "%"); // 獲取被引用的表,它的主鍵就是當前表的外鍵 fkTableNamesAndPk.clear(); ResultSet foreignKeyResultSet = metaData.getImportedKeys(catalog, null, tablename); while (foreignKeyResultSet.next()) { String pkTablenName = foreignKeyResultSet.getString("PKTABLE_NAME"); // 外鍵表 String fkColumnName = foreignKeyResultSet.getString("FKCOLUMN_NAME"); // 外鍵 if (!fkTableNamesAndPk.containsKey(fkColumnName)) fkTableNamesAndPk.put(fkColumnName, pkTablenName); } // foreignKeyResultSet.close(); while (crs.next()) { String columnname = crs.getString("COLUMN_NAME"); String columntype = crs.getString("TYPE_NAME"); System.out.println("--------------------------"+ columntype); if (existFKColumn(columnname)) { String fkclassname = getClassNameByTableName(fkTableNamesAndPk.get(columnname)); sbpackage.append("import " + pkname + "." + fkclassname+ ";\r\n"); sb.append("\t/** */\r\n"); sb.append("\tprivate " + fkclassname + " " + columnname+ ";\r\n"); } else { sb.append("\t/** */\r\n"); sb.append("\tprivate "+ getFieldType(columntype, sbpackage) + " "+ columnname + ";\r\n"); } } sb.append("}"); File file = new File(dirFile, classname + ".java"); if (file.exists()) { file.delete(); } getTitle(sbpackage, classname); FileOutputStream outputStream = new FileOutputStream(file); outputStream.write(sbpackage.toString().getBytes()); outputStream.write(sb.toString().getBytes()); outputStream.close(); System.out.println(classname + " create success ... "); } } catch (Exception e) { e.printStackTrace(System.out); } finally { try { if (null != rs) { rs.close(); } if (null != conn) { conn.close(); } } catch (Exception e2) { } } } /** * 根據表名獲取類名稱 * * @param tablename * @return */ private static String getClassNameByTableName(String tablename) { String classname = getClassName(tablename); for (String name : classNames) { if (name.toLowerCase().equals(tablename.toLowerCase())) { classname = name; } } return classname; } private static boolean existFKColumn(String columnname) { if (fkTableNamesAndPk != null) { if (fkTableNamesAndPk.containsKey(columnname)) return true; } return false; } /** * 適合表名為單個單詞, 例如:表名是TBLUSER 類名是TBLUser;當表名是USER 類名是User;當表面是USERTYPE(兩個單詞) * 時,類名是Usertype,如果要 UserType,將期望的類名添加到classNames字段中(與數據庫表名一致 不區分大小寫)。 * * @param tablename * @return */ public static String getClassName(String tablename) { String res = tablename.toLowerCase(); if (tablename.startsWith("TBL")) { return tablename.substring(0, 4) + res.substring(4); } return tablename.substring(0, 1).toUpperCase() + res.substring(1); } /** * 設置字段類型 MySql數據類型 * * @param columnType * 列類型字符串 * @param sbpackage * 封裝包信息 * @return */ public static String getFieldType(String columnType, StringBuffer sbpackage) { /* * tinyblob tinyblob byte[] tinytext varchar java.lang.string blob blob byte[] text varchar java.lang.string mediumblob mediumblob byte[] mediumtext varchar java.lang.string longblob longblob byte[] longtext varchar java.lang.string enum('value1','value2',...) char java.lang.string set('value1','value2',...) char java.lang.string */ columnType = columnType.toLowerCase(); if (columnType.equals("varchar") || columnType.equals("nvarchar") || columnType.equals("char") // || columnType.equals("tinytext") // || columnType.equals("text") // || columnType.equals("mediumtext") // || columnType.equals("longtext") ) { return "String"; } else if (columnType.equals("tinyblob") ||columnType.equals("blob") ||columnType.equals("mediumblob") ||columnType.equals("longblob")) { return "byte[]1111"; } else if (columnType.equals("datetime") ||columnType.equals("date") ||columnType.equals("timestamp") ||columnType.equals("time") ||columnType.equals("year")) { sbpackage.append("import java.util.Date;\r\n"); return "Date"; } else if (columnType.equals("bit") ||columnType.equals("int") ||columnType.equals("tinyint") ||columnType.equals("smallint") // ||columnType.equals("bool") // ||columnType.equals("mediumint") // ||columnType.equals("bigint") ) { return "int"; } else if (columnType.equals("float")) { return "Float"; } else if (columnType.equals("double")) { return "Double"; } else if (columnType.equals("decimal")) { // sbpackage.append("import java.math.BigDecimal;\r\n"); // return "BigDecimal"; } return "ErrorType"; } /** * 設置類標題注釋 * * @param sbpackage * @param className */ public static void getTitle(StringBuffer sbpackage, String className) { SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日"); sbpackage.append("\r\n/**\r\n"); sbpackage.append("*\r\n"); sbpackage.append("* 標題: " + className + "<br/>\r\n"); sbpackage.append("* 說明: <br/>\r\n"); sbpackage.append("*\r\n"); sbpackage.append("* 作成信息: DATE: " + format.format(new Date()) + " NAME: author\r\n"); sbpackage.append("*\r\n"); sbpackage.append("* 修改信息<br/>\r\n"); sbpackage.append("* 修改日期 修改者 修改ID 修改內容<br/>\r\n"); sbpackage.append("*\r\n"); sbpackage.append("*/\r\n"); } }
單表實現:
根據查詢語句,獲取表結構信息。
package cn.test; import java.io.File; import java.io.FileOutputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; public class Test { private static String path="D:\\tool\\project\\DynamicTable\\src\\cn\\test\\entity"; private static String url = "jdbc:sqlserver://192.168.1.220:1433;User=cjtc;Password=cjtc;DatabaseName=Person"; public static void main(String[] args) { test(); } public static void test() { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; StringBuffer sb=new StringBuffer(); try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); conn = DriverManager.getConnection(url); String sql = "select * from TBLACCOUNT"; ps = conn.prepareStatement(sql); rs = ps.executeQuery(); sb.append("package cn.test.entity;\r\n"); sb.append("public class TBLACCOUNT {\r\n"); File dirFile = new File(path); if (!dirFile.exists()) { dirFile.mkdirs(); } // 獲取列名及類型 int colunmCount = rs.getMetaData().getColumnCount(); String[] colNameArr = new String[colunmCount]; String[] colTypeArr = new String[colunmCount]; for (int i = 0; i < colunmCount; i++) { colNameArr[i] = rs.getMetaData().getColumnName(i + 1); colTypeArr[i] = rs.getMetaData().getColumnTypeName(i + 1); System.out.println(colNameArr[i] + "(" + colTypeArr[i] + ")"+ " | "); if (colTypeArr[i].equals("varchar")||colTypeArr[i].equals("nvarchar")) { sb.append("\tprivate String "+colNameArr[i]+";\r\n"); }else if (colTypeArr[i].equals("datetime")) { } else if (colTypeArr[i].equals("int")) { } } sb.append("}"); File file = new File(dirFile,"TBLACCOUNT.java"); if(file.exists()){ file.delete(); } FileOutputStream outputStream = new FileOutputStream(file); outputStream.write(sb.toString().getBytes()); outputStream.close(); System.out.println(" success ... "); } catch (Exception e) { e.printStackTrace(System.out); } finally { try { if (null != rs) { rs.close(); } if (null != ps) { ps.close(); } if (null != conn) { conn.close(); } } catch (Exception e2) { } } } }