手上的项目做了一年多,一直在使用JPA+Hibernate作为持久层的框架,前期使用了面向对象的方式,发现当业务很复杂时,代码可读性太差,也难维护,不如直接上sql来的快,但是有些细节问题没注意,导致后期重构时工作量很大,就是sql注入的问题,直接写sql没有使用占位符,这里就要仔细说一说了。JPA使用占位符是在sql中使用?或者:key,我选择的是用:key的方式,假设有是个查询条件,此时需要判空十次分别设置key值,然后再判断十次来setParameter()给占位符赋值,这个事情是在重构的时候做的,可想而知工作量,我比较喜欢偷懒,就写了个绑定参数的工具类,代码在最下方,给可能需要用的朋友,写的不是很好,希望有人可以给我提意见。但是还是会出现一些问题,比如参数类型的问题,或者一些特殊业务的问题,但这些问题都是JPA中sql参数绑定的问题,也可能是我没有找到解决办法,后来就想到了mybatis,也是直接写sql,很直接,很舒服,参数绑定也很自然,以前觉得写这些xml文件很麻烦,现在觉得比起JPA的sql绑定会出现一些奇奇怪怪的问题,我还是愿意选择mybatis。但是项目之前的JPA+Hibernate又不能丢,所以现在项目的情况是JPA和mybatis同时存在。上一下代码,记录一下。
以上纯属个人工作杂记。
1.依赖:这里只放了mybatis的依赖,新项目还需要jdbc-connect,连接池等依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
2.mybatis的配置:mybatis-config.xml直接放在resource下面
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- mybatis全局设置 -->
<settings>
<!--使用数据库自增id-->
<setting name="useGeneratedKeys" value="true" />
<setting name="useColumnLabel" value="true" />
<!-- 开启驼峰命名规范-->
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
</configuration>
3.spring集成mybatis:这里注意一下,dataSource可以与Hibernate共用
<bean id="dataSource" class="com.gtis.support.JndiSupportBasicDataSource" destroy-method="close">
<property name="driverClassName" value="${landtax.db.driver}"/>
<property name="jndiName" value="${landtax.db.jndi}"/>
<property name="url" value="${landtax.db.url}"/>
<property name="username" value="${landtax.db.username}"/>
<property name="password" value="${landtax.db.password}"/>
</bean>
<!-- mybatis文件配置,扫描所有mapper文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="typeAliasesPackage" value="cn.gtmap.landtax.entity"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- spring与mybatis整合配置,扫描所有dao,指定的映射器类是接口,接口方法可以用注解来指定 SQL 语句,但是 MyBatis 的映射器 XML 文件也可以用。 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--扫描dao接口-->
<property name="basePackage" value="cn.gtmap.landtax.dao"/>
<!--注入sqlSessionFactory-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
4.mybatis代码骨架:
dao(放dao接口),mapper(注意xml中namespace要与dao一致),entity,service
JPA参数绑定(http接口中查询参数 Object 为实体类对象或者Map都可以处理,Query为JPA的对象)
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import javax.persistence.Parameter;
import javax.persistence.Query;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @Auther: <a href="mailto:jiwang@gtmap.cn">jiwang</a>
* @Version: 1.0, 2018/11/27
* @Description:sql参数绑定处理工具
*/
@Component
public class SqlBinderUtil {
private static final Logger LOGGER = Logger.getLogger(SqlBinderUtil.class);
/**
*@Author: jiwang
*@Date: 8:47 2018/11/30
*@Param: [o, query]
*@return: void
*@Description: sql绑定参数 有两种查询参数形式,一种是实体类queryBean,一种Map类型的参数
**/
public void sqlBindParameter(Object o, Query query){
if (o instanceof Map){
this.sqlBindParameterByMapParam((Map) o,query);
}else {
this.sqlBindParameterByQueryBean(o,query);
}
}
/**
*@Author: jiwang
*@Date: 9:38 2018/12/4
*@Param: [param, query]
*@return: void
*@Description:处理Map类型参数的sql绑定
**/
public void sqlBindParameterByMapParam(Map param,Query query){
Set<Parameter<?>> parameters = query.getParameters();
Iterator<Parameter<?>> setIterator = parameters.iterator();
while (setIterator.hasNext()){
Parameter<?> parameter = setIterator.next();
String key = parameter.getName();
if (param.containsKey(key)){
query.setParameter(key,param.get(key));
}
}
}
/**
*@Author: jiwang
*@Date: 9:38 2018/12/4
*@Param: [o, query]
*@return: void
*@Description:处理实体类类型参数的sql绑定
**/
public void sqlBindParameterByQueryBean(Object o, Query query){
Class clazz = o.getClass();
Set<Parameter<?>> parameterSet = query.getParameters();
Iterator<Parameter<?>> iterator = parameterSet.iterator();
while (iterator.hasNext()){
String key = iterator.next().getName();
try {
Field field = clazz.getDeclaredField(key);
PropertyDescriptor pd = new PropertyDescriptor(field.getName(),clazz);
Method method = pd.getReadMethod();
String value = (String) method.invoke(o);
query.setParameter(key,value);
} catch (IntrospectionException e) {
LOGGER.info(e);
} catch (IllegalAccessException e) {
LOGGER.info(e);
} catch (InvocationTargetException e) {
LOGGER.info(e);
} catch (NoSuchFieldException e) {
LOGGER.info(e);
}
}
}
}