关于Spring Data JPA转换自定义实体的一些方案
程序员文章站
2022-04-23 15:57:28
...
使用JPA的时候,对于特别复杂的关联查询总是显得很麻烦,对于这个问题,网上有很多解决方案,但是有的尝试之后又发现并不成功,所以决定自己写一个转换类工具类,留作记录
import org.hibernate.transform.ResultTransformer;
import java.util.List;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.apache.commons.lang.StringUtils;
import org.hibernate.HibernateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.util.NumberUtils;
import java.sql.Date;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @desc 转化自定义实体类的工具类,一般情况没问题,给大家使用
* <p>
* @author Leao.lv
*/
public class IgnoreTypeeResultTransformer implements ResultTransformer {
private static Logger logger = LoggerFactory.getLogger(IgnoreTypeeResultTransformer.class);
private static final long serialVersionUID = -3779317531110592988L;
public static ConcurrentMap<String, BeanMap> beanMapCache = new ConcurrentHashMap<String, BeanMap>();
private final Class<?> resultClass;
public IgnoreTypeeResultTransformer(final Class<?> resultClass) {
this.resultClass = resultClass;
}
private BeanMap getBeanMap(Object object) {
return BeanMap.create(object);
}
private Object transforObject(Object src, Class dest) throws ClassCastException, IllegalArgumentException {
Object clo = null;
String arrayStr = null;
if (dest.equals(String.class)) {
clo = arrayStr;
} else if (dest.getSuperclass().equals(Number.class) || dest.equals(Number.class)) {
// 只处理 字符转入 或 数值类型转入
clo = NumberUtils.parseNumber(src.toString(), dest);
} else if (dest.getSuperclass().equals(Instant.class) || dest.equals(Instant.class)) {
Timestamp tp = (Timestamp) src;
clo = tp.toInstant();
}else if (dest.getSuperclass().equals(LocalDate.class) || dest.equals(LocalDate.class)) {
Date tp = (Date) src;
clo = tp.toLocalDate();
} else {
//error 设置 null
logger.error("未转化值src:{}, dest:{}", src, dest);
}
return clo;
}
/**
* aliases为每条记录的数据库字段名,
* tupe为与aliases对应的字段的值
*/
@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Object result;
try {
result = resultClass.newInstance();
BeanMap beanMap = getBeanMap(result);
for (int i = 0, len = tuple.length; i < len; i++) {
String key = BeanNaming.underLineToCamel(aliases[i]);
Class target = beanMap.getPropertyType(key);
if (target == null) {
continue;
}
Object src = tuple[i];
if (src != null && !src.getClass().equals(target)) {
//存在需要转化的的值。。
src = transforObject(src, target);
}
beanMap.put(key, src);
}
} catch (Exception e) {
throw new HibernateException("Could not instantiate resultclass: " + this.resultClass.getName(), e);
}
return result;
}
@Override
public List transformList(List collection) {
return collection;
}
private static class BeanNaming extends PropertyNamingStrategy.SnakeCaseStrategy {
private static final char UNDERLINE = '_';
public static Map<String, String> CACHING = new ConcurrentHashMap<>();
protected static String underLineToCamel(String param) {
if (StringUtils.isBlank(param)) {
return "";
}
if (param.indexOf(UNDERLINE) == -1) {
//不需要转化
return param;
}
if (CACHING.containsKey(param)) {
return CACHING.get(param);
}
int len = param.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c = param.charAt(i);
if (c == UNDERLINE) {
if (++i < len) {
sb.append(Character.toUpperCase(param.charAt(i)));
}
} else {
sb.append(c);
}
}
CACHING.put(param, sb.toString());
return sb.toString();
}
}
}
用法是
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.hibernate.query.internal.NativeQueryImpl;
Query query = entityManager.createNativeQuery(sql.toString());
query.unwrap(NativeQueryImpl.class).setResultTransformer(new IgnoreTypeeResultTransformer(对应实体.class)).list();