根據JavaBean 自動生成數據庫表


      有了一個框架,只需要配置好數據庫連接,就可以在java代碼層操控database,對於寫個model便在數據庫中創建了一張表而感到十分神奇,隱約想起以前看《Thinking in Java》中關於注解(Annotation)一張中對於自動生成SQL語句的操作。

首先略微介紹下注解(亦稱為與數據metadata(ORM-對象/關系映射中的核心))。

      Annotation源自JavaSE1.5,內置3個標准注解,4個元注解:

      (1)java.lang.*中的@Override,@Deprecated, @SuppressWarnings

      (2)java.lang.annotations.*中的@Target, @Inherited, @Retention, @Documented

     對於后4個元注解,稍后再在代碼中解釋。

     對於一個創建表的SQL Create語句,我們要確定幾個元素:表名,列名,列名類型,類型長度,約束等,這些都可以在實體類的屬性加以注解說明來實現。

     對於表名注解:

 1  1 package annotiation;
 2  2 import java.lang.annotation.*;
 3  3 
 4  4 @Inherited                             // 允許子類繼承父類中的注解
 5  5 @Documented                            // 將此注解包含在Javadoc中 
 6  6 @Target(ElementType.TYPE)              // 類、接口(包括注解類型)或枚舉類型聲明
 7  7 @Retention(RetentionPolicy.RUNTIME)    // VM在運行時保留注解,從而通過反射獲取信息
 8  8 
 9  9 public @interface DBTable {
10 10     public String name() default "";   // 注解未賦值是,默認為空
11 11 }
View Code

 

     對於字段注解:(這邊先只設定了String類型,其實實際情況沒這么單純,下篇再優化)

 1  1 package annotiation;
 2  2 import java.lang.annotation.*;
 3  3 
 4  4 @Inherited
 5  5 @Documented
 6  6 @Target(ElementType.FIELD)              // 域聲明(包括枚舉類型實例)
 7  7 @Retention(RetentionPolicy.RUNTIME)
 8  8 
 9  9 public @interface SQLInteger {
10 10     String name() default "";
11 11     Constraints constraints() default @Constraints;  // 約束注解,詳細見下面代碼
12 12 }
View Code

 

     對於約束注解:

 1 package annotiation;
 2 import java.lang.annotation.*;
 3 
 4 @Inherited
 5 @Documented
 6 @Target(ElementType.FIELD)
 7 @Retention(RetentionPolicy.RUNTIME)
 8 
 9 public @interface Constraints {
10     boolean primaryKey() default false;   // 主鍵,默認為空
11     boolean allowNull() default true;     // 默認允許為空
12     boolean unique() default false;          // 默認允許重復
13 }
View Code

 

    實體類:

 1 package model;
 2 
 3 import annotiation.*;
 4 
 5 @DBTable(name = "User")                    // 設置表名為User
 6 public class User {
 7     @SQLString(size = 50)                  // 設置字段 username, varchar(50)
 8     String username;
 9     
10     @SQLString(size = 50)
11     String password;
12     
13     @SQLString(size = 30, constraints = @Constraints(primaryKey = true)) // 設置為主鍵
14     String handle;
15     
16     static int memberCount;               
17 
18     public String getUsername() { return username; }
19     
20     public void setUsername(String username) { this.username = username; }// 個人感覺set方法可以去掉
21 
22     public String getPassword() { return password; }
23     
24     public void setPassword(String password) { this.password = password; }
25     
26     public String getHandle() {    return handle; }
27 
28     public void setHandle(String handle) { this.handle = handle; }
29     
30     public String toString() { return handle; }  
31 }
View Code

 

     准備工作之后,就是如何根據注解和反射拼接SQL語句:

 1 package creator;
 2 import java.lang.reflect.*;
 3 import java.lang.annotation.*;
 4 import java.util.*;
 5 
 6 import annotiation.Constraints;
 7 import annotiation.DBTable;
 8 import annotiation.SQLString;
 9 
10 
11 public class TableCreator {
12     private static String getConstraints(Constraints constraints) { // 獲取字段約束屬性
13         String cons = "";
14         if (!constraints.allowNull()) {
15             cons += " NOT NULL";
16         }
17         if (constraints.primaryKey()) {
18             cons += " PRIMARY KEY";
19         }
20         if (constraints.unique()) {
21             cons += " UNIQUE";
22         }
23         return cons; 
24     }
25     
26     /* 這邊還需要通過IO來遍歷指定model包下所有實體類, 如上,待下一篇優化
27     private static ArrayList<String> getTables() {
28         ArrayList<String> tables = new ArrayList<String>();
29         Package pckg = Package.getPackage("model");
30         Class<?>[] cls = pckg.; 
31         for (Class<?> cl : cls) {
32             tables.add(cl.getName());
33         }
34         return tables;
35     }
36     */
37     
38     public static String getSql() throws ClassNotFoundException {
39         String sql = null;
40         //ArrayList<String> tables = getTables();
41         String[] tables = {"model.User"};
42         for (String className : tables) {
43             /*
44             String[] table = className.split("\\.");
45             for (String tb : table) {
46                 System.out.println(tb);
47             }
48             */
49             Class<?> cl = Class.forName(className);            // 通過類名得到該實體類
50             DBTable dbtable = cl.getAnnotation(DBTable.class); // 通過注解得到表明
51             String tableName = dbtable.name().length() > 1 ? dbtable.name() : cl.getName().toUpperCase();
52             /* comments
53             System.out.println("tableName: " + tableName);
54             */
55             List<String> columns = new ArrayList<String>();
56             for (Field field : cl.getDeclaredFields()) {       // 得到該類下所有屬性
57                 String columnName = null;
58                 Annotation[] annotations = field.getAnnotations();
59                 if (annotations.length < 1) {
60                     continue;
61                 }
62                 if (annotations[0] instanceof SQLString) {
63                     SQLString sStr = (SQLString)annotations[0];
64                     columnName = sStr.name().length() < 1 ? field.getName() : sStr.name();
65                     columns.add(columnName + " VARCHAR(" + sStr.size() + ")" + getConstraints(sStr.constraints()));
66                 }
67             }
68             
69             StringBuilder sb = new StringBuilder("Create Table " + tableName + "(");
70             for (String column : columns) {
71                 sb.append("\n    " + column + ",");            // 拼接各個字段的定義語句
72             }
73             sql = sb.substring(0, sb.length() - 1) +");";
74         }
75         System.out.println("=========" + sql + "=========");   // 測試輸出
76         return sql;
77     }
78 }
View Code

 

     輸出的語句應該是:

1 Create Table User(
2     username VARCHAR(50),
3     password VARCHAR(50),
4     handle VARCHAR(30) PRIMARY KEY);
View Code

 

      既然有了SQL語句,只需要通過JDBC連接數據庫執行即可(其實還可以封裝之后實現相同CRUD操作,下篇優化):

1 package dbconnect;
 2 
 3 import java.sql.Connection;
 4 import java.sql.DriverManager;
 5 import java.sql.SQLException;
 6 import java.sql.Statement;
 7 
 8 
 9 public class DBConnect {
10     static Connection connect;
11     static String driver = "com.mysql.jdbc.Driver";
12     static String password = "thoupin'spassword";
13     static String username = "thoupin";
14     static String dbName = "test";
15     static String url = "jdbc:mysql://localhost/" + dbName;
16         
17     public static void connect() {           // 連接
18         try {
19             Class.forName(driver);
20         } catch (ClassNotFoundException e) {
21             System.out.println("Can not find the Driver!");
22             e.printStackTrace();
23         }
24         
25         try {
26             connect = DriverManager.getConnection(url, username, password);
27         } catch (SQLException e) {
28             System.out.println("Database connect failed!");
29             e.printStackTrace();
30         }
31     }
32     
33     public static void execute(String sql) { // 執行語句    
34         Statement stmt;
35         try {
36             stmt = connect.createStatement();
37             stmt.executeUpdate(sql);
38         } catch (SQLException e) {
39             // TODO Auto-generated catch block
40             e.printStackTrace();
41         }
42     }
43 
44     public static void close() {             // 關閉連接
45         if (connect != null) {
46             try {
47                 connect.close();
48             } catch (SQLException e) {
49                 e.printStackTrace();
50             }
51         }
52     }
53 }
View Code

 

     最后就是主程序了:

 1 package Main;
 2 
 3 import creator.TableCreator;
 4 import dbconnect.DBConnect;
 5 
 6 public class run {
 7     public static void main(String[] args) {
 8         DBConnect.connect();
 9         try {
10             DBConnect.execute(TableCreator.getSql());
11         } catch (ClassNotFoundException e) {
12             // TODO Auto-generated catch block
13             e.printStackTrace();
14         }
15         DBConnect.close();
16     }
17 }
View Code

 

      最后數據庫中變出現了一張新表:

      至此,一個自己粗糙簡陋的自動生成工具算是做好了,但實際情況很復雜,遠遠沒有這么簡單, 類似不同字段類型的判斷,多張表的同時創建,判斷新舊表從而決定是否重新執行SQL, 實體改動對數據庫的影響等等問題,就此一系列后面幾篇做優化和研究。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM