模拟hibernate的注解,建立数据表的源码地址:https://gitee.com/zbone/myannohtml
java开发人员对注解,应该不会很陌生。咱们在开发的过程当中,常常会用到注解,那么,什么是注解呢?java
注解,也被称为元数据,为咱们在代码中添加信息,提供了一种形式化的方法,使咱们在稍后某个时刻,能够很是方便地使用这些原数据(thinking in java)。
这句话是什么意思?举一个hibernate的@Table注解,咱们在实体类上定义该注解,它不会当即生效。咱们启动Tomcat时,并借助spring工具,便触发该注解,从而建立数据表。也就是说,咱们先定义注解,等到合适的时间,咱们在使用该注解。mysql
咱们定义注解很是简单,只要在interface前加上@符号,这就是一个注解了,如代码所示:git
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注解,光定义注解是没有意义的。它要配合注解处理器才能生效,我会在下文提到如何写注解处理器。spring
咱们在开发的过程当中,常常会用到override注解,如代码所示: @Override public Result<List<Account>> listAccountById(Long memberId)
;该注解表示当前方法将覆盖超类中的方法。sql
阿里巴巴规范要求,若是当前方法覆盖超类的方法,必须写上override注解。由于咱们若是不当心拼写错误,或者方法签名对不上覆盖的方法,编译器就会发出错误地提示。咱们若是忘记写override,但这并不影响使用。好比,咱们须要重写接口AccountService的auditAcct方法签名,但并无加上override注解,编译器就会错误的提示,但不是报错。数据库
java除了override内置注解,还有Deprecated注解和SuppressWarnings注解。数组
Deprecated是弃用、再也不使用的意思。咱们若是用其来修饰类、方法、属性、构造器等,当咱们去调用被其修饰的类、方法、属性、构造器后,编辑器就会发出警告信息。我此时有一个ArrayUtil类,其中有个方法,判断数组是否为空数组,如代码所示:app
public class ArrayUtil { /** * Created By zby on 20:50 2019/2/13 * 判断字符串是否为null */ @Deprecated public 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拆开来看,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是处理源码级别的注解,它会生成新的字节码或其它文件。好比说,咱们如今有一个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 { @Property private 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不会生成新的文件,它只是在运行时,处理程序中的某些功能,好比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
-- 若是学生表存在,即删除学生表 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语句放置在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=false jdbc.driver=com.mysql.jdbc.Driver jdbc.username=root jdbc.password=root jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&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 { @Test public void test(){ boolean tracSucc = CreationTable.batchCreateTable(); if (tracSucc) { System.out.println("建立数据表成功"); } else { System.out.println("建立数据表失败"); } } }
输出结果如图所示:
查看数据库,发现有生成的数据表,表示是真的生成了数据表:
经过模拟hibernate框架,确实学到了很多东西,可能这就是成长吧。