1.mybatis在xml文件中处理大于号小于号的方法
第一种方法:
用了转义字符把>和<替换掉,然后就没有问题了。
SELECT * FROM test WHERE 1 = 1 AND start_date <= CURRENT_DATE AND end_date >= CURRENT_DATE
附:XML转义字符
< < 小于号
> > 大于号
& & 和
' ’ 单引号
" " 双引号
第二种方法:
因为这个是xml格式的,所以不允许出现类似“>”这样的字符,但是都可以使用符号进行说明,将此类符号不进行解析
你的可以写成这个:
mapper文件示例代码
关于每个标签语句结尾要不要分号的问题(不要!!!!!!)
今天在写SQL查询Oracle中的数据时遇到一个问题。在一般的SQL查询分析器中写好的SQL语句(运行一切正常),扔到用C#写的程序中就报错。错误代码如下:
System.Data.OleDb.OleDbException:One or more errors occurred during processing of command.
ORA-00911: invalid character at…
检查了半天,实在是没找到任何SQL错误的原因,(本来怀疑是字符转码的问题,后来给排除了);最后,终于在网上查到了答案,原来“都是分号惹的祸”!
我一般写SQL的时候都喜欢在每个语句结尾加上”:”,我想这也是一般写SQL的程序员的习惯。因为很多SQL的查询分析其时都会将这个分号当成一个语句的结束。但是,其实在正式执行的时候,是不能将这个分号扔到Oracle的解析器中的,因为Oracle的语法解析器特别严格,就会报出以上的错误出来,解决方法也很简单,去掉分号就可以了。
以上的问题适用于任何用程序书写的Oracle数据查询,看起来有时候习惯也会害死人的。
同样:在使用Pagehelper分页时同样会出现问题
### SQL: SELECT h.HONOR_ID, h.HONOR_NAME, s.STU_ID, s.STU_NAME,
s.STU_SEX, s.STU_COMMENT FROM honor h, student s
where h.HONOR_OWNER = s.STU_ID; LIMIT ?, ?
关于#{} 和${}问题
#{}是预编译处理,${}是字符串替换。 Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值; Mybatis在处理${}时,就是把${}替换成变量的值。 使用#{}可以有效的防止SQL注入,提高系统安全性。 一般能用#的就别用$ $方式一般用于传入数据库对象,例如传入表名.
模糊查询
模糊查询like语句该怎么写
string wildcardname = “%smi%”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like #{value} >
第2种:在sql语句中拼接通配符,会引起sql注入
string wildcardname = “smi”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like "%"#{value}"%" >
关于批量插入:
首先,创建一个简单的insert语句:
<insert id=”insertname”> insert into names (name) values (#{value}) >
listnames = new arraylist(); names.add(“fred”); names.add(“barney”); names.add(“betty”); names.add(“wilma”); // 注意这里 executortype.batch sqlsession sqlsession = sqlsessionfactory.opensession(executortype.batch); try { namemapper mapper = sqlsession.getmapper(namemapper.class); for (string name : names) { mapper.insertname(name); } sqlsession.commit(); }catch(Exception e){ e.printStackTrace(); sqlSession.rollback(); throw e; } finally { sqlsession.close(); }
2.mybatis传入多个参数
需要查阅本文的基本都是需要传入多个参数的,这里记住一句话:无论你传的参数是什么样的,最后mybtis都会将你传入的转换为map的,那么既然这样,当我们要传入多个参数时,何不直接给与map类型即可,然后mapper.xml通过#{map.key}来获取值即可,这个特别适合动态搜索,或者多个参数的查询,并且可以在mapper的xml语句中通过if判断来实现若为空,则不添加查询条件,
<if test="userId != null"> #{userId,jdbcType=VARCHAR}, if>
for来进行遍历
一、单个参数:
<select id="getXXXBeanList" parameterType="java.lang.String" resultType="XXBean"> select t.* from tableName t where t.id= #{id} select>
public List<XXBean> getXXBeanList(@param("id")String id); 其中方法名和ID一致,#{}中的参数名与方法中的参数名一致, 这里采用的是@Param这个参数,实际上@Param这个最后会被Mabatis封装为map类型的。 select 后的字段列表要和bean中的属性名一致, 如果不一致的可以用 as 来补充。
二、多参数:
方案1
public List<XXXBean> getXXXBeanList(String xxId, String xxCode); <select id="getXXXBeanList" resultType="XXBean"> select t.* from tableName where id = #{arg0} and name = #{arg1} select>
由于是多参数那么就不能使用parameterType, 改用#{index}是第几个就用第几个的索引,索引从0开始
或者param1,param2.索引从1开始。
方案2(推荐)基于注解
public List<XXXBean> getXXXBeanList(@Param("id")String id, @Param("code")String code); <select id="getXXXBeanList" resultType="XXBean"> select t.* from tableName where id = #{id} and name = #{code} select>
由于是多参数那么就不能使用parameterType, 这里用@Param来指定哪一个
三、Map封装多参数:
public List<XXXBean> getXXXBeanList(HashMap map); <select id="getXXXBeanList" parameterType="hashmap" resultType="XXBean"> select 字段... from XXX where id=#{xxId} code = #{xxCode} select>
其中hashmap是mybatis自己配置好的直接使用就行。map中key的名字是那个就在#{}使用那个,map如何封装就不用了我说了吧。
四、List封装in:
public List<XXXBean> getXXXBeanList(List<String> list); <select id="getXXXBeanList" resultType="XXBean"> select 字段... from XXX where id in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} foreach> select> foreach 最后的效果是select 字段... from XXX where id in ('1','2','3','4')
五、selectList()只能传递一个参数,但实际所需参数既要包含String类型,又要包含List类型时的处理方法:
将参数放入Map,再取出Map中的List遍历。如下:
List<String> list_3 = new ArrayList<String>(); Map<String, Object> map2 = new HashMap<String, Object>(); list.add("1"); list.add("2"); map.put("list", list); //网址id map.put("siteTag", "0");//网址类型 public List<SysWeb> getSysInfo(Map<String, Object> map2) { return getSqlSession().selectList("sysweb.getSysInfo", map2); } <select id="getSysInfo" parameterType="java.util.Map" resultType="SysWeb"> select t.sysSiteId, t.siteName, t1.mzNum as siteTagNum, t1.mzName as siteTag, t.url, t.iconPath from TD_WEB_SYSSITE t left join TD_MZ_MZDY t1 on t1.mzNum = t.siteTag and t1.mzType = 10 WHERE t.siteTag = #{siteTag } and t.sysSiteId not in <foreach collection="list" item="item" index="index" open="(" close=")" separator=","> #{item} foreach> select>
3.Attribute "resultType" must be declared for element type "update".
<update id ="updateAllScore" resultType ="int"> UPDATE score s SET <if test ="s.SCORE_NUM >=90"> s.SCORE_LEVEL ="A" if> ; update>
解决错误就是把resultType去掉,因为在insert和update语句中是没有返回值的。小坑小坑。分享一下常识性问题。
4.typeAliases
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:
<typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/> typeAliases>
5.sql
这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password sql>
<select id="selectUsers" resultType="map"> select <include refid="userColumns"><property name="alias" value="t1"/>include>, <include refid="userColumns"><property name="alias" value="t2"/>include> from some_table t1 cross join some_table t2 select>
6.高级结果映射
<!-- Very Complex Statement --> <select id="selectBlogDetails" resultMap="detailedBlogResultMap"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio, A.favourite_section as author_favourite_section, P.id as post_id, P.blog_id as post_blog_id, P.author_id as post_author_id, P.created_on as post_created_on, P.section as post_section, P.subject as post_subject, P.draft as draft, P.body as post_body, C.id as comment_id, C.post_id as comment_post_id, C.name as comment_name, C.comment as comment_text, T.id as tag_id, T.name as tag_name from Blog B left outer join Author A on B.author_id = A.id left outer join Post P on B.id = P.blog_id left outer join Comment C on P.id = C.post_id left outer join Post_Tag PT on PT.post_id = P.id left outer join Tag T on PT.tag_id = T.id where B.id = #{id} select>
<resultMap id="detailedBlogResultMap" type="Blog"> <constructor> <idArg column="blog_id" javaType="int"/> constructor> <result property="title" column="blog_title"/> <association property="author" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> <result property="favouriteSection" column="author_favourite_section"/> association> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <association property="author" javaType="Author"/> <collection property="comments" ofType="Comment"> <id property="id" column="comment_id"/> collection> <collection property="tags" ofType="Tag" > <id property="id" column="tag_id"/> collection> <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> discriminator> collection> resultMap>
先来看看resultMap中都有那些属性:
<resultMap> <constructor> <idArg/> <arg/> constructor> <id/> <result/> <association property=""/> <collection property=""/> <discriminator javaType=""> <case value="">case> discriminator> resultMap>
constructor主要是用来配置构造方法,默认情况下mybatis会调用实体类的无参构造方法创建一个实体类,然后再给各个属性赋值,但是有的时候我们可能为实体类生成了有参的构造方法,并且也没有给该实体类生成无参的构造方法,这个时候如果不做特殊配置,resultMap在生成实体类的时候就会报错,因为它没有找到无参构造方法。这个时候mybatis会报如下错误:
解决办法:添加,constructor,如果主键自增,不要使用带主键的构造函数
resultMap id="studentMap" type="com.my.du.entity.Student"> <constructor> <arg column="STU_NAME" javaType="String"/> <arg column="STU_SEX" javaType="String"/> <arg column="STU_COMMENT" javaType="String"/> constructor> <id property="stuId" column="STU_ID" javaType="int" /> <result property="stuName" column="STU_NAME" javaType="String" /> <result property="stuSex" column="STU_SEX" javaType="String" /> <result property="stuComment" column="STU_COMMENT" javaType="String" /> resultMap>
6.1一个resultMap包含另外一个配置方法:
<resultMap id="studentMap" type="com.my.du.entity.Student"> <id property="stuId" column="STU_ID" javaType="int" /> <result property="stuName" column="STU_NAME" javaType="String" /> <result property="stuSex" column="STU_SEX" javaType="String" /> <result property="stuComment" column="STU_COMMENT" javaType="String" /> resultMap> <resultMap id="honorMap" type="com.my.du.entity.Honor"> <id property="honorId" column="HONOR_ID" javaType="String" /> <result property="honorName" column="HONOR_NAME" javaType="String" /> <association property="honorOwner" resultMap ="studentMap" > association> resultMap>
或者
<resultMap id="honorMap" type="com.my.du.entity.Honor"> <id property="honorId" column="HONOR_ID" javaType="String" /> <result property="honorName" column="HONOR_NAME" javaType="String" /> <association property="honorOwner" javaType="com.my.du.entity.Student"> <id property="stuId" column="STU_ID" javaType="int" /> <result property="stuName" column="STU_NAME" javaType="String" /> <result property="stuSex" column="STU_SEX" javaType="String" /> <result property="stuComment" column="STU_COMMENT" javaType="String" /> association> resultMap>
或者
<resultMap id="honorMap" type="com.my.du.entity.Honor"> <id property="honorId" column="HONOR_ID" javaType="String" /> <result property="honorName" column="HONOR_NAME" javaType="String" /> <association property="honorOwner" column="STU_ID" javaType="com.my.du.entity.Student" select="getStudentById"/> resultMap> <select id="getStudentById" parameterType="int" resultMap="studentMap"> SELCT * FROM student WHERE STU_ID = #{stuId} select>
非常重要: 在嵌套据诶过映射中 id 元素扮演了非常重要的角色。应应该通常指定一个 或多个属性,它们可以用来唯一标识结果。实际上就是如果你离开她了,但是有一个严重的 性能问题时 MyBatis 仍然可以工作。选择的属性越少越好,它们可以唯一地标识结果。主键 就是一个显而易见的选择(尽管是联合主键)。
我们在resultMap中指定了association节点,association节点中的select属性表示要执行的方法,该方法实际上指向了一条SQL语句(就是我们在aliasMapper.xml中配置的那条SQL语句),column表示给方法传入的参数的字段,我们这里要传入省份的id,所以column为id,property表示select查询的结果要赋值给谁,我们这里当然是赋值给Province的alias属性。
集合类型
<resultMap id="honorMap" type="com.my.du.entity.Honor"> <id property="honorId" column="HONOR_ID" javaType="String" /> <result property="honorName" column="HONOR_NAME" javaType="String" /> <association property="honorOwner" column="STU_ID" javaType="com.my.du.entity.Student" select="getStudentById"/> <collection property="flags" column="HONOR_ID" javaType="ArrayList" ofType="com.my.du.entity.Flag" select="com.my.du.dao.StudentMapper.getAllFlags"> collection> resultMap> <resultMap id="flagMap" type="com.my.du.entity.Flag"> <id property="flagId" column="FLAG_ID" javaType="String" /> <result property="flagName" column="FLAG_NAME" javaType="String" /> <result property="honorId" column="HONOR_ID" javaType="String" /> resultMap>
package com.my.du.entity; import java.util.List; public class Honor { private String honorId; private String honorName; private Student honorOwner; private Listflags; /** * @return the honorId */ public String getHonorId() { return honorId; } /** * @param honorId the honorId to set */ public void setHonorId(String honorId) { this.honorId = honorId; } /** * @return the honorName */ public String getHonorName() { return honorName; } /** * @param honorName the honorName to set */ public void setHonorName(String honorName) { this.honorName = honorName; } /** * @return the honorOwner */ public Student getHonorOwner() { return honorOwner; } /** * @param honorOwner the honorOwner to set */ public void setHonorOwner(Student honorOwner) { this.honorOwner = honorOwner; } /** * @return the flags */ public List getFlags() { return flags; } /** * @param flags the flags to set */ public void setFlags(List flags) { this.flags = flags; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Honor [honorId=" + honorId + ", honorName=" + honorName + ", honorOwner=" + honorOwner + ", flags=" + flags + "]"; } }
7.动态sql
等于条件的书写:
<when test='flagId == "a" or flagId == "A" '> 或者 <when test="flagId == 'a'.toString() or flagId == 'A'.toString() ">
不能写成
<when test="flagId == 'a' or flagId == 'A' ">
因为mybatis会把'Y'解析为字符,java是强类型语言,所以不能这样写。
if
<update id="updateScoreLevelByScore" parameterType="Score"> UPDATE score s SET <if test="scoreNum >=90"> s.SCORE_LEVEL ="A" if> <if test="scoreNum >=80 and scoreNum <90 "> s.SCORE_LEVEL ="B" if> <if test="scoreNum >=70 and scoreNum <80 "> s.SCORE_LEVEL ="C" if> <if test="scoreNum >=60 and scoreNum <70 "> s.SCORE_LEVEL ="D" if> <if test=" scoreNum <60 "> s.SCORE_LEVEL ="D" if> where s.SCORE_ID = #{scoreId} ; update>
choose, when, otherwise
<when test='flagId == "a" or flagId == "A" '> AND FLAG_ID = 'A' when> <when test='flagId == "b" or flagId == "B" '> AND FLAG_ID = 'B' when> <otherwise> AND FLAG_ID ='C' otherwise>
trim, where, set
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE <if test="state != null"> state = #{state} if> <if test="title != null"> AND title like #{title} if> <if test="author != null and author.name != null"> AND author_name like #{author.name} if> select>
如果这些条件没有一个能匹配上将会怎样?最终这条 SQL 会变成这样
SELECT * FROM BLOG
WHERE
这会导致查询失败。如果仅仅第二个条件匹配又会怎样?这条 SQL 最终会是这样
SELECT * FROM BLOG
WHERE
AND title like 'someTitle'
这个查询也会失败。这个问题不能简单的用条件句式来解决,如果你也曾经被迫这样写过,那么你很可能从此以后都不想再这样去写了。
最简单的解决办法 1=1
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE 1=1 <if test="state != null"> AND state = #{state} if> <if test="title != null"> AND title like #{title} if> <if test="author != null and author.name != null"> AND author_name like #{author.name} if> select>
或者
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG <where> <if test="state != null"> state = #{state} if> <if test="title != null"> AND title like #{title} if> <if test="author != null and author.name != null"> AND author_name like #{author.name} if> where> select>
where 元素知道只有在一个以上的if条件有值的情况下才去插入"WHERE"子句。而且,若最后的内容是"AND"或"OR"开头的,where 元素也知道如何将他们去除
如果 where 元素没有按正常套路出牌,我们还是可以通自定义 trim 元素来定制我们想要的功能。比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR "> ... trim>
prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的)。它带来的结果就是所有在 prefixOverrides 属性中指定的内容将被移除,并且插入 prefix 属性中指定的内容。
类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的。比如:
<update id="updateAuthorIfNecessary"> update Author <set> <if test="username != null">username=#{username},if> <if test="password != null">password=#{password},if> <if test="email != null">email=#{email},if> <if test="bio != null">bio=#{bio}if> set> where id=#{id} update>
这里,set 元素会动态前置 SET 关键字,同时也会消除无关的逗号,因为用了条件语句之后很可能就会在生成的赋值语句的后面留下这些逗号。
若你对等价的自定义 trim 元素的样子感兴趣,那这就应该是它的真面目:
<trim prefix="SET" suffixOverrides=","> ... trim>
foreach
动态 SQL 的另外一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} foreach> select>
foreach 元素的功能是非常强大的,它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量。它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。
注意 你可以将一个 List 实例或者数组作为参数对象传给 MyBatis,当你这么做的时候,MyBatis 会自动将它包装在一个 Map 中并以名称为键。List 实例将会以"list"作为键,而数组实例的键将是"array"。
到此我们已经完成了涉及 XML 配置文件和 XML 映射文件的讨论。下一部分将详细探讨 Java API,这样才能从已创建的映射中获取最大利益。
<select id="getStudentByIds" resultMap="studentMap"> SELECT * FROM student WHERE STU_ID IN <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} foreach> ; select>
bind
bind
元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。比如:
<select id="selectBlogsLike" resultType="Blog"> <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" /> SELECT * FROM BLOG WHERE title LIKE #{pattern} select>
Multi-db vendor support
一个配置了"_databaseId"变量的 databaseIdProvider 对于动态代码来说是可用的,这样就可以根据不同的数据库厂商构建特定的语句。比如下面的例子:
<insert id="insert"> <selectKey keyProperty="id" resultType="int" order="BEFORE"> <if test="_databaseId == 'oracle'"> select seq_users.nextval from dual if> <if test="_databaseId == 'db2'"> select nextval for seq_users from sysibm.sysdummy1" if> selectKey> insert into users values (#{id}, #{name}) insert>
动态 SQL 中可插拔的脚本语言
MyBatis 从 3.2 开始支持可插拔的脚本语言,因此你可以在插入一种语言的驱动(language driver)之后来写基于这种语言的动态 SQL 查询。
可以通过实现下面接口的方式来插入一种语言:
public interface LanguageDriver {
ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
SqlSource createSqlSource(Configuration configuration, XNode script, Class parameterType);
SqlSource createSqlSource(Configuration configuration, String script, Class parameterType);
}
一旦有了自定义的语言驱动,你就可以在 mybatis-config.xml 文件中将它设置为默认语言:
<typeAliases> <typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/> typeAliases> <settings> <setting name="defaultScriptingLanguage" value="myLanguage"/> settings>
除了设置默认语言,你也可以针对特殊的语句指定特定语言,这可以通过如下的 lang
属性来完成:
<select id="selectBlog" lang="myLanguage"> SELECT * FROM BLOG select>
或者在你正在使用的映射中加上注解 @Lang
来完成:
public interface Mapper {
@Lang(MyLanguageDriver.class)
@Select("SELECT * FROM BLOG")
List selectBlog();
}
注意 可以将 Apache Velocity 作为动态语言来使用,更多细节请参考 MyBatis-Velocity 项目。
你前面看到的所有 xml 标签都是默认 MyBatis 语言提供的,它是由别名为 xml
语言驱动器 org.apache.ibatis.scripting.xmltags.XmlLanguageDriver
驱动的。