文档章节

DBGenerator:获取PO注解信息生成表sql

晓叹星沉
 晓叹星沉
发布于 2016/08/16 15:31
字数 1071
阅读 2545
收藏 68

        开发过程中,常常创建了PO之后,还要去数据库里创建对应的表,相似的过程,却要做两次,有没有感觉很麻烦?使用Hibernate可以生成表,还要去配置,而且无法得到表创建sql,于是 DBGenerator 应运而生了。DBGenerator是一个工具类,能获取PO类的注解信息,自动生成表创建SQL,节省了很多时间,同时也能保证测试环境和生产环境表的一致性。

1. jar包依赖

    commons-lang-2.6.jar

    hibernate3.jar

2. 源码

package com.cg.db;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;

import org.apache.commons.lang.StringUtils;

import com.sun.xml.internal.bind.v2.model.core.ID;

/** 
 *
 * <p>根据PO生成表创建sql<p>
 * <p>根据read方法上的注解,生成数据库字段信息,没有read方法的Field跳过<p>
 * @since jdk1.6
 * @date 2016-8-15
 */
public class DBGenerator {

	/**
	 * 
	 * 根据类的注解信息生成表sql
	 * @param clazz
	 * @return
	 */
	public String generate(Class clazz){
		if(!clazz.isAnnotationPresent(Table.class)){
			throw new RuntimeException("No Annotation[Table] founded in class["+clazz.getName()+"]");
		}
		Table table = (Table)clazz.getAnnotation(Table.class);
		String tableName = table.name();
		StringBuilder sb = new StringBuilder("-- Create table\n");
		sb.append("create table " + tableName + "\n");
		sb.append("(\n");
		Field[] fieldArr = clazz.getDeclaredFields();
		int maxLength = getMaxLength(clazz, fieldArr);
		String primaryKey = generateFields(clazz, sb, fieldArr, maxLength);
		sb.append(");\n");
		generatePrimaryKey(sb, tableName, primaryKey);
		return sb.toString();
	}
	 
	/** 
	 * 生成sql字段
	 * @param clazz
	 * @param sb
	 * @param fieldArr
	 * @param primaryKey
	 * @param maxLength
	 * @return  
	 */
	  	
	private String generateFields(Class clazz, StringBuilder sb,
			Field[] fieldArr, int maxLength) {
		String primaryKey = "";
		Field field;
		for(int i=0;i<fieldArr.length;i++){
			field = fieldArr[i];
			int offset;
			try {
				PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
				Method mt = pd.getReadMethod();
				if(null != mt){
					Column col = (Column)mt.getAnnotation(Column.class);
					writeBlankSpace(sb, 2);
					sb.append(col.name());
					offset = maxLength - col.name().length();
					writeBlankSpace(sb, offset);
					writeBlankSpace(sb, 1);
					sb.append(convertJava2Oracle(field, col));
					if(mt.isAnnotationPresent(Id.class)){
						sb.append(" not null");
						primaryKey = col.name();
					}
				}
			} catch (IntrospectionException e) {
				//找不到readMethod,跳过
				//throw new RuntimeException("No " + field.getName() + "'s ReadMethod founded in class["+clazz.getName()+"]");
				continue;
			}
			if(i != (fieldArr.length -1)){
				sb.append(",");
			}
			sb.append("\n");
		}
		return primaryKey;
	}
	 
	/** 
	 * 生成主键sql
	 * @param tableName
	 * @param sb
	 * @param primaryKey  
	 */
	  	
	private void generatePrimaryKey(StringBuilder sb, String tableName, String primaryKey) {
		if(StringUtils.isNotBlank(primaryKey)){
			sb.append("alter table " + tableName + "\n");
			sb.append("  add primary key (" + primaryKey + ")\n");
			sb.append("  using index;");
		}
	}
	 
	/** 
	 * 获得Field最大长度
	 * @param clazz
	 * @param fieldArr  
	 */
	  	
	private int getMaxLength(Class clazz, Field[] fieldArr) {
		Field field;
		int maxLength = 0;
		for(int i=0;i<fieldArr.length;i++){
			field = fieldArr[i];
			PropertyDescriptor pd;
			try {
				pd = new PropertyDescriptor(field.getName(), clazz);
				Method mt = pd.getReadMethod();
				if(null != mt){
					if(!mt.isAnnotationPresent(Column.class)){
						throw new RuntimeException("No Annotation[Column] founded in Method["+mt.getName()+"]");
					}
					Column col = (Column)mt.getAnnotation(Column.class);
					int temLength = col.name().length();
					if(temLength > maxLength){
						maxLength = temLength;
					}
				}
			} catch (IntrospectionException e) {
				//找不到readMethod,跳过
				//throw new RuntimeException("No " + field.getName() + "'s ReadMethod founded in class["+clazz.getName()+"]");
				continue;
			}
		}
		return maxLength;
	}
	
	/**
	 * 
	 * 将java类型转换为orable类型
	 * @param field
	 * @param col
	 * @return
	 */
	private String convertJava2Oracle(Field field, Column col){
		//TODO 此处可根据自己需要做类型转换
		String type = "VARCHAR2";
		if(field.getType().equals(String.class)){
			type = "VARCHAR2(" + col.length() + ")";
		}else if(field.getType().equals(java.util.Date.class) || field.getType().equals(java.sql.Date.class)){
			type = "DATE";
		}else if(field.getType().equals(Integer.class)){
			type = "NUMBER";
		}
		return type;
	}
	
	/**
	 * 
	 * 输出空格
	 * @param sb
	 * @param n
	 */
	private void writeBlankSpace(StringBuilder sb, int n){
		for(int i=0;i<n;i++){
			sb.append(" ");
		}
	}
}

3. 事例

    1.TBankCode类,使用了注解@Table、@Id、@Column标识了表名、主键、列信息, DBGenerator能读取类的注解信息,生成对应sql。(ps:由于本人习惯将 Column加到get方法上,在开发过程中限定DBGenerator 只读取get方法上的注解,此外对应数据库(oracle)的类型只做了几个常用的转换,可根据自己需要调整。)

package com.cg.db;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

@Table(name = "T_BANK_CODE")
public class TBankCode implements java.io.Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -9047055962822128897L;
	/**
	 * 主键id
	 */
	private String id;
	/***省名 */
	private String province;
	/*** 省代码*/
	private String provinceNo;
	/*** 地区名 */
	private String area;
	/*** 地区代码*/
	private String areaNo;
	/*** 银行行名称*/
	private String bankName;
	/*** 银行号*/
	private String bankNo;
	/*** 分行名称 */
	private String branchBankName;
	/*** 分行联行号*/
	private String branchBankCode;
	
	
	@Id
	@Column(name = "ID", unique = true, nullable = false, length = 40)
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	@Column(name = "PROVINCE", length = 100)
	public String getProvince() {
		return province;
	}

	public void setProvince(String province) {
		this.province = province;
	}

	@Column(name = "AREA", length = 100)
	public String getArea() {
		return area;
	}

	public void setArea(String area) {
		this.area = area;
	}
	
	@Column(name = "AREA_NO", length = 10)
	public String getAreaNo() {
		return areaNo;
	}

	public void setAreaNo(String areaNo) {
		this.areaNo = areaNo;
	}

	@Column(name = "PROVINCE_NO", length = 10)
	public String getProvinceNo() {
		return provinceNo;
	}

	public void setProvinceNo(String provinceNo) {
		this.provinceNo = provinceNo;
	}

	@Column(name = "BANK_NAME", length = 100)
	public String getBankName() {
		return bankName;
	}

	public void setBankName(String bankName) {
		this.bankName = bankName;
	}

	@Column(name = "BANK_NO", length = 30)
	public String getBankNo() {
		return bankNo;
	}

	public void setBankNo(String bankNo) {
		this.bankNo = bankNo;
	}

	@Column(name = "BRANCH_BANK_NAME", length = 100)
	public String getBranchBankName() {
		return branchBankName;
	}

	public void setBranchBankName(String branchBankName) {
		this.branchBankName = branchBankName;
	}

	@Column(name = "BRANCH_BANK_CODE", length = 30)
	public String getBranchBankCode() {
		return branchBankCode;
	}

	public void setBranchBankCode(String branchBankCode) {
		this.branchBankCode = branchBankCode;
	}
}

    2.实例化 DBGenerator 调用generate方法,传递参数TBankCode.class

package com.cg.db;

public class TestRun {

	public static void main(String[] args) {
		String a = new DBGenerator().generate(TBankCode.class);
		System.out.println(a);
	}
}

    3.运行结果

-- Create table
create table T_BANK_CODE
(
  ID               VARCHAR2(40) not null,
  PROVINCE         VARCHAR2(100),
  PROVINCE_NO      VARCHAR2(10),
  AREA             VARCHAR2(100),
  AREA_NO          VARCHAR2(10),
  BANK_NAME        VARCHAR2(100),
  BANK_NO          VARCHAR2(30),
  BRANCH_BANK_NAME VARCHAR2(100),
  BRANCH_BANK_CODE VARCHAR2(30)
);
alter table T_BANK_CODE
  add primary key (ID)
  using index;

 

© 著作权归作者所有

共有 人打赏支持
晓叹星沉
粉丝 12
博文 22
码字总数 12552
作品 0
海淀
高级程序员
加载中

评论(17)

jack_jingyun
jack_jingyun
用python比这简单多了
最初幻想
最初幻想
hibernate.hbm2ddl.auto 自动创建表结构就可以了
javacc
javacc
留意下
魔力猫
魔力猫

引用来自“魔力猫”的评论

表面上看似乎节约了点时间,但是考虑到数据库表和对象并不是简单1:1的关系,实际上还要重新对所有SQL进行检查,更不用说整个数据库的EL图等等需要额外的编写。考虑到这些因素,这个工具的作用实际没想象中那么大,不过还是有点借鉴意义。

引用来自“晓叹星沉”的评论

确实是只支持1:1关系的,平时遇到的都是一个PO一个表,1对多的对象好像更偏向于业务对象了,平时都是采用多个PO关联的
而且PO还有继承等问题。
晓叹星沉
晓叹星沉

引用来自“飘逸的逸”的评论

我一般是用Hibernate生成表,然后数据库导出所有表结构备份,然后特殊的情况再调整.
是的,可以用Hibernate生成的话就方便多了
飘逸的逸
飘逸的逸
我一般是用Hibernate生成表,然后数据库导出所有表结构备份,然后特殊的情况再调整.
晓叹星沉
晓叹星沉

引用来自“魔力猫”的评论

表面上看似乎节约了点时间,但是考虑到数据库表和对象并不是简单1:1的关系,实际上还要重新对所有SQL进行检查,更不用说整个数据库的EL图等等需要额外的编写。考虑到这些因素,这个工具的作用实际没想象中那么大,不过还是有点借鉴意义。
确实是只支持1:1关系的,平时遇到的都是一个PO一个表,1对多的对象好像更偏向于业务对象了,平时都是采用多个PO关联的
晓叹星沉
晓叹星沉

引用来自“linziguan”的评论

备注也可以加上就好了
后期有时间的时候可以加上
晓叹星沉
晓叹星沉

引用来自“邹海彬”的评论

hibernate就支持code first啊
项目用的Hibernate版本有点低,不支持根据注解生成17
晓叹星沉
晓叹星沉

引用来自“leh”的评论

请问,我要添加字段默认值和字段备注怎么加?
可以自定义一个注解,包含默认值和备注,运行时获取一下。
由代码生成数据库结构,保障设计正常传递到实现

我们常常使用代码生成工具来生成表结构。为什么不试试从代码来生成数据库表结构呢? 这个观点有些匪夷所思,但是熟悉Hibernate的同志应该有所体会。Hibernate有一个功能,叫做hbm2ddl,在系统...

土豆饼
2016/06/16
1K
14
smjdbctemplate 基于 spring jdbctemplate 做的升级版

GitHub地址:https://github.com/yinjihuan/smjdbctemplate 大家自己下载源码编译安装到本地仓库即可使用,当前版本号为1.0.2 比jdbctemplate有哪些优势 重新定义了CxytiandiJdbcTemplate类,...

尹吉欢
2017/12/31
0
0
《AOSuite G1开发手册》之AOSuite G1服务端开发

《AOSuite G1开发手册》http://git.oschina.net/osworks/AOS/wikis/home AOSuite G1后端主要依赖被广泛使用的Spring和MyBatis框架。因此,大家的技术积累和经验在这里一样是通用的。所以,我...

神盾局-局座
2017/07/09
145
0
JdbcTemplateTool

Spring 出品的 JdbcTemplate 对于不想使用hibernate或者ibatis那样需要大量学习成本而且还想获得对象化的人来说是很好用的。但是 JdbcTemplate还是有很多不足之处或者说是缺点。比如你没法像...

alexxiyang
2014/09/25
3.1K
0
Lemur开源/lemur-generation

lemur-generation 思考业务,代码它写 Gen是我构思了挺久的一个代码生成项目,之前零零散散也写了不少代码生成,但是很多都不太理想不能够满足通用性和特殊定制性的需求,每次生成出来还要改一部...

Lemur开源
2017/10/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

spring-boot | 日志

引言 好久不见,都还好吗? 大家都知道,我一般都是带来实用的东西,这次也一样,我们来试试项目开发中的日志处理。 理论知识 1、为什么要用日志?你是否因为项目出现问题,查找日志文件定位...

成都_小冯同学
8分钟前
0
0
dubbo下的补偿实现(一)

背景 由于前面几篇文章涉及的问题 分布式事务的思考 在我们微服务的场景下 各种本地事务都没有办法控制的很好。 A服务调用B服务 当B服务成功提交之后 A发生了异常 这种情况下要如何处理呢??...

Mr_Qi
17分钟前
2
0
HTML显示json字符串并且进行格式化

通过pre标签进行格式化展示,使用JSON.stringify()方法转换。 代码如下:   <html> <head> <title>HTML显示json字符串并且进行格式化</title> </head> <body> <p id="show_p">{ "name": "B......

writeademo
38分钟前
0
0
LNMP——php-fpm

php-fpm的pool •vim /usr/local/php/etc/php-fpm.conf //在[global]部分增加include并删除我们之前www池子 • include = etc/php-fpm.d/*.conf • mkdir /usr/local/php/etc/php-fpm.d/ • ......

chencheng-linux
41分钟前
0
0
TensorFlow layers slim 模块搭建cnn对mnist分类,比较bn效果

shape变化 (?, 784)(?, 28, 28, 1)(?, 14, 14, 8)(?, 7, 7, 8)(?, 1, 1, 8)(?, 8)(?, 10) 结果对比,基本上还是有点用的 0.91725457 0.9232 0.9548 0.95530.9177273 0.9234 0....

阿豪boy
44分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部