通过java创建数据库表_根据JavaBean 自动生成数据库表

本文介绍了如何使用Java注解和反射机制,通过自定义注解如@Table、@SQLInteger等,根据Java实体类创建数据库表。在实体类中,通过注解指定表名、字段名、字段类型和约束,然后通过反射解析这些注解,动态生成SQL创建语句并执行,以此实现ORM(对象关系映射)的功能。示例中展示了User类及其注解的使用,最后通过DBConnect类连接数据库执行创建表的SQL语句。

有了一个框架,只需要配置好数据库连接,就可以在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语句,我们要确定几个元素:表名,列名,列名类型,类型长度,约束等,这些都可以在实体类的属性加以注解说明来实现。

对于表名注解:

48304ba5e6f9fe08f3fa1abda7d326ab.png

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 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

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 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

对于约束注解:

48304ba5e6f9fe08f3fa1abda7d326ab.png

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 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

实体类:

48304ba5e6f9fe08f3fa1abda7d326ab.png

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 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

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 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

输出的语句应该是:

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

既然有了SQL语句,只需要通过JDBC连接数据库执行即可(其实还可以封装之后实现相同CRUD操作,下篇优化):

48304ba5e6f9fe08f3fa1abda7d326ab.png

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 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

最后就是主程序了:

48304ba5e6f9fe08f3fa1abda7d326ab.png

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 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

7453c3d753db3fd3d7156c62d883dcbd.png

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值