先简单说下Mybatis的动态sql,这不是今天的重点。web
MyBatis的动态SQL是基于OGNL表达式的,它能够帮助咱们方便的在SQL语句中实现某些逻辑。
sql
例如,sql语句where条件中,须要一些安全判断,例如按某一条件查询时若是传入的参数是空,此时查询出的结果极可能是空的,也许咱们须要参数为空时,是查出所有的信息
安全
MyBatis中用于实现动态SQL的元素主要有:mybatis
if架构
choose(when,otherwise)app
trimide
wherespa
set架构设计
foreach设计
示例mapper.xml:
<select id="findActiveBlogLike" parameterType="BLOG" resultType="BLOG"> SELECT * FROM BLOG WHERE <trim prefix="WHERE" prefixOverrides="AND |OR "> <choose> <when test="title != null"> AND title like #{title} </when> <when test="author != null and author.name != null"> AND title like #{author.name} </when> <otherwise> AND featured = 1 </otherwise> </choose> </trim> </select> <update id="updateAuthorIfNecessary" parameterType="Author"> update Author <trim prefix="where" prefixOverrides=","> <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email}</if> </set> where id=#{id} </trim> </update>
可是问题来了,若是咱们没有实体怎么办?如上代码,都是关联实体Author,BLOG。
更进一步,若是咱们连字段和表名都是程序运行时产生的,那么在Mybatis中,咱们的mapper.xml又该如何写呢?
不要说这种需求不多,实际上不少灵活性和扩展性要求比较高的应用,物理模型不肯定,也便是你的表结构不肯定,甚至连表名字都不肯定,基本上sql中的每个字母都不是写死的。
我如今就遇到了这种需求,简单描述以下:
事先定义好了不少数据集的信息模型,针对这些信息模型生成物理模型。而咱们须要针对这些物理模型进行操做。而这些数据集一旦更新,信息模型以及物理模型都要变更,因此事先不可能彻底肯定物理表结构等等信息。此时应该怎么在mybatis中进行处理呢?
翻阅mybatis文档,在一个不起眼的地方发现update标签有一个属性statementType,根据jdbc的经验,这应该是控制sql预编译仍是非预编译的,若是sql执行是预编译的,那么动态传入字段名,表名之类的,显然
是不行的,因此你必须改为非预编译的。
二者有什么区别呢?若是是预编译的,那么系统在初始化时就会读取这段sql代码,将指定的实体类中的字段替换了相似#{}这样的语句,就是造成了相似这样的语句:
"select * from tableName where id=?" 这个时候你在系统运行时再想向这句sql中替换tableName或者id,结果可想而知。若是是非预编译呢,结果恰好相反,他会在系统运行时才会去生成这样相似的语句。此时就能够去替换这些动态的字段或者表名之类。这样在结合以前所讲的返回类型的设置,咱们的问题就解决了。
咱们能够不用设定参数和返回类型的实体类,只须要造成一个动态的表名和字段名的列表类。就能够动态对那些未知的物理模型进行操做.以下代码可做参考:
<select id="queryMetaList" resultType="Map" statementType="STATEMENT"> select * from ${tableName} t where <foreach item="item" index="index" collection="field" open=" " separator="and" close=" "> <choose> <when test="item.fieldType == 'DATE' and item.dateQueryFlag == 0"> ${item.fieldCode} between to_date('${item.fieldValue}','yyyy-mm-dd hh24:mi:ss') </when> <when test="item.fieldType == 'DATE' and item.dateQueryFlag == 1"> to_date('${item.fieldValue}','yyyy-mm-dd hh24:mi:ss') </when> <when test="item.fieldItemCode != null and item.fieldItemCode != ''"> ${item.fieldCode} = '${item.fieldItemCode}' </when> <otherwise> ${item.fieldCode} = '${item.fieldValue}' </otherwise> </choose> </foreach> </select>
注:会有sql注入危险,代码中要处理。
另外,注意返回值,在mybatis中,不管你指定仍是不指定返回类型,mybatis都会默认的先将查询回的值放入一个hashMap中(若是返回的值不止一条就是一个包含hashMap的list)。这其中的区别在于,若是你指定了返回类型,mybatis将会根据返回类型的实体类来从hashMap中获取值并set到这个实体类中。若是不指定就默认返回一个HashMap<String,Object>(List<HashMap<String,Object>>)。
咱们没有实体,固然就不要指定返回值,默认接受处理List<HashMap<String,Object>>结构的返回值便可。
最后发点感慨:
亲,不要再把实体写死了,或者不要有实体。实体写死,最直接的后果就是:Hibernate会把sql框死,Mybatis也会把sql框死,手写jdbc放入实体,也同样是死,有新模块,你还得从web到dao写一串,累不累,你还怎么玩。
因此,作大型平台级应用的架构设计,尽可能不要停留在一个表对应一个实体的阶段,必须考虑应用的动态可扩展和灵活性。