根据JavaBean 自动生成数据库表

根据JavaBean 自动生成数据库表

有了一个框架,只需要配置好数据库连接,就可以在java代码层操控database,对于写个model便在数据库中创建了一张表表示很好奇,隐约想起以前看《Thinking in Java》中关于注解(Annotation)一张中对于自动生成SQL语句的操作。

首先略微介绍下注解(亦称为与数据metadata(ORM-对象/关系映射中的核心))。

Annotation源自JavaSE1.5,内置3个标准注解,4个元注解:

java.lang.*中的@Override,@Deprecated, @SuppressWarnings

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

对于后4个元注解,稍后再在代码中解释。

对于一个创建表的SQL Create语句,我们要确定几个元素:表名,列名,列名类型,类型长度,约束等,这些都可以在实体类的属性加以注解说明来实现。

对于表名注解:

复制代码
 1 package annotiation;
 2 import java.lang.annotation.*;
 3 
 4 @Inherited                             // 允许子类继承父类中的注解
 5 @Documented                            // 将此注解包含在Javadoc中 
 6 @Target(ElementType.TYPE)              // 类、接口(包括注解类型)或枚举类型声明
 7 @Retention(RetentionPolicy.RUNTIME)    // VM在运行时保留注解,从而通过反射获取信息
 8 
 9 public @interface DBTable {
10     public String name() default "";   // 注解未赋值是,默认为空
11 }
复制代码

对于字段注解:(这边先只设定了String类型,其实实际情况没这么单纯,下篇再优化)

复制代码
 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 SQLInteger {
10     String name() default "";
11     Constraints constraints() default @Constraints;  // 约束注解,详细见下面代码
12 }
复制代码

对于约束注解:

复制代码
 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 }
复制代码

实体类:

复制代码
 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 }
复制代码

准备工作之后,就是如何根据注解和反射拼接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 }
复制代码

输出的语句应该是:

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

既然有了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 }
复制代码

最后就是主程序了:

复制代码
 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 }
复制代码

最后数据库中变出现了一张新表:

至此,一个自己粗糙简陋的自动生成工具算是做好了,但实际情况很复杂,远远没有这么简单, 类似不同字段类型的判断,多张表的同时创建,判断新旧表从而决定是否重新执行SQL, 实体改动对数据库的影响等等问题,就此一系列后面几篇做优化和研究。

posted @ 2012-11-18 20:20  张良  阅读(1342)  评论(1编辑  收藏  举报