有了一个框架,只需要配置好数据库连接,就可以在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 packageannotiation;2 import java.lang.annotation.*;3
4 @Inherited //允许子类继承父类中的注解
5 @Documented //将此注解包含在Javadoc中
6 @Target(ElementType.TYPE) //类、接口(包括注解类型)或枚举类型声明
7 @Retention(RetentionPolicy.RUNTIME) //VM在运行时保留注解,从而通过反射获取信息
8
9 public @interfaceDBTable {10 public String name() default ""; //注解未赋值是,默认为空
11 }

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

1 packageannotiation;2 import java.lang.annotation.*;3
4 @Inherited5 @Documented6 @Target(ElementType.FIELD) //域声明(包括枚举类型实例)
7 @Retention(RetentionPolicy.RUNTIME)8
9 public @interfaceSQLInteger {10 String name() default "";11 Constraints constraints() default @Constraints; //约束注解,详细见下面代码
12 }

对于约束注解:

1 packageannotiation;2 import java.lang.annotation.*;3
4 @Inherited5 @Documented6 @Target(ElementType.FIELD)7 @Retention(RetentionPolicy.RUNTIME)8
9 public @interfaceConstraints {10 boolean primaryKey() default false; //主键,默认为空
11 boolean allowNull() default true; //默认允许为空
12 boolean unique() default false; //默认允许重复
13 }

实体类:

1 packagemodel;2
3 import annotiation.*;4
5 @DBTable(name = "User") //设置表名为User
6 public classUser {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 intmemberCount;17
18 public String getUsername() { returnusername; }19
20 public void setUsername(String username) { this.username = username; }//个人感觉set方法可以去掉
21
22 public String getPassword() { returnpassword; }23
24 public void setPassword(String password) { this.password =password; }25
26 public String getHandle() { returnhandle; }27
28 public void setHandle(String handle) { this.handle =handle; }29
30 public String toString() { returnhandle; }31 }

准备工作之后,就是如何根据注解和反射拼接SQL语句:

1 packagecreator;2 import java.lang.reflect.*;3 import java.lang.annotation.*;4 import java.util.*;5
6 importannotiation.Constraints;7 importannotiation.DBTable;8 importannotiation.SQLString;9
10
11 public classTableCreator {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 returncons;24 }25
26 /*这边还需要通过IO来遍历指定model包下所有实体类, 如上,待下一篇优化27 private static ArrayList getTables() {28 ArrayList tables = new ArrayList();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() throwsClassNotFoundException {39 String sql = null;40 //ArrayList 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 /*comments53 System.out.println("tableName: " + tableName);54 */
55 List columns = new ArrayList();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] instanceofSQLString) {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 returnsql;77 }78 }

输出的语句应该是:
1 Create Table User(2 username VARCHAR(50),3 password VARCHAR(50),4 handle VARCHAR(30) PRIMARY KEY);
既然有了SQL语句,只需要通过JDBC连接数据库执行即可(其实还可以封装之后实现相同CRUD操作,下篇优化):

1 packagedbconnect;2
3 importjava.sql.Connection;4 importjava.sql.DriverManager;5 importjava.sql.SQLException;6 importjava.sql.Statement;7
8
9 public classDBConnect {10 staticConnection 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 packageMain;2
3 importcreator.TableCreator;4 importdbconnect.DBConnect;5
6 public classrun {7 public static voidmain(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, 实体改动对数据库的影响等等问题,就此一系列后面几篇做优化和研究。
本文介绍了如何使用Java注解和反射机制,通过自定义注解如@Table、@SQLInteger等,根据Java实体类创建数据库表。在实体类中,通过注解指定表名、字段名、字段类型和约束,然后通过反射解析这些注解,动态生成SQL创建语句并执行,以此实现ORM(对象关系映射)的功能。示例中展示了User类及其注解的使用,最后通过DBConnect类连接数据库执行创建表的SQL语句。
2026

被折叠的 条评论
为什么被折叠?



