Mybatis 学习笔记
resultSets在学习Mybatis的过程中,应该或多或少的都会遇到一些问题,先整理如下。
Mybatis是什么,为什么使用?
答:MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs映射成数据库中的记录。使用的原因因为流行、简单、易维护优化、提高开发效率、开源、性能不错。
什么是N+1问题,如何解决?
使用Mapper XML配置的时候,查询一个list的结果集,需要执行N+1次SQL进行查询,如果数据量大的话,会带来很大的性能问题。
举例:
<resultMap id="blogResult" type="Blog"> <association property="author" column="author_id" javaType="Author" select="selectAuthor"/> </resultMap> <select id="selectBlog" resultMap="blogResult"> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id="selectAuthor" resultType="Author"> SELECT * FROM AUTHOR WHERE ID = #{id} </select>
Blog的结果集中需要执行selectAuthor来获取Author的属性。假设BLOG表有10条记录,每条记录对应AUTHOR表也有10条记录,那么执行blogResult查询,需要进行了1+10次查询。简而言之,原因是:
- 你执行一个SQL语句来获取结果(“+1”)的列表;
- 对于返回的每条记录,你执行了一个查询语句来为每个加载细节(在“N”);
解决办法:
- SQL使用连接查询;
- Mybatis在3.2.3以后,可以使用Association关联多个ResultSet;
例如:
定义存储过程如下
SELECT * FROM BLOG WHERE ID = #{id} SELECT * FROM AUTHOR WHERE ID = #{id}Mapper XML的配置中增加resultSets,如下
<select id="selectBlog" resultSets="blogs,authors" resultMap="blogResult"> {call getBlogsAndAuthors(#{id,jdbcType=INTEGER,mode=IN})} </select>配置resultMap的地方可以引用resultSets中的内容,如下
<resultMap id="blogResult" type="Blog"> <id property="id" column="id" /> <result property="title" column="title"/> <association property="author" javaType="Author" resultSet="authors" column="author_id" foreignColumn="id"> <id property="id" column="id"/> <result property="username" column="username"/> <result property="password" column="password"/> <result property="email" column="email"/> <result property="bio" column="bio"/> </association> </resultMap>
如何处理“is-a”、“has-a”关系?
extends处理is-a;
association处理has-a;
collection处理has-many;
在Nested Select解决has-many的情况下,collection标签中,各个字段的理解?
<collection property="Java属性名" ofType="另一Java类名" javaType="ArrayList" column="关联主键ID(用于嵌套查询SQL语句传入参数,多个用逗号分开,必须和数据库列对应)" select="另一个select映射SQL的ID"/>
<select parameterType="int" resultType="另一Java类名" id="另一个select映射SQL的ID">
例如:
<resultMap type="org.demo.mybatis.po.Deptartment" id="deptResultMap"> <id column="dept_id" property="deptId" javaType="int" /> <result column="dept_name" property="deptName" javaType="string" /> <result column="dept_address" property="deptAddress" javaType="string" /> <!-- column是必填的,同时必须是数据库的列,关联SQL中的入参--> <collection property="employees" column="dept_id" ofType="org.demo.mybatis.po.Employee" select="selectEmployeeForDept" /> </resultMap> <!-- 嵌套的SQL语句将被多次执行 --> <select id="selectEmployeeForDept" resultType="org.demo.mybatis.po.Employee"> select e_id, dept_id, e_name from employee <!--any的值可以随意填写,但是必须写,同时不能使用${}取代,最好使用和colum对应的property,这里使用deptId提高可读性 --> where dept_id = #{any} </select>
#{..} vs ${...}?
#{…}是一个参数标记,而${…}只是简单的字符串替换。一般而言,为避免SQL注入攻击,传递参数应使用#{…}方式,因为这样MyBatis会处理好特殊字符转义的问题;但在SQL语句的某些地方,又不能使用#{…}方式。上述文档举出的例子是不能用这种方式指定表名,而根据我们的经验,在order by子句中也不能用这种方式。从中我们可以总结出:对于诸如表名、字段名(如order by子句后的排序字段)这些表本身或其字段的名字,和SQL关键字(如order by子句后的asc关键字),是不能使用#{…}方式的,而只能使用字符串替换的${…}方式。