MyBatis中mapper.xml其属性resultSets的作用及研究

在完成一个需求的时候,意外发现了resultSets这一属性,以前都没有接触过,现在在学习经历写下了做一次总结与分享

除了resultType,resultMap做映射外,Mybatis还提供了开放的API来做映射的处理。

  • resultTyperesultMap
    首先来说说MyBatis的resultType和resultMp,这个两个实际上就是对数据库中的列名和我们的Java实体中做了一对一的映射处理,使得数据库中的字段可以被赋值到我们的Java实体对象中,熟悉JDBC的童鞋肯定知道JDBC的结果集映射是多么烦,只不过这些繁琐的操作由Mybatis这个半自动ORM框架帮我们做了。

  • resultSets
    这个属性其实更像我们的JDBC,使用JDBC定制地去操作实体的映射
    话不多讲,先看代码

-- 表名做了处理
<select id="selectItemExportByQueryInfo" resultSets="financeInvoiceItemResultMapper">
SELECT bii.item_id id, bli.ln_std_name stdName, bli.id_card idCard, bli.std_no stdNo, bu.unvs_name unvsName, bli.grade, bup.pfsn_level pfsnLevel,
bup.pfsn_name pfsnName, bup.teach_method teachMethod, bli.std_stage stdStage, bli.inclusion_status inclusionStatus, bli.mobile, CONCAT(bia.province,bia.city, bia.district, bia.street, bia.receive_address) address,
oc.campus_name campusName, bii.item_code itemCode, bia.apply_time applyTime, bso.fee_amount feeAmount, bso.zm_scale zmScale, bso.coupon_scale couponScale,
(bso.pay_amount+bso.demurrage_scale) AS actualAmount, bii.status, IF(bsc.new_learn_id,1,0) AS hasChange, bii.export_status exportStatus
FROM
bms.m bii
LEFT JOIN bms.a bia ON bia.apply_id = bii.apply_id
LEFT JOIN bms.i bli ON bli.learn_id = bii.sub_learn_id
LEFT JOIN bms.u bu ON bu.unvs_id = bli.unvs_id
LEFT JOIN bms.p bup ON bup.pfsn_id = bli.pfsn_id
LEFT JOIN bms.s oc ON oc.campus_id = recruit_campus_id
LEFT JOIN pay.r bso ON bso.sub_order_no = bii.sub_order_no
LEFT JOIN bms.e bsc ON bsc.new_learn_id = bli.learn_id
	</select>

这里用到了resultSets="financeInvoiceItemResultMapper"
实际上,这个是我们公司里重新定制地一个MyBatis插件,在这个插件里面进行了一些操作,等下直接上代码,首先来看看,MyBtis的dtd文件里,是有resultSets给我们使用的,这一块是原生MyBatis自带的,而原生的MyBatis是使用DefaultResultSetHandler来进行处理,那resultSets究竟是做什么用的呢?
先看看那官方文档 http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html

通过描述,我们可以大概猜到,这个肯定是用来做映射用的(这不是废话吗),而且可以映射多个结果集。
那么,我们再看看是怎么使用的,还是在文档的底部,我们找到了一些使用案例。

也就是说,我们在对多个结果集做映射的场景的时候,我们是可以指定分别映射的,也是通过association来进行额外映射。
好,原生的我们就先说到这里,毕竟我也没有深究过哈哈。
现在来看看我们公司客制版的resultSets处理

  • 客制版resultSets
    上面说到,老大自己写了一套对resultSets处理的插件,基于学习的角度,知道大家都懒得看文字,也是一样,直接上代码
@Intercepts(@Signature(method = "handleResultSets", type = ResultSetHandler.class, args = { java.sql.Statement.class }))
/**
 * @desc 对结果集处理
 */
public class ResultHandlerInterceptor implements Interceptor {

	private static Logger logger = LoggerFactory.getLogger(ResultHandlerInterceptor.class);

	@Override
	public Object intercept(Invocation invocation) throws Throwable {	//获取参数值,调用execHandlerResult方法进行处理
		ResultSetHandler resultSetHandler = (ResultSetHandler) invocation.getTarget();
		MappedStatement mappedStatement = (MappedStatement) ReflectionUtils.getFieldValue(resultSetHandler,
				"mappedStatement");
		SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
		if (sqlCommandType == SqlCommandType.SELECT) {
			String mapper = StringUtil.join(mappedStatement.getResultSets());
			logger.info("ResultHandlerInterceptor.intercept,mapperRow:{}", mapper);
			if (!StringUtil.isEmpty(mapper)) {
				Statement statement = (Statement) invocation.getArgs()[0];
				return execHandlerResult(mapper, statement.getResultSet());
			}
		}
		return invocation.proceed();

	}

	/**
		* @dsec 客制化处理resultSet结果集
		* @param rs
		*/
	private Object execHandlerResult(String mapper, ResultSet rs) throws SQLException {
		try {
			ResultMapper resultMapper = ApplicationContextUtil.getBeanIgnoreEx(mapper);
			if (resultMapper != null) {
				return resultMapper.handler(rs);
			}
		} finally {
			if (rs != null) {
				rs.close();
			}
		}
		return null;
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {

	}

}

execHandlerResult方法中,获取了ResultMapper这个类,这个也是我们自己的定义的一个接口

/**
 * 
 * @desc RowMapper 基类处理
 * @author lingdian
 *
 * @param <T>
 */
@FunctionalInterface
public interface ResultMapper {

	/**
	 * @desc 处理整个ResultSet
	 * @param rs
	 * @return
	 * @throws SQLException
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	default Object handler(ResultSet rs) throws SQLException {
		List result = Lists.newArrayList();
		int row = 1;
		while (rs != null && rs.next()) {
			result.add(handlerRow(rs, row));
			row++;
		}
		return result;
	}

	/**
	 * @desc 处理当条ResultSet
	 * @param rs
	 * @return
	 * @throws SQLException
	 */
	Object handlerRow(ResultSet rs, int rowNum) throws SQLException;
}

通过这个抽象出来的ResultMapper接口,我们可以自己定义我们的handler方法。
好了,下面到了我要写这篇博客的目的了,就是我在其中感受到了代码的智慧哈哈哈
首先看看我实现ResultMapper的FinanceInvoiceItemResultMapper

@Component("financeInvoiceItemResultMapper")
public class FinanceInvoiceItemResultMapper implements ResultMapper {
    @Override
    public Object handler(ResultSet rs) throws SQLException {
        BaseExportList<BdInvoiceExport> list = new BaseExportList<>();
        int row = 1;
        while(rs != null && rs.next()){
            list.add(rs.getLong(1)+"",(BdInvoiceExport)handlerRow(rs,row));
            row++;
        }
        return list;
    }

    @Override
    public Object handlerRow(ResultSet resultSet, int i) throws SQLException {
        BdInvoiceExport bdInvoiceExport = new BdInvoiceExport();
        bdInvoiceExport.setId(resultSet.getLong(1));
		bdInvoiceExport.setName(resultSet.getString(2));
		... 					// 这里省去了其他的子段,实际上有很多
        return bdInvoiceExport;
    }
}

这里着重看handler方法,重写的这个方法,我们主要是改变原来的List,将实际类型替换为BaseExportList,这个list是继承了ArrayList的

public class BaseExportList<T> extends ArrayList<T> {

	private Map<String, T> data = Maps.newHashMap();

	public boolean add(String id, T e) {
		data.put(id, e);
		return super.add(e);
	}

	public T get(String id) {
		return data.get(id);
	}

	public Map<String, T> getData() {
		return data;
	}
	
	public Set<String> getKeys() {
		return data.keySet();
	}
}

通过观察这个List,我们可以很快的感受,用这个List我可以不通过循环,直接拿到我查询到的结果的id集合!
接着看回handler方法,剩下的也没什么了,就是调用BaseExportList的add方法,在add的时候就将id分好了。

p.s. 这里有个细节,就是BaseExportList的get方法是里面的参数是String类型的,并不是int类型。

而下面的handlerRow方法只是一个个取出来映射进去而已。

意义

在我这个场景中,利用resultSets的意义就在于我在导出数据时,我需要将这些数据改变导出的状态,一般做法是循环遍历导出的数据,然后获取这些id再批量修改状态,而利用的这个resultSets,则不再需要循环遍历了。

/*运用场景*/
BaseExportList<BdInvoiceExport> list = bdInvoiceMapper.selectItemExportByQueryInfo(bdInvoiceQuery);
// ......
Set<String> keys = list.getKeys();
bdInvoiceMapper.updateExportStatus(keys);	//修改状态
// ......

画外话

实际上,在看到以前同事用BaseExportList这个类的时候,我并不知道这个是和我们resultSets属性体系绑定使用了,我还是像原来一样写resultType,我认为是MyBatis在后面进行了转换类型,并调用了BaseExportList的add方法来插值,后面发现,在调用用list.getKeys()时拿不到值,于是开始debug MyBatis,这一过程也学习到了很多,以后有机会另写一篇博客来总结,这次debug最后得出的结论是,MyBatis一开始是将数据保存在ArrayList中,后面确实会转换list类型,但是它是通过反射,来创建一个用户声明的类型,这里是BaseExportList,然后再调用这个对象addAll()方法将原来ArrayList的数据保存在BaseExportList中。
后面和同事讨论了一下,因为BaseExportList是直接继承ArrayList的,而ArrayList的addAll方法是通过System.arraycopy来实现的,那么如果我们重写了BaseExportListaddAll方法,在里面使用add来设值,是不是也可以达到相同的效果嘞,嘿嘿,以后有机会试下

发布了25 篇原创文章 · 获赞 9 · 访问量 6634

猜你喜欢

转载自blog.csdn.net/qq_40233503/article/details/94436578