配置文件
当Spring和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="mapperLocations" value="classpath*:mybatis-mapper/**/*.xml"/>
</bean>
Mapper
mapper的生成除了网上普遍的generator的jar包之外,自己还可以实现一个简单的模板生成:
我用的是Freemarker进行模板渲染,自动生成常用的sql:
package org.digdata.swustoj.client;
import freemarker.template.TemplateException;
import org.apache.commons.io.FileUtils;
import org.digdata.swustoj.annotation.Mapping;
import org.digdata.swustoj.util.BeanUtil;
import org.digdata.swustoj.util.FreeMarkerUtil;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* Created by hongfei.whf on 2016/11/26.
*/
public class MapperGenerator {
private String output_dir = "E:\\tmp\\output";
private String class_path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
private final static SAXReader reader = new SAXReader();
private List<Class<?>> classes = BeanUtil.scan("org.digdata.swustoj.entity");
private String path1 = "auto";
private String path2 = "impl";
@Test
public void run() throws ClassNotFoundException, IOException, IllegalAccessException, InstantiationException, TemplateException, DocumentException {
for (Class clazz : classes) {
if (clazz.getSimpleName().endsWith("WithBLOBs")) continue;
Mapping mapping = (Mapping) clazz.getAnnotation(Mapping.class);
String className = clazz.getCanonicalName();
String namespace = className.replace("entity", "dao") + "Mapper";
String tableName = mapping.tableName();
String resultMap = getResultMap(clazz);
String queryType = mapping.queryType().getCanonicalName();
Map<String, Object> map = new HashMap<>();
map.put("namespace", namespace);
map.put("queryType", queryType);
map.put("tableName", tableName);
map.put("resultMap", resultMap);
ArrayList fields = new ArrayList();
Class extendsClass = clazz;
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
try {
extendsClass = Class.forName(clazz.getCanonicalName() + "WithBLOBs");
fields.addAll(Arrays.asList(extendsClass.getDeclaredFields()));
} catch (Exception e) {
//
}
map.put("ownType", extendsClass.getCanonicalName());
map.put("fields", fields);
String template_path = class_path + File.separator + "mybatis-mapper/template/TemplateMapper.ftl";
String template = FileUtils.readFileToString(new File(template_path));
String custom_path = class_path + File.separator + "mybatis-mapper/template/CustomMapper.ftl";
String custom = FileUtils.readFileToString(new File(custom_path));
String TemplateContent = FreeMarkerUtil.proccessTemplate(clazz.getCanonicalName(),
template, map);
String auto_render_template_file_path = getRenderPath(clazz, true);
FileUtils.write(new File(auto_render_template_file_path), TemplateContent);
String CustomContent = FreeMarkerUtil.proccessTemplate(clazz.getCanonicalName(),
custom, map);
String auto_render_custom_file_path = getRenderPath(clazz, false);
FileUtils.write(new File(auto_render_custom_file_path), CustomContent);
}
}
private String getResultMap(Class clazz) throws DocumentException {
String mapperFilePath = class_path + File.separator + "mybatis-mapper" + File.separator + clazz.getSimpleName() + "Mapper.xml";
Document document = reader.read(new File(mapperFilePath));
Element root = document.getRootElement();
if ("mapper".equals(root.getName())) {
Iterator iter = root.elementIterator();
while (iter.hasNext()) {
Element currElement = (Element) iter.next();
if ("resultMap".equals(currElement.getName())) {
if ("ResultMapWithBLOBs".equals(currElement.attributeValue("id"))) {
return "ResultMapWithBLOBs";
}
}
}
}
return "BaseResultMap";
}
private String getRenderPath(Class clazz, Boolean isTemplate) {
return output_dir +
File.separator +
(isTemplate ? path1 : path2) +
File.separator +
clazz.getSimpleName() +
"Mapper.xml";
}
}
模板1:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="${namespace}">
<!-- query ${tableName} -->
<select id="query" resultMap="${resultMap}"
parameterType="${queryType}">
select
*
from ${tableName}
<include refid="condition"/>
<include refid="orderby"/>
<if test="start != null and rows != null">
<#noparse>limit #{start},#{rows}</#noparse>
</if>
</select>
<!-- count ${tableName} -->
<select id="count" resultType="java.lang.Integer"
parameterType="${queryType}">
select
count(*)
from ${tableName}
<include refid="condition"/>
</select>
<!-- query ${tableName} by ids -->
<select id="queryByIds" parameterType="java.util.List" resultMap="${resultMap}">
select
*
from ${tableName}
<where>
${tableName}.id in
<foreach collection="list" item="item" open="("
separator="," close=")">
<#noparse>#{item}</#noparse>
</foreach>
</where>
</select>
<!-- delete ${tableName} by ids -->
<delete id="delete" parameterType="${queryType}">
delete from ${tableName}
<include refid="condition"/>
</delete>
<!-- save batch ${tableName} -->
<insert id="saveBatch" parameterType="java.util.List">
insert into ${tableName}
<trim prefix="(" suffix=")" suffixOverrides=",">
<#list fields as field>
${field.name},
</#list>
</trim>
value
<foreach item="item" index="index" collection="list" separator=",">
<trim prefix="(" suffix=")" suffixOverrides=",">
<#list fields as field>
<#noparse>#{item.</#noparse>${field.name}<#noparse>},</#noparse>
</#list>
</trim>
</foreach>
</insert>
<!-- save ${tableName} And Return Id -->
<insert id="saveAndRetId" parameterType="${ownType}"
useGeneratedKeys="true" keyProperty="id">
insert into ${tableName}
<trim prefix="(" suffix=")" suffixOverrides=",">
<#list fields as field>
<if test="${field.name} != null">
${field.name},
</if>
</#list>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<#list fields as field>
<if test="${field.name} != null">
<#noparse>#{</#noparse>${field.name}<#noparse>},</#noparse>
</if>
</#list>
</trim>
</insert>
</mapper>
模板2:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="${namespace}">
<!-- 若要进行修改,请在此补全sql,防止以后模板扩展带来的代码迁移的不便 -->
<!-- 注意,此两处sql均被使用到,请勿注释或删除 -->
<!-- where sql -->
<sql id="condition">
<where>
1=1
</where>
</sql>
<!-- orderby sql -->
<sql id="orderby">
</sql>
</mapper>
Dao层统一封装
我利用的是Spring4.0提供的泛型注入:
package org.digdata.swustoj.dao;
import org.digdata.swustoj.annotation.CacheAccess;
import org.digdata.swustoj.annotation.CacheFlush;
import org.digdata.swustoj.query.PageQuery;
import java.util.List;
/**
* Created by hongfei.whf on 2016/8/27.
*/
public interface BaseDao<T, Q extends PageQuery> {
/**
* 删除指定id
*
* @param id
* @return
*/
@CacheFlush
int deleteByPrimaryKey(Integer id);
/**
* 插入
*
* @param record
* @return
*/
@CacheFlush
int insert(T record);
/**
* 条件插入
*
* @param record
* @return
*/
@CacheFlush
int insertSelective(T record);
/**
* 查询指定id
*
* @param id
* @return
*/
@CacheAccess(type = Object.class)
T selectByPrimaryKey(Integer id);
/**
* 根据id条件更新
*
* @param record
* @return
*/
@CacheFlush
int updateByPrimaryKeySelective(T record);
/**
* 根据id更新
*
* @param record
* @return
*/
@CacheFlush
int updateByPrimaryKey(T record);
/**
* 条件查询
*
* @param query
* @return
*/
@CacheAccess(type = List.class)
List<T> query(Q query);
/**
* 根据ids查询
*
* @param ids
* @return
*/
@CacheAccess(type = List.class)
List<T> queryByIds(List<Integer> ids);
/**
* 条件查询数量
*
* @param query
* @return
*/
@CacheAccess(type = Integer.class)
Integer count(Q query);
/**
* 删除
*
* @param query
* @return
*/
@CacheFlush
Boolean delete(Q query);
/**
* 添加
*
* @param record
* @return
*/
@CacheFlush
Boolean save(T record);
/**
* 更新
*
* @param record
* @return
*/
@CacheFlush
Boolean update(T record);
/**
* 批量插入
*
* @param list
* @return
*/
@CacheFlush
Boolean saveBatch(List<T> list);
/**
* 插入并返回主键
*
* @param t
* @return
*/
@CacheFlush
Integer saveAndRetId(T t) throws NoSuchFieldException, IllegalAccessException;
}
注意:这里的注解和Mybatis无关,主要实现与Memcache的统一操作
Dao统一封装实现类
package org.digdata.swustoj.dao.impl;
import org.digdata.swustoj.dao.BaseDao;
import org.digdata.swustoj.query.PageQuery;
import org.digdata.swustoj.util.BeanUtil;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
/**
* Created by hongfei.whf on 2016/8/27.
*/
public abstract class BaseDaoImpl<T, Q extends PageQuery> extends SqlSessionDaoSupport implements BaseDao<T, Q> {
/**
* 获取mapper的namespace
*
* @return
*/
public abstract String getNameSpace();
/**
* 获取子类型,推荐填写T的class,,用于缓存序列化和反序列化的实现
*
* @return
*/
public abstract Class getSubType();
@Override
public int deleteByPrimaryKey(Integer id) {
return this.getSqlSession().delete(getNameSpace() + ".deleteByPrimaryKey", id);
}
@Override
public int insert(T record) {
return this.getSqlSession().insert(getNameSpace() + ".insert", record);
}
@Override
public int insertSelective(T record) {
return this.getSqlSession().insert(getNameSpace() + ".insertSelective", record);
}
@Override
public T selectByPrimaryKey(Integer id) {
return this.getSqlSession().selectOne(getNameSpace() + ".selectByPrimaryKey", id);
}
@Override
public int updateByPrimaryKeySelective(T record) {
return this.getSqlSession().update(getNameSpace() + ".updateByPrimaryKeySelective", record);
}
@Override
public int updateByPrimaryKey(T record) {
return this.getSqlSession().update(getNameSpace() + ".updateByPrimaryKey", record);
}
@Override
public List<T> query(Q query) {
return this.getSqlSession().selectList(getNameSpace() + ".query", query);
}
@Override
public List<T> queryByIds(List<Integer> list) {
return this.getSqlSession().selectList(getNameSpace() + ".queryByIds", list);
}
@Override
public Integer count(Q query) {
return this.getSqlSession().selectOne(getNameSpace() + ".count", query);
}
@Override
public Boolean delete(Q query) {
return this.getSqlSession().delete(getNameSpace() + ".delete", query) > 0 ? true : false;
}
@Override
public Boolean save(T record) {
return this.insertSelective(record) > 0 ? true : false;
}
@Override
public Integer saveAndRetId(T t) throws NoSuchFieldException, IllegalAccessException {
if (this.getSqlSession().insert(getNameSpace() + ".saveAndRetId", t) > 0) {
return BeanUtil.getFieldValue(t, "id", Integer.class);
}
return null;
}
@Override
public Boolean update(T record) {
return this.updateByPrimaryKeySelective(record) > 0 ? true : false;
}
@Override
public Boolean saveBatch(List<T> list) {
return this.getSqlSession().insert(getNameSpace() + ".saveBatch", list) > 0 ? true : false;
}
}
使用方式:
@Repository
public class ProblemDaoImpl extends BaseDaoImpl<ProblemWithBLOBs, ProblemQuery> implements ProblemDao {
// ....
}
Service层的统一封装
Service层的封装参考Dao层的封装即可,我主要封装的是分页,批量操作等。