zoukankan      html  css  js  c++  java
  • 根据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, 实体改动对数据库的影响等等问题,就此一系列后面几篇做优化和研究。

  • 相关阅读:
    XAF应用开发教程(六)控制器
    XAF应用开发教程(五)验证模块
    XAF应用开发教程(四)应用程序模型
    XAF应用开发教程(三)业务对象模型之引用类型与关联关系
    XAF应用开发教程(二)业务对象模型之简单类型属性
    XAF应用开发教程(一) 创建项目
    C#
    C# 实例化类的执行顺序
    C#中?的相关使用
    angular过滤器 -- 关键字高亮显示
  • 原文地址:https://www.cnblogs.com/thoupin/p/2775828.html
Copyright © 2011-2022 走看看