递归调用的应用场景常常出现在多级嵌套的情况,比如树形的菜单。下面通过一个简单的例子来实现mysql+mybatis的递归。
数据模型
private Integer categoryId;
private String categoryName;
private Integer isRoot;
private Integer categoryLevel;
private Integer rootCategoryId;
private Integer parentCategoryId;
private String parentCategoryName;
以上是一个简单的类目的数据实体,主要要注意通过乐parentCategoryId实现了父子的关联。
数据库数据
我们可以很简单的通过父级的id获取其直接子级列表。但是如果我们想要通过某一父级id获取其直接下属和间接下属的(子,孙,曾孙等)列表呢?这就需要用到递归来实现。
实现方法
首先,我们在实体类下面加上这么一个属性。
public List<TCategory>childList=new ArrayList<TCategory>();//子Category列表
然后,我们编写xml文件中的ResultMap,如下。
<!-- 带有chlidList的map -->
<resultMap id="TreeMap" type="com.qgranite.entity.TCategory">
<id column="category_id" property="categoryId" jdbcType="INTEGER" />
<result column="category_name" property="categoryName"
jdbcType="VARCHAR" />
<result column="category_remark" property="categoryRemark"
jdbcType="VARCHAR" />
<result column="category_type" property="categoryType"
jdbcType="INTEGER" />
<result column="is_root" property="isRoot" jdbcType="INTEGER" />
<result column="category_level" property="categoryLevel"
jdbcType="INTEGER" />
<result column="root_category_id" property="rootCategoryId"
jdbcType="INTEGER" />
<result column="parent_category_id" property="parentCategoryId"
jdbcType="INTEGER" />
<result column="parent_category_name" property="parentCategoryName"
jdbcType="VARCHAR" />
<collection property="childList" column="category_id"
ofType="com.qgranite.entity.TCategory" select="selectRecursionByParentCategoryId"></collection>
</resultMap>
最后一句是关键,它说明了递归所需要调用的方法selectRecursionByParentCategoryId。
然后我们来写这个递归方法。
<!-- 根据父键递归查询 -->
<select id="selectRecursionByParentCategoryId" resultMap="TreeMap"
parameterType="java.lang.Integer">
select
*
from t_category
where is_del=0
and
parent_category_id=#{_parameter,jdbcType=INTEGER}
</select>
注意这边的resultMap就是上述定义的resultMap.
如果要递归获取所有的TCategory,我们只要获取所有category_type=1(即根类目),然后从根目录递归下去,注意这边的resultMap必须为TreeMap,才会触发递归。
<!-- 递归查询所有 --> <select id="selectRecursionAll" resultMap="TreeMap"> select * from t_category where is_del=0 and category_type=1 </select>
接下来写后台调用方法。
/**
* 根据特定父类递归查询所有子类
*
* @param categoryId
* @return
*/
public List<TCategory> allCategoryRecursion() {
return baseDao
.findTList(
"TCategoryMapper.selectRecursionAll");
}
/**
* 根据特定父类递归查询所有子类
*
* @param categoryId
* @return
*/
public List<TCategory> subCategoryListByParentId(int categoryId) {
return baseDao
.findTListByParam(
"TCategoryMapper.selectRecursionByParentCategoryId",
categoryId);
}
/**
* 根据categoryId获取子孙categoryId的id字符串,用逗号隔开
*
* @param categoryId
* @return
*/
public String subCategoryStrByParentId(Integer categoryId) {
String categoryStr = categoryId.toString();
List<TCategory> categoryList = baseDao
.findTListByParam(
"TCategoryMapper.selectRecursionByParentCategoryId",
categoryId);
int size = categoryList.size();
for (int i = 0; i < size; i++) {
TCategory category = categoryList.get(i);
categoryStr = categoryStr + "," + category.getCategoryId();
if (!category.getChildList().isEmpty()) {
Iterator<TCategory> it = category.getChildList().iterator();
while (it.hasNext()) {
categoryStr = categoryStr + "," + it.next().getCategoryId();
}
}
}
return categoryStr;
}
其中baseDao的代码如下。
package com.qgranite.dao;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import javax.annotation.Resource;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;
/**
* 所有dao基类
*
* @author xdx
*
* @param <T>
* @param <PK>
*/
@Repository("baseDao")
public class BaseDao<T, PK extends Serializable> {
private Class<T> enetityClass;
@Resource(name = "sqlSessionTemplate")
private SqlSessionTemplate sqlSessionTemplate;
// 构造方法,根据实例类自动获取实体类型,这边利用java的反射
public BaseDao() {
this.enetityClass = null;
Class c = getClass();
Type t = c.getGenericSuperclass();
if (t instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) t;
Type[] type = p.getActualTypeArguments();
this.enetityClass = (Class<T>) type[0];
}
}
/**
* 获取实体
*
* @param id
* @return
*/
public T getT(String sql, Object param) {
return sqlSessionTemplate.selectOne(sql, param);
}
/**
* 不带查询参数的列表
* @param str
* @return
* @throws Exception
*/
public List<T> findTList(String sql){
return sqlSessionTemplate.selectList(sql);
}
/**
* 带有参数的列表
*
* @param str
* @param param
* @return
* @throws Exception
*/
public List<T> findTListByParam(String sql, Object param) {
return sqlSessionTemplate.selectList(sql, param);
}
/**
* 插入一条数据,参数是t
*
* @param sql
* @param t
* @return
*/
public int addT(String sql, T t) {
return sqlSessionTemplate.insert(sql, t);
}
/**
* 修改一条数据,参数是t
* @param sql
* @param t
* @return
*/
public int updateT(String sql,T t){
return sqlSessionTemplate.update(sql, t);
}
/**
* 删除t,参数是主键
* @param sql
* @param t
* @return
*/
public int deleteT(String sql,PK pk){
return sqlSessionTemplate.delete(sql, pk);
}
/**
* 根据param获取一个对象
* @param sql
* @param param
* @return
*/
public Object getObject(String sql,Object param){
return sqlSessionTemplate.selectOne(sql,param);
}
}