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

来源:互联网 发布:詹姆斯退役数据预测 编辑:程序博客网 时间:2024/06/15 16:17

引言

来到公司的第一个任务是使用公司的框架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,但是讲述代码生成的文章还比较少,所以做个笔记。

原创粉丝点击