Mybatis概述
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁 移到了google code,并且改名为MyBatis。是一个基于Java的持久层框架
无论是Mybatis、Hibernate都是ORM的一种实现框架,都是对JDBC的一种封装!
Mybatis工作流程
-
通过Reader对象读取Mybatis映射文件
-
通过SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
-
获取当前线程的SQLSession
-
事务默认开启
-
通过SQLSession读取映射文件中的操作编号,从而读取SQL语句
-
提交事务
-
关闭资源
动态查询
使用if/else statement判断是否添加对应字段
<!--多条件查询【动态SQL】-->
<!--会自动组合成一个正常的WHERE字句-->
<!--name值会从map中寻找-->
<select id="findByCondition" resultMap="studentMap" parameterType="map">
select * from students
<where>
<if test="name!=null">
and name= #{name}
</if>
<if test="sal!=null">
and sal < #{sal}
</if>
</where>
</select>
<!--动态更新-->
<!--不要忘了逗号-->
<update id="updateByConditions" parameterType="map">
update students
<set>
<if test="name!=null">
name = #{name},
</if>
<if test="sal!=null">
sal = #{sal},
</if> </set>
where id = #{id}
</update>
动态插入
<!--SQL片段默认是不帮我们自动生成合适的SQL,因此需要我们自己手动除去逗号-->
<sql id="key">
<trim suffixOverrides=",">
<if test="id!=null">
id,
</if>
<if test="id!=null">
name,
</if>
<if test="id!=null">
sal,
</if>
</trim>
</sql>
<sql id="value">
<trim suffixOverrides=",">
<if test="id!=null">
#{id},
</if>
<if test="id!=null">
#{name},
</if>
<if test="id!=null">
#{sal},
</if>
</trim>
</sql>
<!--动态插入-->
<insert id="insertByConditions" parameterType="zhongfucheng.Student">
insert into students (<include refid="key"/>) values (<include refid="value"/>)
</insert>
MyBatis缓存
-
mybatis一级缓存是一个SqlSession级别,sqlsession只能访问自己的一级缓存的数据
-
二级缓存是跨sqlSession,是mapper级别的缓存,对于mapper级别的缓存不同的sqlsession是 可以共享的。
一级缓存
Mybatis的一级缓存原理:
第一次发出一个查询sql,sql查询结果写入sqlsession的一级缓存中,缓存使用的数据结构是一个 map<key,value>
-
key:hashcode+sql+sql输入参数+输出参数(sql的唯一标识)
-
value:用户信息
同一个sqlsession再次发出相同的sql,就从缓存中取不走数据库。如果两次中间出现commit操作(修 改、添加、删除),本sqlsession中的一级缓存区域全部清空,下次再去缓存中查询不到所以要从数据 库查询,从数据库查询到再写入缓存。
二级缓存
二级缓存的范围是mapper级别(mapper同一个命名空间),mapper以命名空间为单位创建缓存数据结构,结构是map<key,value>。
每次查询先看是否开启了二级缓存,如果开启则首先在二级缓存中查找,如果missed,再进入一级缓存查找,再次miss,开始数据库查询。
Mybatis解决JDBC编程的问题
- 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
- 解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。
- Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
- 解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
- 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
- 解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
- 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
- 解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
面试题
#{}和${}的区别是什么?
在Mybatis中,有两种占位符
#{} 解析传递进来的参数数据 ${}对传递进来的参数原样拼接在SQL中
#{} 是预编译处理,${}是字符串替换。 使用#{}可以有效的防止SQL注入,提高系统安全性。
${} 可以用于传入一些固定的字符串,例如表名或字段名:
from ${TABLE_NAME} ORDER BY ${COL_NAME}
#{}则会进行预编译,防止通过sql注入的方式进行攻击
当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
- 第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致
<select id=”selectorder” parametertype=”int” resultetype=”me.gacl.domain.order”> select order_id id, order_no orderno ,order_price price form orders where order_id=#{id}; </select>
- 第2种: 通过
来映射字段名和实体类属性名的一一对应的关系 ```sql
<resultMap type=”me.gacl.domain.order” id=”orderresultmap”> <!–用id属性来映射主键字段–> <id property=”id” column=”order_id”> <!–用result属性来映射非主键字段,property为实体类属性名column为数据表中的属性– <result property = “orderno” column =”order_no”/> <result property=”price” column=”order_price” /> </reslutMap>
常用第二种
#### 如何获取自动生成的(主)键值?
如果我们一般插入数据的话,如果我们想要知道刚刚插入的数据的主键是多少,我们可以通过以下的方式来获取
> 需求:user对象插入到数据库后,新记录的主键要通过user对象返回,通过user获取主键值。
* 解决思路:通过LAST_INSERT_ID()获取刚插入记录的自增主键值,在insert语句执行后,执行select LAST_INSERT_ID()就可以获取自增主键。
```mysql
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id" order="AFTER" resultType="int">
select LAST_INSERT_ID()
</selectKey>
INSERT INTO USER(username,birthday,sex,address)
VALUES(#{username},#{birthday},#{sex},#{address})
</insert>
在mapper中如何传递多个参数?
第一种:使用占位符的思想
- 在映射文件中使用#{0},#{1}代表传递进来的第几个参数
-
使用@param注解:来命名参数
- #{0},#{1} 方式 ```sql //对应的xml,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数,更多参数一致往后加即可。
<select id=”selectUser”resultMap=”BaseResultMap”> select * fromuser_user_t whereuser_name = #{0} anduser_area=#{1} </select>
* @param注解方式
```java
public interface usermapper {
user selectuser(@param(“username”) string username,
@param(“hashedpassword”) string hashedpassword);
}
<select id=”selectuser” resulttype=”user”>
select id, username, hashedpassword from some_table
where username = #{username}
and hashedpassword = #{hashedpassword}
</select
Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不?
-
Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。
-
Mybatis提供了9种动态sql标签:trim where set foreach if choose when otherwise bind。 - 其执行原理为,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能。
Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?
如果配置了namespace那么当然是可以重复的,因为我们的Statement实际上就是namespace+id 如果没有配置namespace的话,那么相同的id就会导致覆盖了。
通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么? Dao接口里的方法,参数不同时,方法能重载吗?
Dao接口,就是人们常说的Mapper接口
接口的全名,就是映射文件中的namespace的值
接口的方法名,就是映射文件中MappedStatement的id值
接口方法内的参数,就是传递给sql的参数。
Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement
com.mybatis3.mappers.StudentDao.findStudentById
可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面id = findStudentById的 MappedStatement。在Mybatis中,每一个<select>、<insert>、<update>、<delete>标签,都会被解析为一个MappedStatement对象。
Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。
接口绑定有几种实现方式,分别是怎么实现的?
接口绑定有两种实现方式:
-
一种是通过注解绑定,就是在接口的方法上面加上@Select@Update等注解里面包含Sql语句来绑定
-
另外一种就是通过xml里面写SQL来绑定,在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名.