您的位置:首页 > 数据库

Jpa分表和复杂SQL转换成Pojo问题

2017-05-06 00:00 148 查看
最近公司项目需要一个对接的东西,大部分都是单表的CRUD,然后就想到用SpringBoot的JPA来做,这样可以省去很多的重复代码,但是需要解决几个问题:

单表的SQL,其实表的名称是变得,规则很像子表的规则,如何在Jpa中自动更换表名称

对于复杂的原生SQL,可以直接转换为Pojo,达到JdbcTemplate类似效果,并且要兼容1的要求

对于问题1:采用的是Hibernate的Interceptor来拦截执行的SQL,然后更换表名称,拦截部分代码如下:

public class HibernateSQLInterceptor extends EmptyInterceptor{

/**
*
*/
private static final long serialVersionUID = -8497637816391956683L;

private static ThreadLocal<Integer> THREADLOCAL = new ThreadLocal<>();

public static void set(Integer in){
THREADLOCAL .set(in);
}

public static void remove(){
THREADLOCAL .remove();
}

@Override
public String onPrepareStatement(String sql) {
Integer in= THREADLOCAL .get();
if (null == in) {
return sql;
}
return MySQLUtils.refactor(sql, in);
}
}

其中MySQLUtils.refactor是借助durid的SQL解析来替换表名称。

为了不让每个方法都显示的设置这个in,新增一个辅助类:SqlExecuteHelper,代码如下:

public class SqlExecuteHelper {

/**
* 设置in到线程局部变量,会在 HibernateSQLInterceptor 里面的onPrepareStatement改写SQL
* @param in
* @param callBack
* @return
*/
public static <T> T execute(int in,ICallBack<T> callBack){
T result ;
try {
HibernateSQLInterceptor.setIn(in);
result = callBack.callback();
} finally{
HibernateSQLInterceptor.remove();
}
return result;
}
}

ICallBack只是一个简单的回调接口。

然后配置SpringBoot的Hibernate的Interceptor:

spring.jpa.properties.hibernate.ejb.interceptor.session_scoped=HibernateSQLInterceptor

这样Jpa的SQL就会被拦截处理,并且更改表名称。

对于问题2:首先继承自AliasedTupleSubsetResultTransformer来实现Pojo的转换。

import java.beans.PropertyDescriptor;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.hibernate.transform.AliasedTupleSubsetResultTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.NotWritablePropertyException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.util.StringUtils;

class BeanResultTransformer extends AliasedTupleSubsetResultTransformer {

protected final Logger logger = LoggerFactory.getLogger(getClass());

/**
*
*/
private static final long serialVersionUID = 7907702453067570731L;

private static Map<Class<?>, BeanResultTransformer> TRANSFORMER_CACHE = new ConcurrentHashMap<>();

/** Map of the fields we provide mapping for */
private Map<String, PropertyDescriptor> mappedFields;

/** Set of bean properties we provide mapping for */
private Set<String> mappedProperties;

private Class<?> mappedClass;

private BeanResultTransformer(Class<?> mappedClass) {
initialize(mappedClass);
}

protected void initialize(Class<?> mappedClass) {
this.mappedClass = mappedClass;
this.mappedFields = new HashMap<String, PropertyDescriptor>();
this.mappedProperties = new HashSet<String>();
PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
for (PropertyDescriptor pd : pds) {
if (pd.getWriteMethod() != null) {
this.mappedFields.put(lowerCaseName(pd.getName()), pd);
String underscoredName = underscoreName(pd.getName());
if (!lowerCaseName(pd.getName()).equals(underscoredName)) {
this.mappedFields.put(underscoredName, pd);
}
this.mappedProperties.add(pd.getName());
}
}
}

@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Object mappedObject = BeanUtils.instantiateClass(this.mappedClass);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
for (int i = 0; i < tuple.length; i++) {
String alias = aliases[i];
String field = lowerCaseName(alias.replaceAll(" ", ""));
PropertyDescriptor pd = this.mappedFields.get(field);

if (pd != null) {
try {
try {
bw.setPropertyValue(pd.getName(), tuple[i]);
}
catch (TypeMismatchException ex) {
throw ex;
}
}
catch (NotWritablePropertyException ex) {
throw new DataRetrievalFailureException(
"Unable to map column '" + i + "' to property '" + pd.getName() + "'", ex);
}
}
else {
logger.error("No property found for column '" + i + "' mapped to field '" + field + "'");
// No PropertyDescriptor found
if (logger.isDebugEnabled()) {
logger.debug("No property found for column '" + i + "' mapped to field '" + field + "'");
}
}

}
return mappedObject;
}

@Override
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
return false;
}

protected String lowerCaseName(String name) {
return name.toLowerCase(Locale.US);
}

protected String underscoreName(String name) {
if (!StringUtils.hasLength(name)) {
return "";
}
StringBuilder result = new StringBuilder();
result.append(lowerCaseName(name.substring(0, 1)));
for (int i = 1; i < name.length(); i++) {
String s = name.substring(i, i + 1);
String slc = lowerCaseName(s);
if (!s.equals(slc)) {
result.append("_").append(slc);
} else {
result.append(s);
}
}
return result.toString();
}

public static BeanResultTransformer newInstance(Class<?> mappedClass) {
BeanResultTransformer instance = TRANSFORMER_CACHE.get(mappedClass);
if (null == instance) {
instance = new BeanResultTransformer(mappedClass);
TRANSFORMER_CACHE.put(mappedClass, instance);
}
return instance;
}
}

然后定义一个JpaTemplate:

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.hibernate.SQLQuery;
import org.hibernate.transform.AliasToEntityMapResultTransformer;

public class JpaTemplate {

private EntityManager entityManager;

public JpaTemplate(EntityManager entityManager) {
this.entityManager = entityManager;
}

public <T> List<T> query(String sql, Class<T> requiredType) {
return query(sql, new Object[0], requiredType);
}

@SuppressWarnings("unchecked")
public <T> List<T> query(String sql, Object[] args, Class<T> requiredType) {
Query query = entityManager.createNativeQuery(sql);
query.unwrap(SQLQuery.class).setResultTransformer(BeanResultTransformer.newInstance(requiredType));
buildQueryArgs(query, args);
return query.getResultList();
}

public <T> T queryForObject(String sql, Class<T> requiredType) {
return queryForObject(sql, new Object[0], requiredType);
}

@SuppressWarnings("unchecked")
public <T> T queryForObject(String sql, Object[] args, Class<T> requiredType) {
Query query = entityManager.createNativeQuery(sql);
query.unwrap(SQLQuery.class).setResultTransformer(BeanResultTransformer.newInstance(requiredType));
buildQueryArgs(query, args);
return (T) query.getSingleResult();
}

public Map<String, Object> queryForMap(String sql) {
return queryForMap(sql, new Object[0]);

}

@SuppressWarnings("unchecked")
public Map<String, Object> queryForMap(String sql, Object[] args) {
Query query = entityManager.createNativeQuery(sql);
query.unwrap(SQLQuery.class).setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
buildQueryArgs(query, args);
return (Map<String, Object>) query.getSingleResult();

}

public List<Map<String, Object>> queryForList(String sql) {
return queryForList(sql, new Object[0]);

}

@SuppressWarnings("unchecked")
public List<Map<String, Object>> queryForList(String sql, Object[] args) {
Query query = entityManager.createNativeQuery(sql);
query.unwrap(SQLQuery.class).setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
buildQueryArgs(query, args);
return query.getResultList();
}

private void buildQueryArgs(Query query, Object[] args) {
if (null != args) {
for (int i = 1; i < args.length; i++) {
query.setParameter(i, args[i]);
}
}
}
}

这样就可以实现复杂SQL到Pojo的映射。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Spring Boot Hibernate