如何根据数据库文件自动生成整个bean层与service层

引言

来到公司的第一个任务是使用公司的框架leap(http://leapframework.org/ ORM层真的非常牛逼)进行项目开发,在这个框架中它规定了自己的javabean的注解(如使用@Id描述主键,用@Column描述字段),从而达到像hibernate中的那种持久化对象的功能,虽然ORM层是非常好用,但是写javabean和注解确实非常麻烦的一件事情(因为不是jpa规范,所有无法使用hibernate-tool)等工具来创建javabean,即使创建了普通的javabean,也要在字段上面写上注解,非常麻烦。那如何写一个工具来帮我们完成这些繁琐的操作呢?

设计思路

首先需要先对javabean进行抽象

数据库表名->类名
数据库字段->类字段
数据库类型->java类型
数据库主键->需要做标志,使用特殊的注解

//其他需要传入的参数
类的包名
生成的文件地址

有了上述的思路,那么就是说只要有

数据库文件
一套javabean的模版

便能动态的来进行一个数据库到javabean的转换

技术选型

1.数据库访问
数据库访问方面直接使用JDBC,因为不会出现频率性的数据库访问,所以使用JDBC就够了,在这里我们需要收集的信息是

//数据库里面所有的表
show tables;
//根据表名,获取表里面的所有字段(包括字段类型,字段是否主键等信息)
desc your_table_name;

2.创建模版与模版信息替换
在这里我曾经想过使用正则表达式来解决这个问题,自己定义自己的表达式进行替换,后面在收集想法时,发现一个牛逼的框架 freemarks 可以帮我解决这个问题。

你可以先看文档,也可以参考一下我对这个框架的理解再去看文档,

首先,先说一下freemarks这个框架设计出来的目的,其实他设计出来的目的是为了代替jsp这样的渲染引擎的。

jsp模版渲染引擎使用的思路大概是这样:
变化的值存进request,response,session,jsp引擎解析jsp文件里面的表达式,再去request,response,session取到相关表达的值构建整个页面,再输出到response里面给用户。整个流程都与request,response,session等对象结合在一起。

用过spring mvc 的你应该知道mvc的优点是他在control层用到一个model的对象去帮你封装你要传递给request的值,对用户抽象,让你感觉是

值->model->传递(request..)->jsp->动态页面

但其实底层还是用的request。

那么freemarks的出现就是为了解决这个问题,他提出的两个概念就是“模型”与“模版”。他提出这么一个流程

值->model->传递->模版->动态页面

可见的是它不依赖request等对象,完全封装了一套自己的东西。
freemarks 能不能替代jsp我不能断定,但是和我们的需求是非常切合的。
这个时候你就需要通读一下文档(其实跟El表达式差不多),准备开发。

3.文件存储
使用IO就能完成这个功能,但是我想玩得花一点,就使用了NIO,使用一个内存映射来完成整个功能。

实际开发

先发一下git地址,以防一些地方讲的不全https://git.oschina.net/jjtHappy/leap-tools
先从下往上讲

首先就是数据库链接,普通的JDBC链接不用介绍,取表名,根据表名取字段的sql已经讲了

主要是如何进行的操作

数据库表名->类名
数据库字段->类字段
数据库类型->java类型
数据库主键->需要做标志,使用特殊的注解

首先,表名转类名,很简单,只是一个格式的转换

/**
     * 
     * Title:把传入的字段转为驼峰
     * Description:
     * @param table 传入的字段
     * @param mod 切割字符
     * @return
     * @author jjtEatJava
     * @date 2017年8月8日
     */
    public static String toCamel(String string,String mod) {
        String[] tableNameSplit = string.split(mod);
        StringBuffer entityName = new StringBuffer();
        for(String s : tableNameSplit) {
            char[] cs=s.toLowerCase().toCharArray();
            cs[0]-=32;
            entityName.append(String.valueOf(cs));
        }
        return entityName.toString();
    }

表字段转javabean同理,开头字母转小写就行了

数据库类型转java类型,其实也是一个很简单的操作

    /**
     * Title:将数据库类型转为java类型
     * Description:
     * @param tableType 数据库类型
     * @return
     * @author jjtEatJava
     * @date 2017年7月30日
     */
    public static String tableTypeToJavaType(String tableType) {
        //去括号
        int lastIndex = tableType.lastIndexOf("(");
        tableType = tableType.substring(0, lastIndex==-1?tableType.length():lastIndex).toLowerCase();
        String javaType;
        switch(tableType) {
            case    "char":
            case    "varchar":
            case    "tinytext":
            case    "text":
            case    "mediumtext":
            case    "longtext":
                javaType="String";
                break;
            case    "tinyblob":
            case    "blob":
            case    "mediumblob":
            case    "longblob":
                javaType="byte[]";
                break;      
            case    "tinyint":
            case    "smallint":
            case    "mediumint":
            case    "int":
            case    "bigint":
                javaType="Integer";
                break;
            case    "float":
                javaType="float";
                break;
            case    "double":
                javaType="double";
                break;
            case    "decimal":
                javaType="BigDecimal";
                break;
            case    "date":
            case    "time":
            case    "year":
            case    "datetime":
            case    "timestamp":
                javaType="Date";
                break;
            default:
                throw new RuntimeException("无法解析"+tableType+"类型");
        }
        return javaType;
    }

主要你可以了解一下mysql类型与java类型的对照表,写个switch就行了

为了方便,你可以写个javabean,在数据库取出时,就进行一个封装

public class Field {

    private String tableField;//数据库字段名
    private String tableType;//数据库类型
    private String tableKey;//索引类型

    private String javaField;//java字段名
    private String javaType;//java类型
    ....
}

这里如何判断是否是主键,只要判断索引类型有没有带“PRI”字段即可

封装一个好用的dao层是对上层是非常方便的,具体代码可以查看SimpleDao

解决了数据获取问题,接下来就是解决模版问题,这个就是看你对javabean的抽象了,当你看完文档后你可以看看我写的模版(结合了框架的一些内容)

package ${packageName}.bean;

<#list importPacks as importPack>
import ${importPack};
</#list>
import leap.orm.annotation.Column;
import leap.orm.annotation.Id;
import leap.orm.annotation.Table;
import leap.orm.model.Model;

@Table("${tableName}")
public class ${entityName} extends Model implements java.io.Serializable {
    <#-- 构造属性 -->
    <#list fields as field>
    <#if field.tableKey?contains("PRI")>
    @Id
    </#if>
    @Column(name="${field.tableField}")
    private ${field.javaType} ${field.javaField?uncap_first};
    </#list>

    <#-- 构造set and get -->
    <#list fields as field >
    <#-- set -->
    public void set${field.javaField?cap_first}(${field.javaType} ${field.javaField?uncap_first}){
        this.${field.javaField?uncap_first}=${field.javaField?uncap_first};
    }
    <#-- get -->
    public ${field.javaType} get${field.javaField?cap_first}(){
        return this.${field.javaField?uncap_first};
    }
    </#list>
}

可以看模版非常简单,要求的数据模型只是

包名
导入的包
数据库表名
实体名(就是表名的驼峰转化)
上述封装的域列表(用freemarks表达式进行遍历)

这里需要注意的点是,如何决定导入的包,所以你需要考虑几个点

1.根据java类型寻找包,并获取包名
2.包名不能重复

所以有了这个想法,可以通过下面的方法实现

    /**
     * 
     * Title:根据所有的域获取导包列表
     * Description:
     * @param fields bean里面的所有的域
     * @return
     * @author jjtEatJava
     * @date 2017年8月8日
     */
    public  static Set<String> getPackByFields(List<Field> fields) {
        Set<String> set = new HashSet<String>();
        for(Field field:fields) {
            String javaType = field.getJavaType();
            String packageName=null;
            switch(javaType) {
                case "String":
                    packageName="java.lang.String";
                    break;
                case "Integer":
                    packageName="java.lang.Integer";
                    break;
                case "Double":
                    packageName="java.lang.Double";
                    break;
                case "Float":
                    packageName="";
                    break;
                case "byte[]":
                    packageName="";
                    break;
                case "Date":
                    packageName="java.util.Date";
                    break;
                case "BigDecimal":
                    packageName="java.math.BigDecimal";
                    break;
                default :
                packageName="";
            }
            set.add(packageName);
        }
        return set;
    }

完成了模版设计,就是模型的构建与传递了,传递过程都是按着freemarks的来,再使用NIO进行文件创建
主要关键的方法如下:

 /**
      * 
      * Title:创建文件
      * Description:
      * @param temp 模版
      * @param model 数据模型
      * @param basePah 基础路径
      * @param fileName 文件名
      * @throws TemplateException
      * @throws IOException
      * @throws FileNotFoundException
      * @author jjtEatJava
      * @date 2017年8月8日
      */
     private void createFile(Template temp, Map<String, Object> model,String basePah, String fileName)
            throws TemplateException, IOException, FileNotFoundException {
        //创建文件夹
        File baseDir = new File(basePah);
        if(!baseDir.exists())
            baseDir.mkdirs();
        File file =new File (baseDir,fileName);
        if(file.exists()) {
            file.delete();
            file.createNewFile();
        }else {
            file.createNewFile();
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Writer writer = new OutputStreamWriter(out);
        //把模版加载到内存
        temp.process(model,writer);
        //获取缓存字符数组
        byte[] byteBuff = out.toByteArray();
        RandomAccessFile fileOutput= new RandomAccessFile(file,"rw");
        FileChannel chn =fileOutput.getChannel();
        //隧道 映射
        MappedByteBuffer buff = chn.map(FileChannel.MapMode.READ_WRITE, 0,byteBuff.length);
        for(int i=0;i<byteBuff.length;i++) {
            buff.put(byteBuff[i]);
        }
        out.close();
        fileOutput.close();
    }

详细可参照代码。

这样的一个思路便可以让你完成整个dao层的创建
按着这个思路你可以完成整个service层的创建
以下是我写的工具的生成结果

- H:\test4\leap
  - bean
    - LeapPost.java
    - LeapUser.java
    - LeapUserPost.java
  - service
    - BaseService.java
    - impl
      - BaseServiceImpl.java
      - LeapPostServiceImpl.java
      - LeapUserPostServiceImpl.java
      - LeapUserServiceImpl.java
    - LeapPostService.java
    - LeapUserPostService.java
    - LeapUserService.java

你说这篇文章给不了你干货,但真正的干货我就不信能在一篇文章中看出来的。老老实实看文档,根据本文给出的思路,一步一步才能创建自己的一个基础代码撰写工具,有了这个工具至少提高了10%的工作效率。

后面看了一些hibernate-tools的源码…发现…它用的也是freemarks,但是讲述代码生成的文章还比较少,所以做个笔记。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值