SpringBoot + Jfinal Enjoy + JdbcTemplate 整合

N 人看过

最近发现了 Jfinal 的 ORM 非常好用,于是想着将其抽出来,因为公司目前基于 JdbcTemplate 简单做了封装,将 SQL 语句写在 SQL 文件中,然后读取出来用 JdbcTemplate 执行,跟 Jfinal 是类似的,所以想将其抽出来扩展公司当前的框架

下载链接:https://pan.quark.cn/s/bac2b41ffc60

核心代码讲解

项目所用到的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>com.jfinal</groupId>
    <artifactId>enjoy</artifactId>
    <version>5.0.0</version>
</dependency>

自建2个注解,用于表示实体类字段名

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface DbEntity {
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface DbColumn {

    String value() default "";

    boolean exist() default true;
}

DbMapper:用于保存 java 属性与数据库字段的映射关系

public class DbMapper {

    private static final Map<Class<?>, Map<String, String>> classColMap =  new HashMap<>();

    private static final Map<Class<?>, Map<String, Method>> classSetterMap = new HashMap<>();

    private static final Map<Class<?>, Map<String, Class<?>>> classFieldTypeMap = new HashMap<>();

    public static Map<String, String> columnMap(Class<?> clazz) {
        return classColMap.get(clazz);
    }

    public static String toSetterName(String fieldName) {
        String firstLetter = fieldName.substring(0, 1);
        return "set" + fieldName.replaceFirst(firstLetter, firstLetter.toUpperCase());
    }

    public static Method setter(Class<?> entityClass, String field) {
        return classSetterMap.get(entityClass).get(field);
    }

    public static void classColMap(Class<?> clazz, Map<String, String> colMap) {
        classColMap.put(clazz, colMap);
    }

    public static void classSetterMap(Class<?> clazz, Map<String, Method> setterMap) {
        classSetterMap.put(clazz, setterMap);
    }

    public static void classFieldTypeMap(Class<?> clazz, Map<String, Class<?>> fieldTypeMap) {
        classFieldTypeMap.put(clazz, fieldTypeMap);
    }

    public static Class<?> getClassFieldType(Class<?> clazz, String fieldName) {
        return classFieldTypeMap.get(clazz).get(fieldName);
    }

读取实体类并将其属性与字段的映射保存

        /**
     * 扫描包下全部类
     *
     * @author vital
     */
    public void scanner() {
        String basePackage = "net.vitalblog.edb.entity";
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        try {
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(basePackage) + "/**/*.class";
            org.springframework.core.io.Resource[] resources = resourcePatternResolver.getResources(pattern);
            MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (org.springframework.core.io.Resource resource : resources) {
                // 用于读取类信息
                MetadataReader reader = readerFactory.getMetadataReader(resource);
                // 扫描到的class
                String classname = reader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(classname);
                // 判断是否有指定注解
                if (clazz.getAnnotation(DbEntity.class) != null) {
                    register(clazz);
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            log.error("error", e);
        }
    }

    /**
     * 将类信息保存
     *
     * @param clazz 实体类
     * @author vital
     */
    private <T> void register(Class<T> clazz) {
        List<Field> fieldList = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
        Class<? super T> superclass = clazz;
        // 判断是否有父类
        for (int i = 0; i < 5; i++) {
            superclass = superclass.getSuperclass();
            if (superclass == null) {
                break;
            }
            fieldList.addAll(Arrays.asList(superclass.getDeclaredFields()));
        }
        Map<String, Class<?>> fieldTypeMap = new HashMap<>(fieldList.size() + 1);
        Map<String, String> colMap = new HashMap<>(fieldList.size() + 1);
        Map<String, Method> setterMap = new HashMap<>(fieldList.size() + 1);

        for (Field field : fieldList) {
            String fieldName = field.getName();
            DbColumn column = field.getAnnotation(DbColumn.class);
            if (column != null && !column.exist()) {
                continue;
            }
            String colName = column != null ? column.value() : camelCase2UnderScoreCase(fieldName);
            fieldTypeMap.put(fieldName, field.getType());
            colMap.put(colName, fieldName);
            try {
                Method setter = clazz.getMethod(DbMapper.toSetterName(fieldName), field.getType());
                setterMap.put(fieldName, setter);
            } catch (Exception e) {
                log.error("error", e);
            }
        }

        DbMapper.classFieldTypeMap(clazz, fieldTypeMap);
        DbMapper.classColMap(clazz, colMap);
        DbMapper.classSetterMap(clazz, setterMap);
    }

    /**
     * 驼峰命名转下划线命名
     *
     * @param str 驼峰字段名
     * @return java.lang.String
     * @author vital
     */
    public static String camelCase2UnderScoreCase(String str) {
        Pattern compile = Pattern.compile("[A-Z]");
        Matcher matcher = compile.matcher(str);
        StringBuffer sb = new StringBuffer();
        while(matcher.find()) {
            matcher.appendReplacement(sb,  "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

RowMapper映射

public class CommonRowMapper<T> implements RowMapper<T> {

    private final Class<T> entityClass;

    public CommonRowMapper(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    @Override
    public T mapRow(ResultSet rs, int rowNum) throws SQLException {
        ResultSetMetaData metaData = rs.getMetaData();
        int colAmount = metaData.getColumnCount();
        Map<String, String> columnMap = DbMapper.columnMap(this.entityClass);
        T result = null;
        try {
            result = this.entityClass.newInstance();
            for (int index = 1; index <= colAmount; index++) {
                String column = JdbcUtils.lookupColumnName(metaData, index);
                String fieldName = columnMap.get(column);
                if (StringUtils.isBlank(fieldName)) {
                    continue;
                }
                Class<?> fieldType = DbMapper.getClassFieldType(this.entityClass, fieldName);
                Object value = JdbcUtils.getResultSetValue(rs, index, fieldType);
                if (value == null) {
                    continue;
                }
                Method setter = DbMapper.setter(this.entityClass, fieldName);
                setter.invoke(result, value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

}

使用

public <T> T findOne(String sql, Class<T> entityClass, Object... paras) {
        return jdbcTemplate.queryForObject(sql, new CommonRowMapper<>(entityClass), paras);
}

测试效果: