目录

  • 导读
    • 注解释义
      • 注解定义
    • 内置三大注解
      • override注解
      • Deprecated注解
      • SuppressWarnings注解
      • 元注解
  • SOURCE和RUNTIME的区别
    • SOURCE
    • RUNTIME
  • 注解创建SQL表
    • 注意事项
    • 原生SQL语句
    • 定义注解
    • 设置权重
    • 生成SQL语句
    • 生成数据表
    • 测试创建表
  • 总结

导读

模拟hibernate的注解来创建数据表的源码地址:https://gitee.com/zbone/myanno添加链接描述
该项目从以下几个维度来做:

  1. 注解
  2. 先创建主键表,再创建外键表
  3. 加载配置文件
  4. 连接池

注解释义

java开发人员对注解,应该不会很陌生。我们在开发的过程中,经常会用到注解,那么,什么是注解呢?

注解,也被称为元数据,为我们在代码中添加信息,提供了一种形式化的方法,使我们在稍后某个时刻,可以非常方便地使用这些原数据(thinking in java)。

这句话是什么意思?举一个hibernate的@Table注解,我们在实体类上定义该注解,它不会立即生效。我们启动Tomcat时,并借助spring工具,便触发该注解,从而创建数据表。也就是说,我们先定义注解,等到合适的时间,我们在使用该注解。


注解定义

我们定义注解非常简单,只要在interface前加上@符号,这就是一个注解了,如代码所示:

package com.zbystudy.anno;import java.lang.annotation.Retention;
import java.lang.annotation.Target;import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;/*** Created By zby on 13:45 2019/3/31* 表名注解*/
@Target(TYPE)
@Retention(RUNTIME)
public @interface Table {/*** 表名*/String name() default "";/*** 表属于哪个数据库*/String catelog() default "";
}

这类似于hibernate中的@Table注解,光定义注解是没有意义的。它要配合注解处理器才能生效,我会在下文提到如何写注解处理器。


内置三大注解

override注解

我们在开发的过程中,经常会用到override注解,如代码所示:@Override public Result<List<Account>> listAccountById(Long memberId);该注解表示当前方法将覆盖超类中的方法。

阿里巴巴规范要求,如果当前方法覆盖超类的方法,必须写上override注解。因为我们如果不小心拼写错误,或者方法签名对不上覆盖的方法,编译器就会发出错误地提示。我们如果忘记写override,但这并不影响使用。比如,我们需要重写接口AccountService的auditAcct方法签名,但并没有加上override注解,编译器就会错误的提示,但不是报错。

java除了override内置注解,还有Deprecated注解和SuppressWarnings注解。


Deprecated注解

Deprecated是弃用、不再使用的意思。我们如果用其来修饰类、方法、属性、构造器等,当我们去调用被其修饰的类、方法、属性、构造器后,编辑器就会发出警告信息。我此时有一个ArrayUtil类,其中有个方法,判断数组是否为空数组,如代码所示:

public class ArrayUtil {/*** Created By zby on 20:50 2019/2/13* 判断字符串是否为null*/@Deprecatedpublic static boolean isEmpty(Object[] objects) {if (null == objects || objects.length == 0)return true;for (Object object : objects) {if (null != object)return false;}return true;}public static void main(String[] args) {String[] str={"nihao","wohao"};boolean isEmpty=ArrayUtil.isEmpty(str);System.out.println(isEmpty);}}

这也不影响使用,只是编辑器不建议你使用了,如图所示:


SuppressWarnings注解

SuppressWarnings拆开来看,Suppress是压制的意思,Warnings是警告的意思。它表示为压制警告,即关闭不当的警告信息。比如,还是上面的ArrayUtil类,其中有一个containsAny(String param, String[] filters)方法,它表示数组中是否包含某个值,但目前没有用到,其报出这个提示:

我们在上面的方法添加SuppressWarnings注解,其便不会报出这样的警告信息:

/*** Created By zby on 15:00 2019/2/14* 判断参数是否在该数组中** @param param   参数* @param filters 数组*/
@SuppressWarnings("all")
public static boolean containsAny(String param, String[] filters) {if (isNotEmpty(filters)) {if (StringUtils.isNotBlank(param)) {for (String tmp : filters) {if (tmp.equals(param)) {return true;}}}}return false;
}

以上三个注解是java内置的三大注解,Override和SuppressWarnings是源码级别(RetentionPolicy.SOURCE)的注解,而Deprecated是运行时(RetentionPolicy.RUNTIME)注解。源码级别和和运行时有什么区别,这个会在下文中讲解。

同时,除了以上三个内置注解,java还提供了四种自定义注解的注解,分别是@Target、@Retention、@Documented、@Inherited,这四种注解是我写这篇文档的重点,在下面便讲解这四种注解,也成为元注解。


元注解

元注解帮助我们自定义注解,它本身包含四种注解,以下是四种注解的作用域:


SOURCE和RUNTIME的区别

正如我们上文提到的,光定义注解是完全没有意义,我们需要手动书写注解的处理器。

SOURCE

SOURCE是处理源码级别的注解,它会生成新的字节码或其它文件。比如说,我们现在有一个javabean文件,我们想通过一个注解来自动生成set和get方法。这个该怎么实现呢?我们需要在启动jvm之后、.java文件转为字节码(.class)文件之前,就需要生成对应的set和get方法,因为它只在JVM编译期有效。

我们事先定义好注解类,其是将属性生成set和get方法,如下代码所示

/*** Created By zby on 14:35 2019/4/11*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Property {}

这里,有一个项目的java类,如代码所示:

public class Project {@Propertyprivate String name;}

我们这样定义之后,预想在启动了jvm后,生成如下.class文件

public class Project {private String name;public void setName(String name) {this.name = name;}public String getName() {return name;}
}

事实上,不会生成这样的.class文件,为什么呢?生成这样的.class文件超出了注解的功能,它需要再写其它的工具类来实现。因为我们定义好注解后,需要手动去写注解的处理器,lombok就是这样实现的。关于,如何书写SOURCE的注解处理器,你可以参考这个文档:插件化注解处理API(Pluggable Annotation Processing API)

如果你的实体类上写了lombok注解后,你可以去看看当前java文件所对应的.class文件,其和java原文件是完全的不同。


RUNTIME

RUNTIME不会生成新的文件,它只是在运行时,处理程序中的某些功能,比如hibernate的注解。我们在启动jvm后,hibernate会根据注解,来创建相应的数据表。但是,我们定义好注解,也是没有任何意义的。注解本身不会执行任何操作,所有的操作都是在我们定义的处理器中执行。

现在有一个注解类,标记字段是否是主键:

/*** Created By zby on 23:17 2019/4/1* 自增主键*/
@Target({METHOD,FIELD})
@Retention(RUNTIME)
public @interface Id {}

它本身没有任何意义,我们可以通过反射获取注解,从而执行相应的操作。

在下文中,我会模拟hibernate的注解,来创建生成数据表的代码,源码的地址在:https://gitee.com/zbone/myanno


注解创建SQL表

注意事项

  1. 我们既然想通过注解创建SQL语句,我们必须非常熟悉SQL语句。我们现在想用纯SQL语句来创建表,以及表之间的外键关系。我们必须在创建外键表之前创建主表,否则,SQL会报出相应的错误。

  2. 我们拿到了实体类,如何区分主表和外键表,设置主表和外键的先后关系,即先创建主表,再创建外表。

  3. 怎么将java类型转为SQL类型?

  4. 对于columnDefinition方法来说,如果其存储了当前字段自定义的类型长度,而java类型转化为SQL类型时,默认是字段类型的最大值。如果该类型长度小于默认的字段类型,我们怎么拿到自定义类型?

  5. 。。。。。。


原生SQL语句

-- 如果学生表存在,即删除学生表
DROP TABLE if EXISTS tb_student ;-- 创建学生表
CREATE TABLE `tb_student` (`id` INT (11) NOT NULL AUTO_INCREMENT COMMENT '主键' PRIMARY KEY,`name` VARCHAR (255) COMMENT '姓名' NULL,`sex` bit (1) COMMENT '性别' NULL
) ENGINE = INNODB DEFAULT CHARACTER SET = utf8;-- 如果课程表存在,则删除课程表
DROP TABLE if EXISTS tb_course ;-- 创建课程表
CREATE TABLE `tb_course` (`id`  int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键' PRIMARY KEY,`score`  double NULL DEFAULT 0 COMMENT '分数' ,`subject_name`  enum('SUBJECT_TYPE_CHINESE','SUBJECT_TYPE_MATH','SUBJECT_TYPE_ENGLISH') NULL  COMMENT '课程名称,存储枚举key值' ,`teacher_name`  varchar(255)  NULL DEFAULT NULL COMMENT '老师名称' ,`student_id`  int(11) NOT NULL,CONSTRAINT fk_student_course  FOREIGN KEY (`student_id`) REFERENCES `tb_student` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;-- 如果分数表存在,则删除分数表
DROP TABLE if EXISTS tb_score ;-- 创建分数表
CREATE TABLE `tb_score`(id BIGINT(20) not null AUTO_INCREMENT  comment '主键' primary key,score DOUBLE null  ,course_id BIGINT(18) null  COMMENT '外键是课程表' ,CONSTRAINT fk_course_score FOREIGN KEY(`course_id`) REFERENCES `tb_course`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8

以上是创建tb_student表和tb_course表的原生SQL语句,我们就是通过注解来拼装原生mysql语句,并调用jdbc的方法来创建数据表。以上的SQL语句也是通过我写的这个框架代码生成的


定义注解

定义的注解太多了,我在这里,只选择Column注解展示,其它注解等你们下载好源码,自己可以选择性地看。

/*** Created By zby on 14:07 2019/3/31* 表名类型的信息*/
@Target({FIELD,METHOD})
@Retention(RUNTIME)
public @interface Column {/*** 字段名*/String name() default "";/*** 字符长度*/int length() default 255;/*** 字段是否为空*/boolean nullable() default true;/*** 是否是唯一值*/boolean unique() default false;/*** 字段类型定义*/String columnDefinition() default "";}

设置权重

在将javabean对象和对象属性转化为SQL语句之前,我将javabean对象设置了权重。权重用来区分将javabean对象生成SQL语句的先后顺序,避免在未创建主表就创建了外键表的错误。因而,对类集合进行处理排序,也就是根据权重的先后顺序。

这里使用到的是LinkedHashSet类,其存储的是Javaban的类名。我没有使用HashSet类,因为链表具有先后顺序,而散列表没有先后顺序。同时,这里使用了递归,因为,当前外键表可能是别的外键表的主表,这时,就需要再次遍历,如核心代码所示:

/*** Created By zby on 23:22 2019/4/2* 设置权重** @param entityPath    实体类的路径* @param priority      权重值* @param fieldTypeName 属性类型名称*/
private static void setPriority(String entityPath, Integer priority, String fieldTypeName) {try {Class clazz = Class.forName(isNotBlank(entityPath) ? entityPath : fieldTypeName);Field[] fields = clazz.getDeclaredFields();if (ArrayUtil.isNotEmpty(fields)) {boolean hasManyToOne = false;for (Field field : fields) {ManyToOne manyToOne = field.getDeclaredAnnotation(ManyToOne.class);if (null != manyToOne) {fieldTypeName = field.getType().getName();hasManyToOne = true;break;}}if (hasManyToOne) {setPriority(null, ++priority, fieldTypeName);} else {entityPathMap.put(ENTITY_PATH, priority);}}} catch (ClassNotFoundException e) {e.printStackTrace();}
}

你会看到上面是私有方法,我这边只对外开放一个方法,对外开放的是设置权重后的实体类的类路径名:

/*** Created By zby on 23:31 2019/4/2* 获取排序后的值*/
public static Set<String> sortEntityPath() {int maxPriority = getMaxPriority();Set<String> entityPathSet = new LinkedHashSet<>();for (int i = 1; i <= maxPriority; i++) {if (entityPathMap != null && entityPathMap.size() > 0) {for (Map.Entry<String, Integer> entry : entityPathMap.entrySet()) {int value = entry.getValue();if (value == i) {entityPathSet.add(entry.getKey());}}}}return entityPathSet;
}

生成SQL语句

经过上面的排序之后,开始生成创建表的SQL语句,这里面就要用到了反射。因为是运行时注解类型,可以使用反射来获取对象类型。

我们将创建好的SQL语句放置在map中,key值是表名,value值是SQL语句,但是,我们使用的是private static Map<String, StringBuilder> dbTableMap = new LinkedHashMap<>();链表式的map,这里也涉及到先后顺序。因为链表存储的是指向下一个对象的节点,对象最开始在哪个位置,以后也在那个位置,位置始终是不会变的。

以下是生成SQL语句的代码,可能有点长,细心看,还是能看完的哈:

static {Set<String> entityFileNames = EntityPriority.sortEntityPath();
//        创建类for (String entityFileName : entityFileNames) {try {Class clazz = Class.forName(entityFileName);
//                获取表名Table dbTable = (Table) clazz.getDeclaredAnnotation(Table.class);if (null == dbTable) {System.out.println("no table annotation found in class:" + entityFileName);continue;}String tableName = dbTable.name();String currClazzName = clazz.getSimpleName();if (isBlank(tableName)) {System.out.println("no table name found in class:" + entityFileName);continue;}StringBuilder tableBuilder = new StringBuilder("CREATE TABLE" + BLANK_OP + BACK_QUOTE + tableName + BACK_QUOTE + LEFT_BRACKET + LINE_FEED_OP);
//                设置外键信息
//                存储的是外键信息Set<ForeignTable> foreignTables = new LinkedHashSet<>();
//                获取属性Field[] fields = clazz.getDeclaredFields();if (isNotEmpty(fields)) {for (Field field : fields) {//                        设置初始值TableColumn tableColumn = ColumnBuilder.instance();Column column = field.getDeclaredAnnotation(Column.class);if (null != column) {//                            获取字段名tableColumn.setColName(isBlank(column.name()) ? field.getName() : column.name());tableColumn.setColLength(column.length());tableColumn.setColDefinition(column.columnDefinition());tableColumn.setColNull(column.nullable() ? NULL : NOT_NULL);tableColumn.setColUnique(column.unique() ? "unique" : NUL_OP);}
//                        主键Id id = field.getDeclaredAnnotation(Id.class);if (null != id) {tableColumn.setColPk("primary key");tableColumn.setColPkVal(tableColumn.getColName());}
//                        自动增长GeneratedValue generated = field.getDeclaredAnnotation(GeneratedValue.class);if (null != generated) {tableColumn.setIncrementStrategy(generated.strategy().name().equals("AUTO") ? "AUTO_INCREMENT" : NUL_OP);}// 获取属性类型,同时获取字段长度String typeName = field.getType().getSimpleName();tableColumn.setColType(TypeTransformer.javaToSql(typeName));
//                        处理枚举类型Enumerated enumerated = field.getDeclaredAnnotation(Enumerated.class);if (null != enumerated) {String enumKey = NUL_OP;if ("STRING".equals(enumerated.value().name())) {for (Object obj : field.getType().getEnumConstants()) {enumKey += SINGLE_QUOTES + obj.toString() + SINGLE_QUOTES + SERIES_COMMA_OP;}} else if ("ORDINAL".equals(enumerated.value().name())) {Object[] objects = field.getType().getEnumConstants();for (int i = 0; i < objects.length; i++) {enumKey += SINGLE_QUOTES + i + SINGLE_QUOTES + SERIES_COMMA_OP;}} else {continue;}enumKey = substring(enumKey, 0, enumKey.lastIndexOf(SERIES_COMMA_OP));tableColumn.setColType("enum" + LEFT_BRACKET + enumKey + RIGHT_BRACKET);}
//                        处理多对一的关系ManyToOne manyToOne = field.getDeclaredAnnotation(ManyToOne.class);if (manyToOne != null) {CascadeType[] cascadeTypes = manyToOne.cascade();if (ArrayUtil.isEmpty(cascadeTypes)) {tableColumn.setColCascade("ON DELETE CASCADE ON UPDATE CASCADE");} else if (ArrayUtil.isNotEmpty(cascadeTypes) && cascadeTypes.length == 1) {tableColumn.setColCascade("ON DELETE " + transNoAction(cascadeTypes[0].name()) +" ON UPDATE CASCADE");} else if (ArrayUtil.isNotEmpty(cascadeTypes) && cascadeTypes.length == 2) {tableColumn.setColCascade("ON DELETE " + transNoAction(cascadeTypes[0].name())+ " ON UPDATE " + transNoAction(cascadeTypes[1].name()));} else {continue;}}
//                      关联表JoinColumn joinColumn = field.getDeclaredAnnotation(JoinColumn.class);if (null != joinColumn) {ForeignTable foreignTable = ForeignTableBuilder.instance();foreignTable.setForeignKeyName(joinColumn.name());foreignTable.setCascade(tableColumn.getColCascade());foreignTable.setForeignTableName(joinColumn.table());tableColumn.setColName(joinColumn.name());tableColumn.setForeignTable(joinColumn.table());tableColumn.setColDefinition(joinColumn.columnDefinition());tableColumn.setColNull(joinColumn.nullable() ? NULL : NOT_NULL);
//                            外键类型类型忘记填写Class fieldType = Class.forName(field.getType().getName());if (isBlank(tableColumn.getForeignTable())) {dbTable = (Table) fieldType.getDeclaredAnnotation(Table.class);if (dbTable != null && isNotBlank(dbTable.name())) {tableColumn.setForeignTable(dbTable.name());foreignTable.setForeignTableName(tableColumn.getForeignTable());}}foreignTable.setForeignName("fk" + UNDERLINE + classNameToProName(fieldType.getSimpleName()) +UNDERLINE + classNameToProName(currClazzName));fields = fieldType.getDeclaredFields();if (ArrayUtil.isNotEmpty(fields)) {for (Field fkField : fields) {id = fkField.getDeclaredAnnotation(Id.class);if (null != id) {tableColumn.setColType(TypeTransformer.javaToSql(fkField.getType().getSimpleName()));column = fkField.getDeclaredAnnotation(Column.class);
//                                    设置外键表的关联字段foreignTable.setForeignTablePk(null != column && isBlank(column.name()) ? column.name() : fkField.getName());}}}foreignTables.add(foreignTable);}
//                      处理columnDefinition = "int(11) NOT NULL COMMENT '外键是学生表'"和真实的字段String colType = tableColumn.getColType();String colDefinition = tableColumn.getColDefinition();if (isNotBlank(colDefinition) && isNotBlank(colType)) {String[] sqlNumberType = {"INT(11)", "INTEGER(11)", "BIGINT(20)", "VARCHAR(255)", "SMALLINT(6)", "NUMERIC(10)", "TINYINT(4)", "BIT(1)"};if (ArrayUtils.contains(sqlNumberType, colType)) {int colNum = Integer.parseInt(substring(colType, colType.indexOf(LEFT_BRACKET) + 1, colType.lastIndexOf(RIGHT_BRACKET)));colType = substring(colType, 0, colType.lastIndexOf(LEFT_BRACKET));if (StringUtils.containsAny(colDefinition, colType)) {int typeEndLength = StringHelper.getEndLength(colDefinition, colType);
//                                    COL_DEFINITION包含字符串,且同时包含(  )if (typeEndLength != 0 && StringUtils.containsAny(colDefinition, LEFT_BRACKET, RIGHT_BRACKET)) {String definitionNum = StringUtils.substring(colDefinition, typeEndLength + 1, colDefinition.indexOf(")"));if (Integer.parseInt(definitionNum) <= colNum) {tableColumn.setColType(colType + LEFT_BRACKET + Integer.parseInt(definitionNum) + RIGHT_BRACKET);}tableColumn.setColDefinition(StringUtils.remove(colDefinition, tableColumn.getColType()));}}}}tableBuilder.append(TAB_OP + BLANK_OP + tableColumn.getColName() + BLANK_OP+ tableColumn.getColType() + BLANK_OP+ tableColumn.getColNull() + BLANK_OP+ (tableColumn.getIncrementStrategy() != null ? tableColumn.getIncrementStrategy() + BLANK_OP : NUL_OP)+ (tableColumn.getColDefinition() != null ? tableColumn.getColDefinition() + BLANK_OP : NUL_OP)+ (tableColumn.getColPk() != null ? tableColumn.getColPk() : NUL_OP)+ SERIES_COMMA_OP + LINE_FEED_OP);}}if (foreignTables.size() > 0) {for (ForeignTable foreignTable : foreignTables) {tableBuilder.append(TAB_OP + BLANK_OP + "CONSTRAINT" + BLANK_OP +foreignTable.getForeignName() + BLANK_OP + "FOREIGN KEY" + LEFT_BRACKET + BACK_QUOTE +foreignTable.getForeignKeyName() + BACK_QUOTE + RIGHT_BRACKET + BLANK_OP + "REFERENCES" + BLANK_OP + BACK_QUOTE +foreignTable.getForeignTableName() + BACK_QUOTE + LEFT_BRACKET + BACK_QUOTE +foreignTable.getForeignTablePk() + BACK_QUOTE + RIGHT_BRACKET + BLANK_OP +foreignTable.getCascade() + SERIES_COMMA_OP + LINE_FEED_OP);}}tableBuilder = new StringBuilder(tableBuilder.substring(0, tableBuilder.lastIndexOf(SERIES_COMMA_OP)));tableBuilder.append(LINE_FEED_OP + BLANK_OP + RIGHT_BRACKET + BLANK_OP+ EngineCharator.tablEengine + BLANK_OP + EngineCharator.tableCharacter);if (MapUtil.existKey(tableName, dbTableMap)) {continue;}dbTableMap.put(tableName, tableBuilder);} catch (ClassNotFoundException e) {e.printStackTrace();}}}

上面的代码有点冗长,没有做具体的细分,以后,会慢慢地优化。

生成数据表

经过上面的步骤后,此时,获得了数据表的SQL语句,开始调用jdbc底层的代码。在执行创建数据表的代码之前,还需要些配置文件:

#实体类的路径,可以采用ant风格
jdbc.package=com.zbystudy.po.*#是否忽略已创建表,如果为true,不删除已创建的表
#如果为false,则删除所有的表,重新创建新表
jdbc.ignoreExistTable=falsejdbc.driver=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8&amp;zeroDateTimeBehavior=convertToNull&amp;transformedBitIsBoolean=true&useSSL=true

有了配置文件,根据配置文件的条件,来创建数据表和删除数据表。根据先创建主表、再创建外键表的顺来创建表,根据先删除外键表、再删除主表的方式来删除数据表。

/*** Created By zby on 16:12 2019/4/3* 创建表*/
public class CreationTable {/*** Created By zby on 16:15 2019/4/3* 判断表是否存在*/public static boolean existsTable(String tableName) {if (isBlank(tableName)) {return false;}String sql = "SELECT column_name FROM information_schema.columns WHERE table_name=?";Connection conn = sqlConnectFactory.createConnect();PreparedStatement ps = null;ResultSet rs = null;try {ps = conn.prepareStatement(sql);ps.setString(1, tableName);rs = ps.executeQuery();while (rs.next()) {return true;}return false;} catch (SQLException e) {e.printStackTrace();} finally {ConnCloseUtil.closeConn(conn, ps, rs);}return false;}/*** Created By zby on 10:48 2019/4/8* 删除表*/public static boolean dropTable(String tableName) {if (isBlank(tableName)) {return false;}String sql = "DROP TABLE " + tableName;Connection conn = sqlConnectFactory.createConnect();PreparedStatement ps = null;try {ps = conn.prepareStatement(sql);int result = ps.executeUpdate();return result == 0 ? true : false;} catch (SQLException e) {e.printStackTrace();} finally {ConnCloseUtil.closeConn(conn, ps);}return false;}/*** Created By zby on 0:12 2019/4/9* <p>* 批量删除*/public static void batchDropTable(Map<String, StringBuilder> tableSqls) {if (isKeyBlank(tableSqls)) {throw new RuntimeException("表名为空,请核查后再删除表");}for (Map.Entry<String, StringBuilder> entry : tableSqls.entrySet()) {String tableName = entry.getKey();
//            表不存在,跳过此循环if (!existsTable(tableName)) {continue;}dropTable(entry.getKey());}}/*** Created By zby on 9:30 2019/4/8* 创建数据表*/public synchronized static boolean batchCreateTable() {//        是否忽略已存在的表String ignoreExistTable = sqlConnectFactory.getProperties().getProperty("jdbc.ignoreExistTable");Connection conn = sqlConnectFactory.createConnect();boolean tranSuccess = false;try {conn.setAutoCommit(false);conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);Map<String, StringBuilder> tableSQLs = TableInit.getDbTableMap();if (isNotBlank(ignoreExistTable) && ignoreExistTable.equalsIgnoreCase("true")) {//                如果表名为空,则无法删除if (isKeyBlank(tableSQLs)) {return false;}for (Map.Entry<String, StringBuilder> entry : tableSQLs.entrySet()) {boolean tableExists = existsTable(entry.getKey());
//              如果表存在,则跳过循环if (tableExists) {continue;}tranSuccess = CreateTable(entry.getKey(), conn, entry.getValue().toString());}} else {//                map数据反转Map<String, StringBuilder> reverseTableSqls = reverseMap(tableSQLs);
//                如果表名为空,则无法删除if (isKeyBlank(reverseTableSqls)) {return false;}
//                先删除所有表,在创建表batchDropTable(reverseTableSqls);for (Map.Entry<String, StringBuilder> entry : tableSQLs.entrySet()) {tranSuccess = CreateTable(entry.getKey(), conn, entry.getValue().toString());}}if (tranSuccess) {conn.commit();}} catch (SQLException e) {e.printStackTrace();} finally {ConnCloseUtil.closeConn(conn);}return tranSuccess;}/*** Created By zby on 9:30 2019/4/9* 创建数据表** @param tableName 表名* @param conn      数据库连接对象* @param sql       创建表的执行语句*/public static boolean CreateTable(String tableName, Connection conn, String sql) {if (conn != null && isNotBlank(sql)) {PreparedStatement ps = null;try {ps = conn.prepareStatement(sql);return ps.executeUpdate() == 0 ? true : false;} catch (SQLException e) {e.printStackTrace();throw new RuntimeException("添加表tableName=" + tableName + "失败");} finally {ConnCloseUtil.closeState(ps);}}return false;}}

测试创建表

万事俱备,只欠东风,既然写好了代码,那么,就测试能不能创建成功,以下是测试代码:

package com.zbystudy;import com.zbystudy.core.vo.CreationTable;
import org.junit.Test;/*** Created By zby on 11:32 2019/4/9*/
public class CreationTableTest {@Testpublic void test(){boolean tracSucc = CreationTable.batchCreateTable();if (tracSucc) {System.out.println("创建数据表成功");} else {System.out.println("创建数据表失败");}}
}

输出结果如图所示:

查看数据库,发现有生成的数据表,表示是真的生成了数据表:

总结

通过模拟hibernate框架,确实学到了不少东西,可能这就是成长吧。

模拟hibernate的注解来创建数据表,内置注解相关推荐

  1. 01-创建项目 创建数据表-类别管理--添加类别--持久层

    文章目录 1. 关于此项目 2. 关于项目的开发流程 3. 创建数据库与数据表 4. 关于Project与Module 5. 创建Project 6. 商品管理模块项目 7. 关于编写代码 8. 类别 ...

  2. Python使用SQLAlchemy连接数据库并创建数据表、插入数据、删除数据、更新表、查询表(CRUD)

    Python使用SQLAlchemy连接数据库并创建数据表.插入数据.删除数据.更新表.查询表(CRUD) 目录 Python使用SQLAlchemy连接数据库并创建数据表 #SQLAlchemy # ...

  3. MySQL 创建数据表

    MySQL 创建数据表 创建MySQL数据表的SQL语法: CREATE TABLE table_name (column_name column_type); 例如,我们在 PENGKE 数据库中创 ...

  4. XamarinSQLite教程创建数据表

    XamarinSQLite教程创建数据表 新创建的数据库没有任何表.开发者需要手动添加数据表,并添加测试所需的数据. 1.创建数据表 为了存储数据,开发者需要添加自己的表,并设计表的结构.操作步骤如下 ...

  5. mysql user表添加记录_《MySQL数据操作与查询》- 返校复习课练习题,创建数据库user_system,创建数据表user及user_ext...

    一.其它(共18题,100分) 1.创建数据库user_system CREATE DATABASE user_system 2.在数据库user_system中创建数据表user及user_ext, ...

  6. Oracle 创建数据表以及对数据表、字段、主外键、约束的操作

    选择主键的原则: 最少性 尽量选择使用单个键作为主键 稳定性 尽量选择数值更新少的列作为主键 1.创建数据表(CREATE TABLE) --创建数据表Student create table Stu ...

  7. mysql if exists 数据表_使用IF NOT EXISTS创建数据表

    如果简单的使用如下sql语句可能会返回失败,失败的原因极有可能是已经存在这张数据表了. 如果简单的使用如下sql语句可能会返回失败,失败的原因极有可能是已经存在这张数据表了. create table ...

  8. 利用T-SQL语句创建数据表

    创建表的另一种方式是输入代码进行创建. 在连接上需要操作的地址后,选择数据库,从中通过查询--创建查询进行操作,出现下面的界面: 在这里输入代码进行表格的创建: create table为创建表: p ...

  9. MySQL创建数据表(CREATE TABLE语句)

    在创建数据库之后,接下来就要在数据库中创建数据表.所谓创建数据表,指的是在已经创建的数据库中建立新表. 创建数据表的过程是规定数据列的属性的过程,同时也是实施数据完整性(包括实体完整性.引用完整性和域 ...

最新文章

  1. Python命令行参数学习
  2. python时间处理
  3. 为什么边缘概率密度是联合概率密度的积分_德甲前瞻|柏林赫塔VS柏林联合
  4. 九十二、动态规划系列之股票问题(上)
  5. element-ui中el-table的表头、内容样式
  6. unix修改服务器时间格式,处理unix中的日期格式
  7. oracle证书洛阳,ORACLE手工建库
  8. Emlog简约好看的博客主题末遇
  9. 编程之美 4.7蚂蚁爬杆
  10. Java过滤emoji表情,找出emoji的unicode范围。
  11. steam快速换号工具易语言源码 附成品
  12. 关于DynamipsGUI
  13. python照片处理生成3d模型_【神器】摄影实时建模,用照片生成3D模型
  14. 基于java小区物业管理系统(含源文件)
  15. 8、ESP32-S - 控制 SPI 液晶屏显示动态图片
  16. 使用CPN Tools工具做简单的登录模型(初学者)
  17. b站React禹哥版视频笔记-React面向组件编程(上)
  18. jboot_jboot这些框架有意义吗
  19. aspx页面乱码问题解决
  20. zookeeper集群部署

热门文章

  1. 【AI创新者】驭势吴甘沙:做无人驾驶的赋能者
  2. arcgistif合并_ARCGIS拼接影像教程
  3. Minecraft 开服和 Linux 常用指令
  4. 什么是SOCKS5代理
  5. 基于盛大云IaaS部署CloudFoundry平台
  6. win10双系统网络不稳定解决方法
  7. 计算机图形学绘画应用,计算机图形学的介绍
  8. java控制excel_利用Java控制EXCEL实例详解
  9. electron如何控制电脑摄像头
  10. 联想台式电脑的计算机软件系统,联想台式电脑一键重装系统win7系统教程