最近发现了 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);
}
测试效果:
