SpringBoot+MyBatis中自动根据@Table注解和@Column注解生成ResultMap

其实我一点都不想用mybatis,好多地方得自己写,比如这里。

使用mybatis要写大量的xml,烦的一批。最烦人的莫过于写各种resultmap,就是数据库字段和实体属性做映射。我认为这里应该是mybatis自动支持的,但是,它没有。为了轻量化(caocaocoa)???。

很少用mybatis,不知道有没有相关插件。只有自己写方法实现了。

先整理一下大体思路:

1.扫描项目代码下的所有类

2.选出所有类中的含有Table注解的类

3.根据column注解找出类下的属性映射关系

4.创建mybatis配置类,在mybatis配置完成后根据2.3两步的信息创建新的resultmap添加到mybatis的SqlSessionFactory中

代码:

这个是mybatis配置类,

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Table;

import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;

import com.esri.rest.util.ClassUtil;
import com.esri.rest.util.SpringUtil;

@Configuration
@AutoConfigureAfter(MybatisAutoConfiguration.class)
public class MyBatisTypeMapScannerConfig {

    private Logger log = Logger.getLogger(MyBatisTypeMapScannerConfig.class);

    public MyBatisTypeMapScannerConfig(ApplicationContext applicationContext, SqlSessionFactory sqlSessionFactory) {
        log.debug("自动添加resultMap");
        org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();

        //ResultMap rm = new ResultMap.Builder(configuration, id, type, null).build();
        //configuration.addResultMap(rm);
        // 选出所有类中的含有Table注解的类
        List<Class<?>> list = ClassUtil.getClassesWithAnnotation(Table.class);

        for (Class<?> clas : list) {
            System.out.println(clas); //创建新的resultmap添加到mybatis的SqlSessionFactory中
            ResultMap rm = new ResultMap.Builder(configuration, clas.getName(), clas, getResultMapping(configuration, clas)).build();
            configuration.addResultMap(rm);
        }

        log.debug("自动添加resultMap");
    }

    private List<ResultMapping> getResultMapping(org.apache.ibatis.session.Configuration configuration, Class<?> type) {
        List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();      //   根据column注解找出类下的属性映射关系
        Map<String, Map<String, Object>> cols =  ClassUtil.getColumnRelation(type);
        System.out.println(cols);
        Set<String> keys = cols.keySet();
        String[] keyArr = new String[keys.size()];
        keys.toArray(keyArr);

        for(String key:keyArr){
            String property;
            String column;
            Object javaType;
            Map<String, Object> map = cols.get(key);
            property = key;
            column = (String) map.get("dbname");
            javaType = map.get("type");
            ResultMapping mapping = new ResultMapping.Builder(configuration, property, column,(Class<?> )javaType).build();
            resultMappings.add(mapping);
        }

        return resultMappings;
    }

}
ClassUtil 类
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

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

/**
 * 扫描esri包下的所有类
 * <p>
 * Title: ClassUtil.java
 * </p>
 * <p>
 * Description:
 * </p>
 *
 * @author lichao1
 * @date 2018年12月3日
 * @version 1.0
 */
public class ClassUtil {

    private static Set<Class<?>> classList;
    static {
        classList = getClasses("com.esri.rest");
    }

    /**
     * 从包package中获取所有的Class
     *
     * @param pack
     * @return
     */
    public static Set<Class<?>> getClasses(String pack) {

        // 第一个class类的集合
        Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
        // 是否循环迭代
        boolean recursive = true;
        // 获取包的名字 并进行替换
        String packageName = pack;
        String packageDirName = packageName.replace(‘.‘, ‘/‘);
        // 定义一个枚举的集合 并进行循环来处理这个目录下的things
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            // 循环迭代下去
            while (dirs.hasMoreElements()) {
                // 获取下一个元素
                URL url = dirs.nextElement();
                // 得到协议的名称
                String protocol = url.getProtocol();
                // 如果是以文件的形式保存在服务器上
                if ("file".equals(protocol)) {
                    // System.err.println("file类型的扫描");
                    // 获取包的物理路径
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    // 以文件的方式扫描整个包下的文件 并添加到集合中
                    findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
                } else if ("jar".equals(protocol)) {
                    // 如果是jar包文件
                    // 定义一个JarFile
                    // System.err.println("jar类型的扫描");
                    JarFile jar;
                    try {
                        // 获取jar
                        jar = ((JarURLConnection) url.openConnection()).getJarFile();
                        // 从此jar包 得到一个枚举类
                        Enumeration<JarEntry> entries = jar.entries();
                        // 同样的进行循环迭代
                        while (entries.hasMoreElements()) {
                            // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
                            JarEntry entry = entries.nextElement();
                            String name = entry.getName();
                            // 如果是以/开头的
                            if (name.charAt(0) == ‘/‘) {
                                // 获取后面的字符串
                                name = name.substring(1);
                            }
                            // 如果前半部分和定义的包名相同
                            if (name.startsWith(packageDirName)) {
                                int idx = name.lastIndexOf(‘/‘);
                                // 如果以"/"结尾 是一个包
                                if (idx != -1) {
                                    // 获取包名 把"/"替换成"."
                                    packageName = name.substring(0, idx).replace(‘/‘, ‘.‘);
                                }
                                // 如果可以迭代下去 并且是一个包
                                if ((idx != -1) || recursive) {
                                    // 如果是一个.class文件 而且不是目录
                                    if (name.endsWith(".class") && !entry.isDirectory()) {
                                        // 去掉后面的".class" 获取真正的类名
                                        String className = name.substring(packageName.length() + 1, name.length() - 6);
                                        try {
                                            // 添加到classes
                                            classes.add(Class.forName(packageName + ‘.‘ + className));
                                        } catch (ClassNotFoundException e) {
                                            // log
                                            // .error("添加用户自定义视图类错误
                                            // 找不到此类的.class文件");
                                            e.printStackTrace();
                                        }
                                    }
                                }
                            }
                        }
                    } catch (IOException e) {
                        // log.error("在扫描用户定义视图时从jar包获取文件出错");
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return classes;
    }

    /**
     * 以文件的形式来获取包下的所有Class
     *
     * @param packageName
     * @param packagePath
     * @param recursive
     * @param classes
     */
    private static void findAndAddClassesInPackageByFile(String packageName, String packagePath,
            final boolean recursive, Set<Class<?>> classes) {
        // 获取此包的目录 建立一个File
        File dir = new File(packagePath);
        // 如果不存在或者 也不是目录就直接返回
        if (!dir.exists() || !dir.isDirectory()) {
            // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
            return;
        }
        // 如果存在 就获取包下的所有文件 包括目录
        File[] dirfiles = dir.listFiles(new FileFilter() {
            // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
            public boolean accept(File file) {
                return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });
        // 循环所有文件
        for (File file : dirfiles) {
            // 如果是目录 则继续扫描
            if (file.isDirectory()) {
                findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
                        classes);
            } else {
                // 如果是java类文件 去掉后面的.class 只留下类名
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    // 添加到集合中去
                    // classes.add(Class.forName(packageName + ‘.‘ +
                    // className));
                    // 经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
                    classes.add(
                            Thread.currentThread().getContextClassLoader().loadClass(packageName + ‘.‘ + className));
                } catch (ClassNotFoundException e) {
                    // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 根据注解查找类
     *
     * @param annotationType
     * @return
     */
    public static List<Class<?>> getClassesWithAnnotation(Class<? extends Annotation> annotationType) {
        List<Class<?>> list = new ArrayList<Class<?>>();
        Object[] ts = classList.toArray();
        for (Object t : ts) {
            Class<?> ty = (Class<?>) t;
            Object aclass = ty.getAnnotation(annotationType);
            if (aclass != null) {
                list.add(ty);
            }
        }
        return list;
    }

    /**
     * 获取类中标记Column注解的字段
     * @param clas
     * @return
     */
    public static Map<String, Map<String, Object>> getColumnRelation(Class<?> clas){
        Map<String, Map<String, Object>> cols = new HashMap<String, Map<String, Object>>();
        // 直接标注属性
        Field field;
        Field[] fields = clas.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            fields[i].setAccessible(true);
        }
        for (int i = 0; i < fields.length; i++) {
            Map<String, Object> map= new HashMap<String, Object>();
            try {
                field = clas.getDeclaredField(fields[i].getName());
                Column column = field.getAnnotation(Column.class);
                if (column != null) {
                    map.put("dbname", column.name());
                    map.put("length", column.length());
                    map.put("type", field.getType());
                    //System.out.println(column.name());
                    //System.out.println(column.length());
                    cols.put(field.getName(), map);
                }
            } catch (Exception e) {
            }
        }

        // 标注方法
        Method method;
        Method[] methods = clas.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            Map<String, Object> map= new HashMap<String, Object>();
            try {
                method = methods[i];
                Column column = method.getAnnotation(Column.class);
                if (column != null) {
                    String filedName = method.getName();
                    boolean isGet = filedName.indexOf("get")>=0;
                    if(isGet){
                        map.put("type", method.getReturnType());
                    }else{
                        map.put("type", method.getParameterTypes()[0]);
                    }
                    filedName = filedName.replaceAll("set", "");
                    filedName = filedName.replaceAll("get", "");
                    filedName = filedName.substring(0, 1).toLowerCase() + filedName.substring(1);
                    map.put("dbname", column.name());
                    map.put("length", column.length());
                    cols.put(filedName, map);
                }
            } catch (SecurityException e) {
                e.printStackTrace();
            }
        } 

        return cols;
    }

    public static void main(String[] args) {
        System.out.println(classList);
        // Object[] ts = classList.toArray();
        // for(Object t:ts){
        // Class<?> tt = (Class<?>) t;
        // System.out.println(tt);
        // }

        List<Class<?>> list = getClassesWithAnnotation(Table.class);
        for (Class<?> clas : list) {
            System.out.println(clas);
            Map<String, Map<String, Object>> cols =  getColumnRelation(clas);
            System.out.println(cols);
        }
    }
}

经测试可行,细节方面自己调整

原文地址:https://www.cnblogs.com/Leechg/p/10059407.html

时间: 2024-12-15 05:07:28

SpringBoot+MyBatis中自动根据@Table注解和@Column注解生成ResultMap的相关文章

JPA中自动使用@Table(name = &quot;userTab&quot;)后自动将表名、列名添加了下划线的问题

一.问题 JPA中自动使用@Table(name = "userTab")后自动将表名.列名添加了下划线的问题,如下图: 二.解决 在application.properties文件中加入: spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl 如: #连接字符串 spring.datasource.driver-c

关于springboot项目中自动注入,但是用的时候值为空的BUG

最近想做一些web项目来填充下业余时间,首先想到了使用springboot框架,毕竟方便 快捷 首先:去这里 http://start.spring.io/ 直接构建了一个springboot初始化的项目框架 然后:在页面上选择相应的依赖包,然后点击构建按钮,然后下载并且导入IDE中,目前喜欢使用IDEA 于是一个简答的springboot项目就搭建好了 废话说完,然后想体验下spring中redis的使用: 那就直接新建了一个类,然后 @Autowired 自动注入 RedisTemplate

Springboot mybatis generate 自动生成实体类和Mapper

maven项目 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.o

springboot+mybatis项目自动生成

springboot_data_access_demo基于rapid,根据自定义模版生成的基于mybatis+mysql的数据库访问示例项目.简单配置数据库信息,配置不同的生成策略生成可以直接运行访问数据库的项目,吸取了mybatis generator的动态条件优势,同时又稍有扩展.可以生成简单易懂的sql,支持大部分单表操作,一般情况下不需要自己动手写sql.模板可以根据自己需求做相应的修改(如有需要可以提供springboot_data_access_demo项目的生成模板).支持的方法有

LocalDateTime整合到SpringBoot + MyBatis中

啥也不说先贴两个代码: 一:MVC层配置 @Configuration public class JacksonConfig { /** 默认日期时间格式 */ public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; /** 默认日期格式 */ public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";

SpringBoot中mybatis的自动生成

1.在pom文件中加入自动生成的插件 <!-- mybatis generator 自动生成代码插件 --> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration>

SpringBoot+Mybatis 自动创建数据表(适用mysql)

Mybatis用了快两年了,在我手上的发展史大概是这样的 第一个阶段 利用Mybatis-Generator自动生成实体类.DAO接口和Mapping映射文件.那时候觉得这个特别好用,大概的过程是这样的 在数据库中先建好表配置好几个xml文件(一般都是复制粘贴上一个项目的),然后根据数据库中的表,生成实体类.DAO接口和Mapping映射文件当需要添加数据操作的时候,先在xml中写好CRUD语句,然后在DAO接口层写接口,最后到映射文件渐渐地,我忽然发现,这种方式越来越烦.改一个字段,要修改很多

SpringBoot 添加mybatis generator 自动生成代码插件

自动生成数据层代码,提高开发效率 1.pom添加插件,并指定配置文件路径 <!-- mybatis generator 自动生成代码插件 --> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.5</version> <

Spring 中使用 Mybatis generator 自动生成代码

Mybatis generator 是一个 Mybatis 插件,可以用于自动生成项目中需要的 pojo 类.对应的 Mapper 接口和 mapper.xml 文件.使用该插件,一方面可以节省开发时间,另一方面也避免手写时打错字的问题. 由于工作需要,我打算在原有的 Spring 项目中使用 Mybatis generator 自动生成代码,以便简化 DAO 层的开发. 闲言少叙,just do it! 1. 引入依赖 a. 父模块配置 <properties> <spring_ver