MyBatis构建sql时动态传⼊表名以及字段名
 ⼀直在使⽤Mybatis这个ORM框架,都是使⽤mybatis⾥的⼀些常⽤功能。今天在项⽬开发中有个业务是需要限制各个⽤户对某些表⾥的字段查询以及某些字段是否显⽰,如某
张表的某些字段不让⽤户查询到。这种情况下,就需要构建sql来动态传⼊表名、字段名了。现在对解决⽅法进⾏下总结,希望对遇到同样问题的伙伴有些帮助。
  动态SQL是mybatis的强⼤特性之⼀,mybatis在对sql语句进⾏预编译之前,会对sql进⾏动态解析,解析为⼀个BoundSql对象,也是在此处对动态sql进⾏处理。下⾯让我们
先来熟悉下mybatis⾥#{}与${}的⽤法:
  在动态sql解析过程,#{}与${}的效果是不⼀样的:
#{ } 解析为⼀个 JDBC 预编译语句(prepared statement)的参数标记符。
  如以下sql语句
select * from user where name = #{name};
  会被解析为:
select * from user where name = ?;
  可以看到#{}被解析为⼀个参数占位符?。
${ } 仅仅为⼀个纯碎的 string 替换,在动态 SQL 解析阶段将会进⾏变量替换
  如以下sql语句:
select * from user where name = ${name};
  当我们传递参数“sprite”时,sql会解析为:
select * from user where name = "sprite";
  可以看到预编译之前的sql语句已经不包含变量name了。
综上所得, ${ } 的变量的替换阶段是在动态 SQL 解析阶段,⽽ #{ }的变量的替换是在 DBMS 中。
#{}与${}的区别可以简单总结如下:
#{}将传⼊的参数当成⼀个字符串,会给传⼊的参数加⼀个双引号
${}将传⼊的参数直接显⽰⽣成在sql中,不会添加引号
#{}能够很⼤程度上防⽌sql注⼊,${}⽆法防⽌sql注⼊
  ${}在预编译之前已经被变量替换了,这会存在sql注⼊的风险。如下sql
select * from ${tableName} where name = ${name}
  如果传⼊的参数tableName为user; delete user; --,那么sql动态解析之后,预编译之前的sql将变为:
select * from user; delete user; -- where name = ?;
  --之后的语句将作为注释不起作⽤,顿时我和我的⼩伙伴惊呆了!!!看到没,本来的查询语句,竟然偷偷的包含了⼀个删除表数据的sql,是删除,删除,删除!!!重要
的事情说三遍,可想⽽知,这个风险是有多⼤。
${}⼀般⽤于传输数据库的表名、字段名等
能⽤#{}的地⽅尽量别⽤${}
  进⼊正题,通过上⾯的分析,相信⼤家可能已经对如何动态调⽤表名和字段名有些思路了。⽰例如下:
<select id="getUser" resultType="java.util.Map" parameterType="java.lang.String" statementType="STATEMENT">
select
${columns}
from ${tableName}
where COMPANY_REMARK = ${company}
</select>
增加字段的sql语句  要实现动态调⽤表名和字段名,就不能使⽤预编译了,需添加statementType="STATEMENT"" 。
statementType:STATEMENT(⾮预编译),PREPARED(预编译)或CALLABLE中的任意⼀个,这就告诉 MyBatis 分别使⽤Statement,PreparedStatement或者CallableStatement。默认:PREPAR
ED。这⾥显然不能使⽤预编译,要改成⾮预编  其次,sql⾥的变量取值是${xxx},不是#{xxx}。
  因为${}是将传⼊的参数直接显⽰⽣成sql,如${xxx}传⼊的参数为字符串数据,需在参数传⼊前加上引号,如:
String name = "sprite";
name = "'" + name + "'";
  mybatis动态调⽤表名和字段名,还可以应⽤于⽇志的收集上,如数据库的⽇志表,每隔⼀个⽉动态建⼀个⽇志表,表名前缀相同(如log_201610,log_201611等),这样实
现⽇志的分⽉分表存储,⽅便⽇志的分析。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
⽤了mybatis很长⼀段时间了,但是感觉⽤的都是⽐较基本的功能,很多mybatis相对ibatis的新功能都没怎么⽤过。⽐如其内置的注解功能之类的。这次遇到了⼀个问题,每次我
们在配置mybaits时,需要在l⽂件中写对应的执⾏sql脚本。这时我们⼀般会先定义实体类来作为sql的返回类型或者执⾏sql的参数类型。⽐如如下代码
Java代码
1. <select id="queryApplyStatusNum" parameterType="ApplyCriteria"
2.        resultType="ApplyStatusNumDto">
3.        select
4.        o.statecode as statusCode,count(*) as statusNum
5.        from
6.        TM_ConsultationApply o
7.        where
8.        <if test="doctorCode != null and doctorCode != ''"> o.DoctorCode = #{doctorCode} and </if>
9.        <if test="beginDate != null and endDate != null">
10.            ((trunc(o.AppointBeginDate )<=
11.            to_date(#{endDate},'yyyy/mm/dd')) and
12.            (to_date(#{beginDate},'yyyy/mm/dd')
13.            <=o.AppointEndDate)) and  </if>
14.        1=1
15.        group by o.statecode
16. </select>
这是个很简单的查询,其中parameterType为对应的参数实体类,这些类中的字段将会替换sql语句中的类似#{}的语句,使之成为完整的sql 语句。同样的,resultType同样对应的是返回值的实体类。这⾥可以细说⼀下,其实在mybatis中,⽆论你指定还是不指定返回类型,mybatis都会默认的先将查询回的值放⼊⼀个hashMap中(如果返回的值不⽌⼀条就是⼀个包含hashMap的list)。这其中的区别在于,如果你指定了返回类型,mybatis将会根据返回类型的实体类来从hashMap中获取值并set到这个实体类中。如果不指定就默认返回⼀个HashMap<String,Object>(List<HashMap<String,Object>>)。
okay~⼤概讲解到此,说说这次的问题。以前都是指定了实体类,然后写好sql语句直接套⽤就可以了。但是现在有个问题,万⼀你的物理模型不确定,也即是你的表结构不确定,甚⾄连表名字都不确定该怎么办呢?我这次遇到了这个问题。我们有个需求,事先定义好了很多数据集的信息模型,针对这些信息模型⽣成物理模型。⽽我们需要针对这些物理模型进⾏操作。⽽这些数据集⼀旦更新,信息模型以及物理模型都要变动,所以事先不可能完全确定物理表结构等等信息。此时应该怎么在mybatis中进⾏处理呢?
这⾥在说⼀下mybatis中⼀个属性:statementType。这个属性的作⽤是告诉mybatis我们写的这个sql到底是预编译(PRESTATEMENT)还是⾮预编译(STATEMENT)的。有什么区别呢?如果是预编译的,那么系统在初始化时就会读取这段sql代码,将指定的实体类中的字段替换了类似#{}这样的语句,就是形成了类似这样的语句:
"select * from tableName where code=?" 这个时候你在系统运⾏时再想向这句sql中替换tableName或者code,结果可想⽽知。如果是⾮预编译呢,结果刚好相反,他会在系统运⾏时才会去⽣成这样类似的语句。此时就可以去替换这些动态的字段或者表名之类。这样在结合之前所讲的返回类型的设置,我们的问题就解决了
。我们可以不⽤设定参数和返回类型的实体类,只需要形成⼀个动态的表名和字段名的列表类。就可以动态对那些未知的物理模型进⾏操作.如下代码可作参考:
Sql代码
1. <select id="queryMetaList" resultType="Map" statementType="STATEMENT">
2.        select * from ${tableName} t where
3.        <foreach item="item" index="index" collection="field" open=" "
4.            separator="and" close=" ">
5.            <choose>
6.                <when test="item.fieldType == 'DATE' and item.dateQueryFlag == 0">
7.                    ${item.fieldCode} between
8.                    to_date('${item.fieldValue}','yyyy-mm-dd
9.                    hh24:mi:ss')
10.                </when>
11.                <when test="item.fieldType == 'DATE' and item.dateQueryFlag == 1">
12.                    to_date('${item.fieldValue}','yyyy-mm-dd
13.                    hh24:mi:ss')
14.                </when>
15.                <when test="item.fieldItemCode != null and item.fieldItemCode != ''">
16.                    ${item.fieldCode} =
17.                    '${item.fieldItemCode}'
18.                </when>
19.                <otherwise>
20.                    ${item.fieldCode} =
21.                    '${item.fieldValue}'
22.                </otherwise>
23.            </choose>
24.        </foreach>
25.
26. </select>
对了,漏了⼀句,如果是⾮预编译的话,最好使⽤${}⽽不是#{}
-----------------------------------------------------------------------------------------------------

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。