實現基於註解的 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 啦。

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