实现基于注解的 mybatis,并基于此编写代码生成工具

实现基于注解的 mybatis

关于这个问题,请大家移步我的个人博客(http://www.kfyty.com/info.html?article=29),就不在这里重新写一遍了。当然博客里的是第一版的实现,经过多次迭代,最新版本已经面目全非,这里放上我的 github 地址,感兴趣的可以去看一下:https://github.com/kfyty/kfyty-utils

这里就列一下已经实现的功能吧

  1. 实现了基于注解的映射,主要有三个注解:@Query,@Execute,@Param
  2. @Query 内支持子查询注解(@SubQuery),可以用来为实体内的某个其他实体编写 sql 来查询数据(可惜 java 不支持循环注解)
  3. @Query,@Execute,@SubQuery 内均支持 @ForEach 注解,用来拼接 sql 语句
  4. 自动推断返回值类型,包括基本数据类型及其数组、List、Set、Map<Key, Entity>、Map<Object, Object>、List<Map<Object, Object>>
  5. 支持重复注解,每个注解的结果集将放入一个 List 返回
  6. 支持 #{}、${} 操作符,操作符中的表达式支持 ‘.’操作符,以访问对象属性
  7. sql 语句中的别名支持 ‘.’操作符,以支持直接给对象中的某个对象的属性赋值

使用实现的 mybatis 编写自己的代码生成工具

基本使用演示

1、使用内置的 java 模板类生成

  1. 首先创建一张测试表,如图:
    在这里插入图片描述
  2. 编写数据源配置类:
    这里的@Configuration 和 @Bean 注解,以及下面的 @AutoWired 注解,不是 Spring 的注解,是自定义的,自己实现的(PS:关于数据源配置文件大家自己添加就好)
package com.kfyty.demo.config;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.kfyty.configuration.annotation.Bean;
import com.kfyty.configuration.annotation.Configuration;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.util.Properties;

/**
 * 功能描述: 数据源配置
 *
 * @author [email protected]
 * @date 2019/9/07 21:38
 * @since JDK 1.8
 */
@Slf4j
@Configuration
public class DataSourceConfig {

    private static final String PATH = "/druid.properties";

    @Bean
    public DataSource getDruidDataSource() {
        try {
            Properties properties = new Properties();
            properties.load(DataSourceConfig.class.getResourceAsStream(PATH));
            return DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            log.error("load properties failed:", e);
        }
        return null;
    }
}
  1. 编写生成代码配置类:
package com.kfyty.demo.config;

import com.kfyty.configuration.annotation.AutoWired;
import com.kfyty.configuration.annotation.Configuration;
import com.kfyty.generate.configuration.GenerateConfiguration;
import com.kfyty.generate.configuration.annotation.BasePackage;
import com.kfyty.generate.configuration.annotation.DataBase;
import com.kfyty.generate.configuration.annotation.DataBaseMapper;
import com.kfyty.generate.configuration.annotation.FilePath;
import com.kfyty.generate.configuration.annotation.GenerateTemplate;
import com.kfyty.generate.configuration.annotation.Table;
import com.kfyty.generate.database.MySQLDataBaseMapper;
import com.kfyty.generate.template.entity.EntityTemplate;

import javax.sql.DataSource;

@Configuration
@DataBase("test")
@Table("user")
@DataBaseMapper(MySQLDataBaseMapper.class)
@FilePath("D:/temp/code-generate")
@BasePackage("com.kfyty")
@GenerateTemplate(EntityTemplate.class)
public class GenerateConfig implements GenerateConfiguration {
    @AutoWired
    private DataSource dataSource;

    @Override
    public DataSource getDataSource() {
        return this.dataSource;
    }
}
  1. 编写 Main 方法,开始生成代码:
package com.kfyty.demo;

import com.kfyty.KfytyApplication;
import com.kfyty.configuration.annotation.EnableAutoGenerateSources;
import com.kfyty.configuration.annotation.KfytyBootApplication;

/**
 * 功能描述: 生成代码主方法
 *
 * @author [email protected]
 * @date 2019/9/07 21:58
 * @since JDK 1.88
 */
@KfytyBootApplication
@EnableAutoGenerateSources(loadTemplate = false)
public class Main {
    public static void main(String[] args) throws Exception {
        KfytyApplication.run(Main.class);
    }
}
  1. 看一下日志:
12:11:31.859 [main] DEBUG com.kfyty.parser.ClassAnnotationParser - : found component: [class com.kfyty.demo.config.DataSourceConfig] !
12:11:31.886 [main] DEBUG com.kfyty.parser.ClassAnnotationParser - : found component: [class com.kfyty.demo.config.GenerateConfig] !
12:11:31.952 [main] DEBUG com.kfyty.parser.MethodAnnotationParser - : found bean resource: [interface javax.sql.DataSource] !
12:11:31.957 [main] DEBUG com.kfyty.parser.FieldAnnotationParser - : found auto wired: [interface javax.sql.DataSource] !
12:11:31.982 [main] DEBUG com.kfyty.configuration.ApplicationConfigurable - : auto configuration after check success !
12:11:32.480 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
12:11:33.230 [main] DEBUG com.kfyty.util.JdbcUtil - : ==>  Preparing: select table_schema dataBaseName, table_name, table_comment from information_schema.tables where table_schema = ?
12:11:33.231 [main] DEBUG com.kfyty.util.JdbcUtil - : ==> Parameters: [test]
12:11:33.252 [main] DEBUG com.kfyty.util.JdbcUtil - :                <==      Total: 2 [class java.util.ArrayList]
12:11:33.280 [main] DEBUG com.kfyty.util.JdbcUtil - : ==>  Preparing: select table_name, column_name field, data_type fieldType, if(column_key = 'PRI', 'true', 'false') primaryKey, if(is_nullable = 'YES', 'true', 'false') nullable, column_comment fieldComment from information_schema.COLUMNS where table_schema = ? and table_name = ?
12:11:33.280 [main] DEBUG com.kfyty.util.JdbcUtil - : ==> Parameters: [test, user]
12:11:33.284 [main] DEBUG com.kfyty.util.JdbcUtil - :                <==      Total: 3 [class java.util.ArrayList]
12:11:33.284 [main] DEBUG com.kfyty.generate.GenerateSources - : initialize data base info success !
12:11:33.310 [main] DEBUG com.kfyty.generate.GenerateSources - : generate resource:[User.java] success --> [D:\temp\code-generate\com\kfyty\User.java]

Process finished with exit code 0
  1. 查看生成的代码:
package com.kfyty;

import java.util.Date;

import lombok.Data;

/**
 * TABLE_NAME: user
 * TABLE_COMMENT: 
 *
 * @author kfyty
 * @email [email protected]
 */
@Data
public class User {
	/**
	 * 主键
	 */
	private Integer id;

	/**
	 * 用户名
	 */
	private String username;

	/**
	 * 创建时间
	 */
	private Date createTime;

}

2、自定义 freemarker 模板生成

1、在 resource 文件夹下创建 template 文件夹

2、在 template 文件夹下创建 my_template1 文件夹。
因为我对 freemarker 模板的命名是有规范的:
比如 dao.java.ftl,则代表生成的文件类型是 java,类名后缀是 Dao。
比如 entity_NoSu.java.ftl,代表生成的文件类型是 java,类名没有后缀
比如 mapper.xml.ftl,代表生成的文件类型是 xml,文件名后缀为 Mapper

所以,如果有多套模板的话,命名就会有冲突,因此需要多建一层文件夹,作为模板前缀,以区分是哪一套模板

3、创建 entity_NoSu.java.ftl

package ${basePackage}

import java.util.Date;

import lombok.Data;

/**
* TABLE_NAME: ${table}
* TABLE_COMMENT: ${note}
*
* @author kfyty
* @email [email protected]
*/
@Data
public class ${className} {
    protected ${pkField.fieldType} ${pkField.field};

    <#list fields as field>
    /**
    * ${columns[field_index].field}: ${field.fieldComment}
    */
    protected ${field.fieldType} ${field.field};

    </#list>
}

4、在 resources 文件夹下创建配置文件 code-generate.properties
编写的模板还需要在这里配置才能被读取;另外在这里配置的变量,都可以在 freemarker 模板中直接使用

basePackage=com.kfyty

my_template1.template=\
  entity_NoSu.java.ftl

5、相应的启动类如下

package com.kfyty.demo;

import com.kfyty.KfytyApplication;
import com.kfyty.configuration.annotation.EnableAutoGenerateSources;
import com.kfyty.configuration.annotation.KfytyBootApplication;

/**
 * 功能描述: 生成代码主方法
 *
 * @author [email protected]
 * @date 2019/9/07 21:58
 * @since JDK 1.88
 */
@KfytyBootApplication
@EnableAutoGenerateSources(templatePrefix = "my_template1")
public class Main {
    public static void main(String[] args) throws Exception {
        KfytyApplication.run(Main.class);
    }
}

6、生成的模板如下:

package com.kfyty

import java.util.Date;

import lombok.Data;

/**
* TABLE_NAME: user
* TABLE_COMMENT: 
*
* @author kfyty
* @email [email protected]
*/
@Data
public class User {
    protected Integer id;

    /**
    * username: 用户名
    */
    protected String username;

    /**
    * create_time: 创建时间
    */
    protected Date createTime;

}

自定义生成模板

freemarker 模板本来就是高度自定义的,所以这里只介绍基于 java 类的自定义模板。

1.1 自定义生成模板

上例中使用的是我编写的默认的生成模板,如果不满足需要的话,可以继承 EntityTemplate 以自定义模板:

package com.kfyty.demo.config;

import com.kfyty.configuration.annotation.Component;
import com.kfyty.generate.info.AbstractDataBaseInfo;
import com.kfyty.generate.template.entity.EntityTemplate;
import com.kfyty.util.CommonUtil;

import java.io.IOException;

/**
 * 添加自定义的 @Component 注解即可生效
 */
@Component
public class EntityTemplateConfig extends EntityTemplate {
    @Override
    public String classSuffix() {
        return "Entity";
    }

    @Override
    public String generateExtendsClass(AbstractDataBaseInfo dataBaseInfo) throws IOException {
        return CommonUtil.fillString("AbstractEntity<{}, Integer>", this.className);
    }

    @Override
    public String generateImplementsInterfaces(AbstractDataBaseInfo dataBaseInfo) throws IOException {
        return "IEntity<Integer>";
    }
}

看一下生成的代码:

package com.kfyty.entity;

import java.util.Date;

import lombok.Data;

/**
 * TABLE_NAME: user
 * TABLE_COMMENT: 
 *
 * @author kfyty
 * @email [email protected]
 */
@Data
public class UserEntity extends AbstractEntity<UserEntity, Integer> implements IEntity<Integer> {
	/**
	 * 主键
	 */
	private Integer id;

	/**
	 * 用户名
	 */
	private String username;

	/**
	 * 创建时间
	 */
	private Date createTime;

}

1.2. 自定义表结构元数据:

1、编写元数据承载实体类,比如我需要 jdbcType,那么拓展的元数据实体类如下:

package com.kfyty.demo.config;

import com.kfyty.generate.info.AbstractTableInfo;

public class TableMeta extends AbstractTableInfo {
    private String jdbcType;
}

2、编写数据库映射接口,自定义查询 SQL:

package com.kfyty.demo.config;

import com.kfyty.configuration.annotation.Component;
import com.kfyty.generate.database.MySQLDataBaseMapper;
import com.kfyty.jdbc.annotation.Param;
import com.kfyty.jdbc.annotation.Query;

import java.util.List;

/**
 * 添加自定义的 @Component 即可生效,同时需要把 GenerateConfig 中配置的 @DataBaseMapper 注解去掉
 */
@Component
public interface TableMetaConfig extends MySQLDataBaseMapper {
    @Override
    @Query("select data_type jdbcType, table_name, column_name field, data_type fieldType, if(column_key = 'PRI', 'true', 'false') primaryKey, if(is_nullable = 'YES', 'true', 'false') nullable, column_comment fieldComment from information_schema.COLUMNS where table_schema = #{dataBaseName} and table_name = #{tableName}")
    List<TableMeta> findTableInfo(@Param("dataBaseName") String dataBaseName, @Param("tableName") String tableName);
}

这样就可以在 java 模板中强转为 TableMeta 或者在 freemarker 中直接使用 jdbcType 啦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章