基于反射和注解的Bean对应数据库表的自动生成
2016-05-10 23:42
441 查看
对于bean对象自动生成数据库中对应的表结构,有很多强大的框架来帮我们完成。这里记录自己做的基于反射和注解方式自动生成数据库表的核心代码。
首先,是bean对象的配置文件,文件名为DAO.xml
<?xml version="1.0" encoding="UTF-8"?>
<packages>
<!-- DAO的所有包,这里可以将所有的bean配置在此 -->
<package-name>com.csl.student.vo</package-name>
</packages>
其次,对配置文件进行读取,类名为XMLReader。这里的解析用的DOM4j方式。
public class XMLReader {
private static SAXReader saxReader ;
private static Document document ;
// 初始化解析器和文档对象
static {
try {
// 解析器
saxReader = new SAXReader();
// 指定解析哪个xml文件
document = saxReader.read(new File("这里写XML文件的绝对路径"));
} catch (DocumentException e) {
e.printStackTrace();
}
}
//读取父节点下的所有package-name属性下的所有的值,保存到List集合中。
public static List<String> read() {
//声明集合
List<String> packages = new ArrayList<String>();
// 得到根元素
Element root = document.getRootElement();
// 取出 root元素 下的所有package-name元素
List<Element> packageNode= new ArrayList<Element>();
packageNode=
root.elements("package-name");
for (int i = 0; i < packageNode.size(); i++) {
String packages_name = ((Element)packageNode.get(i)).getText();
packages.add(packages_name);
}
return packages;
}
}
然后,根据得到的bean的全名(包名和类名)利用注解和反射创建表。这里分为三个步骤:
第一,根据得到bean所在的包,扫描整个包,得到该包下所有的bean类。扫描包的抽象类为PackageScan,设置成抽象是为了程序的拓展。
public abstract class PackageScan {
/**
* 作用:得到指定包名下的类的全名
* 进而可以进一步采用反射技术得到实体类的信息,可以用于创建数据库中的表
* @param packageName:待扫描包的名字
* @return 指定包下的所有类的名字
*/
public static List<String> getClassName(String packageName){
//存放类全名的集合
List<String> className = new ArrayList<String>();
//当前线程的类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//将传入路径中 全部 . 替换成 /,便于类加载器加载
String src = packageName.replaceAll("\\.", "/");
try {
//得到资源的URL
URL url = classLoader.getResource(src);
//创建指向指定包 的文件对象
File file = new File(url.toURI());
//得到该文件下的所有子文件
File[] files = file.listFiles();
//遍历所有子文件
for (int i = 0; i < files.length; i++) {
getClassName(packageName, files[i], className);
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
return className;
}
//重载方法
private static void getClassName(String packageName, File packageFile,List<String> list) {
//判断是否是文件
if (packageFile.isFile()) {
//是文件时,将该文件的后缀名去除
list.add(packageName+"."+packageFile.getName().replace(".class", ""));
} else {
//不是文件时,继续遍历,直到遍历到文件
File[] files = packageFile.listFiles();
//类的全地址需要加上当前的路径信息。
String temppackagename = packageName + "." + packageFile.getName();
for (int i = 0; i < files.length; i++) {
getClassName(temppackagename, files[i], list);
}
}
}
}
第二,由上步可以得到bean类的全名(包括完整的包名和类名),根据类的信息利用注解拼接创建表的SQL语句。
public static <T> void createTable(Class<T> class1) throws SQLException {
// 创建表的SQL语句
StringBuilder sql = new StringBuilder();
// 子SQL语句
StringBuilder subsql = new StringBuilder();
// 声明一个主键
String primaryKey = null;
// 获取类注解
Table table = class1.getAnnotation(Table.class);
// 获取表的名字
String tablename = table.tablename();
//如果表存在删除原表
//sql.append("DROP TABLE IF EXISTS "+tablename+";");
sql.append("CREATE TABLE" + " " + tablename + "(");
// 得到bean的所有属性
Field[] fileds = class1.getDeclaredFields();
// 遍历属性
for (int i = 0; i < fileds.length; i++) {
// 获得每个属性的注解,如果有没有注解的属性column为空
Column column = fileds[i].getAnnotation(Column.class);
if (column != null) {
/**
* 依次获取各个属性的注解的值
*/
String name = column.columnName().equals("") ? fileds[i].getName() + " " : column.columnName() + " ";
String type = column.columnType();
// 如果用户没有设置表的字段名,那么默认使用属性名
// 日期类型没有长度
String length = column.columnType().contains("date") ? "" : "("
+ column.columnLenght() + ")";
// 为空判断
String isNull = column.isNull() ? " ," : " NOT NULL ,";
subsql.append(name + type + length + isNull);
// 主键判断
if (column.primaryKey()) {
primaryKey = "PRIMARY KEY (" + column.columnName() + ")";
}
}
}
// 加上主键的拼接
if (primaryKey != null) {
subsql.append(primaryKey);// 拼接主键
} else {
subsql.substring(1, subsql.length() - 1);// 去掉最后的 ,
}
sql = sql.append(subsql + ")");
/**
* 开始使用JDBC创建表
*/
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JdbcUtil.createConnection();
preparedStatement = JdbcUtil.createPreparedStatement(connection,sql);
preparedStatement.execute();
} catch (SQLException e) {
preparedStatement = SxtJdbcUtil.createPreparedStatement(connection,"DROP TABLE IF EXISTS "+tablename+";");
preparedStatement.execute();
System.out.println("原表存在,已经删除!创建新表");
preparedStatement = SxtJdbcUtil.createPreparedStatement(connection,sql);
preparedStatement.execute();
} finally {
// 关闭连接
JdbcUtil.closeAll(connection, preparedStatement, null);
}
}
第三,在项目启动时,监听整个项目,启动表的创建工作。
public void contextInitialized(ServletContextEvent event) {
try {
List<String> lists = XMLReader.read();//读取XML文件,得到该文件下的全部节点的值(得到bean对象的包地址)
for (int i = 0; i < lists.size(); i++) {//遍历所有
List<String> listsList = PackageScan.getClassName(lists.get(i).toString());
for (int j = 0; j < listsList.size(); j++) {
createTable(Class.forName(listsList.get(j)));//根据
}
}
} catch (SQLException e) {
System.out.println("删除表在创建失败!");
} catch (ClassNotFoundException e) {
System.out.println("指定类未找到!");
e.printStackTrace();
}
}
补充一下注解:包括表级注解和列级注解。
//列级注解
@Retention(RetentionPolicy.RUNTIME)//作用实效
@Target(ElementType.FIELD)//作用的范围
public @interface Column {
//the default attribute
String value() default "";
//the column'name of table
String columnName() default "";
//the column'length of table
int columnLenght() default 255;
//the cloumn'type of table
String columnType() default "varchar";
//is null
boolean isNull() default false;
//is primary key
boolean primaryKey() default false;
}
//表级注解
public @interface Table {
//default the filed
String value() default "";
//the table name
String tablename() default "";
}
这样,就基本完成了在WEB项目启动时,自动扫描指定包下的Java文件,完成加载和初始化,最后实现表的创建。
首先,是bean对象的配置文件,文件名为DAO.xml
<?xml version="1.0" encoding="UTF-8"?>
<packages>
<!-- DAO的所有包,这里可以将所有的bean配置在此 -->
<package-name>com.csl.student.vo</package-name>
</packages>
其次,对配置文件进行读取,类名为XMLReader。这里的解析用的DOM4j方式。
public class XMLReader {
private static SAXReader saxReader ;
private static Document document ;
// 初始化解析器和文档对象
static {
try {
// 解析器
saxReader = new SAXReader();
// 指定解析哪个xml文件
document = saxReader.read(new File("这里写XML文件的绝对路径"));
} catch (DocumentException e) {
e.printStackTrace();
}
}
//读取父节点下的所有package-name属性下的所有的值,保存到List集合中。
public static List<String> read() {
//声明集合
List<String> packages = new ArrayList<String>();
// 得到根元素
Element root = document.getRootElement();
// 取出 root元素 下的所有package-name元素
List<Element> packageNode= new ArrayList<Element>();
packageNode=
root.elements("package-name");
for (int i = 0; i < packageNode.size(); i++) {
String packages_name = ((Element)packageNode.get(i)).getText();
packages.add(packages_name);
}
return packages;
}
}
然后,根据得到的bean的全名(包名和类名)利用注解和反射创建表。这里分为三个步骤:
第一,根据得到bean所在的包,扫描整个包,得到该包下所有的bean类。扫描包的抽象类为PackageScan,设置成抽象是为了程序的拓展。
public abstract class PackageScan {
/**
* 作用:得到指定包名下的类的全名
* 进而可以进一步采用反射技术得到实体类的信息,可以用于创建数据库中的表
* @param packageName:待扫描包的名字
* @return 指定包下的所有类的名字
*/
public static List<String> getClassName(String packageName){
//存放类全名的集合
List<String> className = new ArrayList<String>();
//当前线程的类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//将传入路径中 全部 . 替换成 /,便于类加载器加载
String src = packageName.replaceAll("\\.", "/");
try {
//得到资源的URL
URL url = classLoader.getResource(src);
//创建指向指定包 的文件对象
File file = new File(url.toURI());
//得到该文件下的所有子文件
File[] files = file.listFiles();
//遍历所有子文件
for (int i = 0; i < files.length; i++) {
getClassName(packageName, files[i], className);
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
return className;
}
//重载方法
private static void getClassName(String packageName, File packageFile,List<String> list) {
//判断是否是文件
if (packageFile.isFile()) {
//是文件时,将该文件的后缀名去除
list.add(packageName+"."+packageFile.getName().replace(".class", ""));
} else {
//不是文件时,继续遍历,直到遍历到文件
File[] files = packageFile.listFiles();
//类的全地址需要加上当前的路径信息。
String temppackagename = packageName + "." + packageFile.getName();
for (int i = 0; i < files.length; i++) {
getClassName(temppackagename, files[i], list);
}
}
}
}
第二,由上步可以得到bean类的全名(包括完整的包名和类名),根据类的信息利用注解拼接创建表的SQL语句。
public static <T> void createTable(Class<T> class1) throws SQLException {
// 创建表的SQL语句
StringBuilder sql = new StringBuilder();
// 子SQL语句
StringBuilder subsql = new StringBuilder();
// 声明一个主键
String primaryKey = null;
// 获取类注解
Table table = class1.getAnnotation(Table.class);
// 获取表的名字
String tablename = table.tablename();
//如果表存在删除原表
//sql.append("DROP TABLE IF EXISTS "+tablename+";");
sql.append("CREATE TABLE" + " " + tablename + "(");
// 得到bean的所有属性
Field[] fileds = class1.getDeclaredFields();
// 遍历属性
for (int i = 0; i < fileds.length; i++) {
// 获得每个属性的注解,如果有没有注解的属性column为空
Column column = fileds[i].getAnnotation(Column.class);
if (column != null) {
/**
* 依次获取各个属性的注解的值
*/
String name = column.columnName().equals("") ? fileds[i].getName() + " " : column.columnName() + " ";
String type = column.columnType();
// 如果用户没有设置表的字段名,那么默认使用属性名
// 日期类型没有长度
String length = column.columnType().contains("date") ? "" : "("
+ column.columnLenght() + ")";
// 为空判断
String isNull = column.isNull() ? " ," : " NOT NULL ,";
subsql.append(name + type + length + isNull);
// 主键判断
if (column.primaryKey()) {
primaryKey = "PRIMARY KEY (" + column.columnName() + ")";
}
}
}
// 加上主键的拼接
if (primaryKey != null) {
subsql.append(primaryKey);// 拼接主键
} else {
subsql.substring(1, subsql.length() - 1);// 去掉最后的 ,
}
sql = sql.append(subsql + ")");
/**
* 开始使用JDBC创建表
*/
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JdbcUtil.createConnection();
preparedStatement = JdbcUtil.createPreparedStatement(connection,sql);
preparedStatement.execute();
} catch (SQLException e) {
preparedStatement = SxtJdbcUtil.createPreparedStatement(connection,"DROP TABLE IF EXISTS "+tablename+";");
preparedStatement.execute();
System.out.println("原表存在,已经删除!创建新表");
preparedStatement = SxtJdbcUtil.createPreparedStatement(connection,sql);
preparedStatement.execute();
} finally {
// 关闭连接
JdbcUtil.closeAll(connection, preparedStatement, null);
}
}
第三,在项目启动时,监听整个项目,启动表的创建工作。
public void contextInitialized(ServletContextEvent event) {
try {
List<String> lists = XMLReader.read();//读取XML文件,得到该文件下的全部节点的值(得到bean对象的包地址)
for (int i = 0; i < lists.size(); i++) {//遍历所有
List<String> listsList = PackageScan.getClassName(lists.get(i).toString());
for (int j = 0; j < listsList.size(); j++) {
createTable(Class.forName(listsList.get(j)));//根据
}
}
} catch (SQLException e) {
System.out.println("删除表在创建失败!");
} catch (ClassNotFoundException e) {
System.out.println("指定类未找到!");
e.printStackTrace();
}
}
补充一下注解:包括表级注解和列级注解。
//列级注解
@Retention(RetentionPolicy.RUNTIME)//作用实效
@Target(ElementType.FIELD)//作用的范围
public @interface Column {
//the default attribute
String value() default "";
//the column'name of table
String columnName() default "";
//the column'length of table
int columnLenght() default 255;
//the cloumn'type of table
String columnType() default "varchar";
//is null
boolean isNull() default false;
//is primary key
boolean primaryKey() default false;
}
//表级注解
public @interface Table {
//default the filed
String value() default "";
//the table name
String tablename() default "";
}
这样,就基本完成了在WEB项目启动时,自动扫描指定包下的Java文件,完成加载和初始化,最后实现表的创建。
相关文章推荐
- MySQL简介
- SQL查询结果导出到Excel中
- mysql 慢查询
- 从数据库中往solr中导入数据之1
- Spark Sql,Dataframe和数据集指南
- mac版MySQL初始密码或者自己的密码忘了怎么办?
- MySQL 5.6 for Windows 解压缩版配置安装
- Oracle自定义聚集函数
- SQL Server数据库恢复备份
- MyBatis调用Oracle存储过程
- 数据库知识点②
- MySql学习笔记_数据表的创建。
- MySql学习笔记_数据表的创建。
- PL/SQL Developer中SQL语句如何格式化
- mysql时间格式化,按时间段查询的MySQL语句
- 数据库的基本定义及方法
- 如何使用php文件测试mongodb是否安装成功
- mysql 事务级别
- 周SIR习得技巧点 - 如何将业务数据(EXCLE)导入到ORACLE数据库中
- mysql详解--mysql中的锁