在mybatis中使用Criteria式条件查询

在使用常规的mybatis时,我们经常碰到的问题就是条件式查询。在一个查询界面,查询条件较多,并且运算符并不总是=时,在后台就需要拼装sql语句。这种处理方式肯定不是使用mybatis的初衷,对于使用了hibernate的我来说,如果mybatis也有一套criteria查询就好了。在具体实现中,我们只需要按照hibernate的处理方式定义好相应的criteria,最后传递给mybatis,其自身处理相应的条件和参数信息,最终返回相应的数据即可。如下一个示例代码所示:

List<Criteria> criteriaList = Lists.newArrayList();
criteriaList.add(Criterias.eq("aaa",111));//等于某个值
criteriaList.add(Criterias.ge("date",new Date()));//大于或等于某个时间
criteriaList.add(Criterias.in("code", new String[]{"a","b","c"}));//代码值在一个集合当中

如果使用这种方式,无疑会大大降低编写表单式查询的代码复杂度。同时,在内部处理中也不需要作任何判断,而直接将生成的sql交给mybatis去执行即可。当然,我们不希望生成的sql连我们自己都看不懂(想一想hibernate生成的sql),最终生成的sql像下面这样即可。

select * from table where aaa = #{aaa1,jdbcType=NUMERIC} and date >= #{date2,jdbcType=TIMESTAMP} and code in (#{code1,jdbcType=VARCHAR},#{code2,jdbcType=VARCHAR},#{code3,jdbcType=VARCHAR})

这是标准的mybatis语句,在进行代码调试和处理时也方便进行查看并处理。那么整个处理逻辑即变成如何处理参数信息,即如下所示的语句

字段名 运算符 #{参数名,jdbcType=字段类型} //filed = #{param1,jdbcType=VARCHAR}

参考 数据库表与java域模型之间的mapping和自动生成(基于mybatis)。我们可以很容易地就完成这个处理。分别处理 字段名 运算符 参数名 字段类型 参数映射即可。

1    字段名

在参数传递中,我们传递了date参数名。我们认识这个是指定类的字段,通过模型映射,我们可以将其直接转换为数据库表中的字段信息。如下所示:

ColumnMapping columnMapping = DomainUtils.getXXXXColumnMapping(propertyClazz, property);
String jdbcField = columnMapping.getJdbcField();

2    运算符

根据不同的查询条件,需要产生不同的运算。我们希望通过统一的工厂类方法产生统一的criteria,而不同的实现自己处理运行符即可。那么,在整体上,我们就可以定义一个统一的criteria接口,其产生一个条件SQL输出。如下所示:

public interface Criteria<T> {
	public Mql<T> toMql(Class rootClazz, String paramPrefix);
}

而具体的实现,可以通过工厂方法中的参数将独立实现所需要的数据信息传递至参数中即可。如本文所说的EqCriteria,即表示一个相等式运算。定义如下所示:

/** 生成相等式条件 */
	public static <T> Criteria eq(String propertyStr, T value) {
		return new EqCriteria<T>(propertyStr, value);
	}

其具体实现,也很简单,即将属性所对应的字段与值之间作一个相等式连接即可。如下所示:

String jdbcField = 获取数据表字段值;
return jdbcFile + "=" + “#{” + 参数名 + ",jdbcType=" + 类型类型 + "}"

3    参数名

一般情况下,我们可以使用默认传递的参数即可。但考虑到在具体实现中,容易出现参数同名的问题,如在in查询中。如果生成的sql如下所示,就错了。

code in (#{code,jdbcType=VARCHAR},#{code,jdbcType=VARCHAR},#{code,jdbcType=VARCHAR})

在参数传递上,这里肯定为一个集合(in式查询中).我们希望将集合中的每一个数据信息与对应的参数进行匹配。如将参数转换为类似code1,code2,code3的格式即可以了。在这个步骤中,我们引入一个类似递增器的处理方式,即在参数名后追加数字,而且通过仔细处理,让在单个线程中,每一个参数名不同即可。如下的一个参考实现:

/** 生成参数占位符 */
	protected String generateParamPoint(String property) {
		int value = pointThreadLocal.get();
		int newValue = value + 1;
		if(newValue == 1000) {
			newValue = 0;
		}
		pointThreadLocal.set(newValue);

		return property + "_" + value;
	}

4    参数类型

与第1步处理类型,根据参数直接获取相应的模型映射即可。这里不再叙述。

5    参数映射

在第3步中,我们为每一个参数生成了惟一的占位符,那么就需要存储此参数信息,以传递给mybatis。这里我们使用了一个map来存储信息。简单的实现如下所示:

Map paramMap = Maps.newHashMap();
String paramPoint = generateParamPoint(property);//生成占位符
paramMap.put(paramPoint,value);//将相应参数放入参数表中

6    集成处理

针对前5个步骤,我们已经可以处理单个条件产生的信息,最终每个条件生成一个条件语句和一个参数表信息。通过循环将所有的条件进行叠加处理,即形成了一个criteriaMqlList和paramMap,最终这个信息与查询主体之间,形成一个可以传递给mybatis处理的条件,如下定义所示:

	public <T> List<T> list(@Param("table") Table table,@Param("mqlList") List<String> mqlList,@Param("p") Map<String, Object> paramMap, Page page);

其中table表示要查询哪个数据表,可以通过模型映射得出。page为分页参数。具体的实现也很简单,如下参考所示:

select * from ${table.schema()}.${table.name()} 
		<if test="mqlList.size > 0">
			where
			<foreach collection="mqlList" item="mql" separator="and">
				${mql}
			</foreach>
		</if>

其中参数paramMap并没有在sql中用到,这是因为其定义在criteria的实现中被使用,在处理完$替换后最终会生成如下类似的mql:

select * from schema.table where aaa = #{p.aa_1,jdbcType=VARCHAR} and code in (#{p.code_2,jdbcType=VARCHAR},#{p.code_3,jdbcType=VARCHAR},#{p.code_4,jdbcType=VARCHAR})

这样mybatis就能够处理sql语句,并能返回我们所需要的对象了。这里面,没有涉及如何处理resultType,可以参考:在Mybatis中使用接口继承实现通用性crud

转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/201312250002.html

相关文章:

作者: flym

I am flym,the master of the site:)

发表评论

电子邮件地址不会被公开。 必填项已用*标注