Mybatis

1.入门

1.创建工程,引入坐标

<dependencies> <dependency> <groupId>org.mybatis</groupId>          <ar tifactId>mybatis</artifactId> <version>3.5.0</version> </dependency> <dependency> <groupId>mysql</groupId>  <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency><groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
</dependencies>

2.配置mybatis主配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration>
<!--指定默认的环境-->
<environments default="mysql"><!--环境配置,可以存在多个--> <environment id="mysql"> <!--使用了JDBC的事务管理--> <transactionManager type="JDBC"></transactionManager> <!--先配置为POOLED,代表以池的形式管理连接--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/es" /> <property name="username" value="root"/> <property name="password" value="adminadmin"/>
</dataSource>
</environment>
</environments>
</configuration>

3.xml映射文件

文件位置:\resources\com\itheima\mapper\UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper"> <insert id="save" parameterType="com.itheima.domain.User"> INSERT INTO USER(NAME,PASSWORD,email,phoneNumber,birthday) VALUE(#{name},#{password},#{email},#{phoneNumber},#{birthday}); </insert>
</mapper>

4.将Mapp文件加入到主配置文件中

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="mysql"><!--省略数据库配置--> </environments> <!--引入映射文件--> <mappers> <mapper resource="com/itheima/mapper/UserMapper.xml" /> </mappers>
</configuration>

5.测试

//保存
@Test
public void testSave() throws Exception {
User user = new User(); user.setName("传智播客");
user.setPassword("admin"); user.setBirthday(new Date());
user.setEmail("admin@itcast.cn"); user.setPhoneNumber("110");//1 加载Mybatis的主配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); //2 创建sqlSessionFactory工厂SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //3 创建sqlSessionSqlSession sqlSession = sqlSessionFactory.openSession(); //4.执行操作 sqlSession.insert("userMapper.save",user); //5 提交事务 sqlSession.commit(); //6 释放资源 sqlSession.close();}

小结:

基于接口代理方式的开发只需要程序员编写 Mapper 接口,Mybatis 框架会为我们动态生成实现类的对象。
这种开发方式要求我们遵循一定的规范:

  • Mapper接口的类路径与Mapper.xml 文件中的namespace相同
  • Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
  • Mapper接口方法的输入参数类型和Mapper.xml中定义的每个sql的parameterType的类型相同
  • Mapper接口方法的输出参数类型和Mapper.xml中定义的每个sql的resultType的类型相同

2.Mybatis基于接口代理方式的内部执行原理

我们的Dao层现在只有一个接口,而接口是不实际干活的,那么是谁在做save的实际工作呢?
下面通过追踪源码看一下:
1、通过追踪源码我们会发现,我们使用的mapper实际上是一个代理对象,是由MapperProxy代理产生的。

2、追踪MapperProxy的invoke方法会发现,其最终调用了mapperMethod.execute(sqlSession, args)

3、进入execute方法会发现,最终工作的还是sqlSession。

3.Mybatis基本原理

4.Mybatis的API

Resources加载mybatis的配置文件。SqlSessionFactoryBuilder利用Resources指定的资源,将配置信息加载到内存中,还会加载mybatis配置文件中指定的所有映射配置信息,    并用特定的对象实例进行保存,从而创建SqlSessionFactory对象。SqlSessionFactory这是一个工厂对象,对于这种创建和销毁都非常耗费资源的重量级对象,一个项目中只需要存在一个即可。也就是说,它的生命周期跟项目的生命周期是一致的(项目不死,我不销毁)它的任务是创建SqlSession。SqlSession这是Mybatis的一个核心对象。我们基于这个对象可以实现对数据的CRUD操作。对于这个对象应做到每个线程独有,每次用时打开,用完关闭。

抽取工具类

  1. 抽取基本代码工具类
public class MybatisUtil {private static SqlSessionFactory sqlSessionFactory = null;static {try {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (Exception e) {throw new RuntimeException("加载配置文件失败");}}public static SqlSession openSession() {return sqlSessionFactory.openSession();}
}

2 抽取测试基类

public class BaseMapperUtil {protected SqlSession sqlSession = null;@Beforepublic void init() {sqlSession = MybatisUtil.openSession();}@Afterpublic void destory() {sqlSession.commit();sqlSession.close();}
}

5.基于接口代理实现CRUD

//Mapper接口public interface UserMapper {//保存void save(User user);//根据UID查询User findByUid(Integer i);//根据UID更新void update(User user);//根据ID删除void deleteByUid(Integer uid);}
//Mapper.xml
<mapper namespace="com.itheima.mapper.UserMapper"><insert id="save" parameterType="com.itheima.domain.User">insert intouser(name,password,email,phoneNumber,birthday)value(#{name},#{password},#{email},#{phoneNumber},#{birthday})</insert><select id="findByUid" parameterType="int" resultType="com.itheima.domain.User">select * from user where uid = #{id}</select><update id="update" parameterType="com.itheima.domain.User">update user set password = #{password} where uid = #{uid}</update><update id="deleteByUid" parameterType="int">delete from user where uid = #{uid}</update>
</mapper>
    //测试类public class CRUDTest extends BaseMapperUtil {//保存@Testpublic void testSave() throws Exception {User user = new User();user.setName("传智播客3");user.setPassword("admin");user.setBirthday(new Date());user.setEmail("admin@itcast.cn");user.setPhoneNumber("110");UserMapper userMapper = sqlSession.getMapper(UserMapper.class);userMapper.save(user);}//根据UID查询@Testpublic void testFindByUid() {UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user = userMapper.findByUid(1);System.out.println(user);}//根据UID修改@Testpublic void testUpdate() {User user = new User();user.setUid(1);user.setPassword("adminadmin");UserMapper userMapper = sqlSession.getMapper(UserMapper.class);userMapper.update(user);}//根据ID删除@Testpublic void testDeleteByUid() {UserMapper userMapper = sqlSession.getMapper(UserMapper.class);userMapper.deleteByUid(2);}}

5.1查询

<!--当仅仅有一个查询条件的时候parameterType="" 可以省略#{} 占位符可以随便写,只要不为空可以【因为参数只要一个,所以名字就不重要了,但是推荐跟名称一致】
-->
<select id="findByEmail" resultType="com.itheima.domain.User">select * from user where email = #{emailFFFFFFFFFF}
</select>
多个条件
第一种方式:不使用注解,这时候xml中的SQL语句中只能使用arg 或者param 获取参数//根据多个条件查询List<User> findByEmailAndphoneNumber(String email, String phoneNumber);<select id="findByEmailAndphoneNumber" resultType="com.itheima.domain.User"><!--select * from user where email = #{arg0} and phoneNumber = #{arg1}-->select * from user where email = #{param1} and phoneNumber = #{param2}</select>
第二种方式:使用注解,引入@Param()注解,这时候就可以是用属性名称获取参数了//根据多个条件查询List<User> findByEmailAndphoneNumber(@Param("email") String email, @Param("phoneNumber")String phoneNumber);<select id="findByEmailAndphoneNumber" resultType="com.itheima.domain.User">select * from user where email = #{email} and phoneNumber = #{phoneNumber}</select>
第三种方式(推荐使用):使用pojo对象传递参数//使用对象封装查询条件List<User> findByEmailAndphoneNumber2(User user);<select id="findByEmailAndphoneNumber2" resultType="com.itheima.domain.User">select * from user where email = #{email} and phoneNumber = #{phoneNumber}</select>

5.2模糊查询

<!--方式一: 传入参数的时候,直接传入setEmail(%传智%)-->
<select id="findByName1" resultType="com.itheima.domain.User">select * from user where name like #{email}
</select><!--方式二: 在SQL语句中直接控制模糊模式'%'  传入参数的时候,直接传入setEmail(传智)-->
<select id="findByName2" resultType="com.itheima.domain.User">select * from user where name like "%"#{email}"%"
</select><!--方式三: 使用${}赋值,注意$不表示占位符,而是表示字符串拼接,而且要求接口方法中要使用@Param声明-->
<select id="findByName3" resultType="com.itheima.domain.User">select * from user where name like "%${email}%"
</select><!--方式四: 使用MySQL的函数,  推荐!!!-->
<select id="findByName4" resultType="com.itheima.domain.User">select * from user where name like concat('%',#{email},'%');
</select>

面试直达:请说出Mybatis中#{}和${}的区别

#{}表示占位符,${}表示字符串拼接
${}可能会引起SQL注入问题,#{}不会
两者都可以接受简单类型的值和pojo类型的属性值,但是${}接收简单类型数据只能使用${value},#{} 可以是随意值

6.返回主键

6.1 useGeneratedKeys

使用useGeneratedKeys="true" 声明返回主键
使用keyProperty指定将主键映射到pojo的哪个属性
<insert id="saveReturnId" useGeneratedKeys="true" keyProperty="uid">insert intouser(name,password,email,phoneNumber,birthday)value(#{name},#{password},#{email},#{phoneNumber},#{birthday})
</insert>

6.2 selectKey

<insert id="saveReturnId"><!--keyColumn     指定主键列名称keyProperty    指定主键封装到实体的哪个属性resultType      指定主键类型order              指定在数据插入数据库前(后),执行此语句--><selectKey keyProperty="uid" keyColumn="uid" resultType="int" order="AFTER">select last_insert_id()</selectKey>insert intouser(name,password,email,phoneNumber,birthday)value(#{name},#{password},#{email},#{phoneNumber},#{birthday})
</insert>

7.动态SQL

根据程序运行时传入参数的不同而产生SQL语句结构不同,就是动态SQL。
findByCondtion(User user):根据传入的user对象进行查询,将不为空的属性作为查询条件

用户输入的是: 用户名和密码  select * from user where name= #{name} and password = #{password}
用户输入的是: 用户名和邮箱select * from user where name= #{name} and email = #{email}
用户输入的是: 用户名和和密码和邮箱select * from user where name= #{name} and password = #{password} and email = #{email}

动态SQL是Mybatis的强大特性之一,Mybatis3之后,需要了解的动态SQL标签仅仅只有下面几个:

  • if choose (when, otherwise) 用于条件判断
  • trim (where, set) 用于去除分隔符
  • foreach 用于循环遍历

7.1 条件判断

7.1.1 if

if 用来做条件判断,接受一个ognl表达式,返回一个boolean值。< if test="ognl表达式">sql片段< /if>。

第一种情况: if 在where语句中的使用,实现动态条件判断

需求:根据传入的user对象的进行查询,将不为空的属性作为查询条件

//实现根据传入的值是否为空实现where语句拼接
//注意语句中使用了  1=1  来避免语句出现错误
<select id="findByCondition" resultType="com.itheima.domain.User">select * from user where 1=1<if test="name != null and name != ''">and name like concat('%',#{name},'%')</if><if test="email != null and email != ''">and email = #{email}</if>
</select>

第二种情况: if 在set语句中的使用,实现动态条件更新

需求:根据传入的user对象的进行更新,将不为空的属性更新到数据库

//实现根据传入的值是否为空实现set语句拼接
//注意语句中使用了 uid = #{uid} 来避免语句出现错误
<update id="update">update user set<if test="name != null and name != ''">name = #{name},</if><if test="email != null and email != ''">email = #{email},</if>uid = #{uid} where uid = #{uid}
</update>

第三种情况: if 在insert语句中的使用,实现动态条件插入

需求:根据传入的user对象的进行插入,将不为空的属性插入到数据库

//实现根据传入的值是否为空实现insert语句拼接
<insert id="insert">insert intouser(name,email,phoneNumber,birthday<if test="password != null and password != ''">,password</if>)value(#{name},#{email},#{phoneNumber},#{birthday}<if test="password != null and password != ''">,#{password}</if>)
</insert>

7.1.2 choose、when、otherwise

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的if elseif 语句。

<select id="select" resultType="com.itheima.domain.User">select * from user where 1=1<choose><when test="name != null and name != ''">and name = #{name}</when><when test="email != null and email != ''">and email = #{email}</when><when test="phoneNumber != null and phoneNumber != ''">and phoneNumber = #{phoneNumber}</when><otherwise>and 1 = 2</otherwise></choose>
</select>

下面是java和mybatis中的判断语句语法的对比

7.2 格式优化

这三个关键字是用来对Mybatis中的SQL语句书写进行优化的。主要是用来去除多余的符号和关键字。

7.2.1 where

where标签的作用:

  • 如果where包含的标签中有返回true的语句,Mybatis会在语句插入一个‘where’。

  • 如果标签返回的内容是以AND 或OR 开头的,Mybatis会将其剔除掉。

    select * from user and name like concat('%',#{name},'%') and email = #{email}

7.2.2 set

set标签的作用:去掉set语句最后的逗号(,)

注意:set标签中必须保证至少有一个语句

<update id="update2">update user<set><if test="name != null and name != ''">name = #{name},</if><if test="email != null and email != ''">email = #{email},</if>uid = #{uid}</set>where uid = #{uid}
</update>

7.2.3 trim(了解)

trim标签是一个格式化标签,可以完成set或者是where的功能。

//使用trim替代where
//prefix 代表必要的时候在trim标签的最前面加上一个where
//prefixOverrides  代表必要的时候干掉trim生成语句中的第一个and或者or
<select id="find3" resultType="com.itheima.domain.User">select * from user<trim prefix="where" prefixOverrides="and | or"><if test="name != null and name != ''">and name like concat('%',#{name},'%')</if><if test="email != null and email != ''">and email = #{email}</if></trim>
</select>//使用trim替代set
//prefix 代表必要的时候在trim标签的最前面加上一个set
//suffixOverrides  代表必要的时候干掉trim生成语句中的最后一个,
<update id="update3">update user<trim prefix="set" suffixOverrides=","><if test="name != null and name != ''">name = #{name},</if><if test="email != null and email != ''">email = #{email},</if></trim>where uid = #{uid}
</update>

8 循环遍历

foreach主要是用来做数据的循环遍历。

典型的应用场景是SQL中的in语法中,select * from user where uid in (1,2,3) 在这样的语句中,传入的参数部分必须依靠 foreach遍历才能实现。我们传入的参数,一般有下面几个形式:

  • 集合(List Set)

  • 数组

  • pojo

    foreach的选项
    collection:数据源【重点关注这一项,它的值会根据出入的参数类型不同而不同】
    open:开始遍历之前的拼接字符串
    close:结束遍历之后的拼接字符串
    separator:每次遍历之间的分隔符
    item:每次遍历出的数据
    index:遍历的次数,从0开始

8.1 集合

<select id="find4" resultType="com.itheima.domain.User"><!--collection:数据源【当参数为一个集合时,此值为collection,如果参数是List,此值也可以使用list代替】此处的值也可以通过@Param("自定义")的方式在Dao层方法上自己指定。--><!--select * from user where uid in (1,2,3)-->select * from user where uid in<foreach collection="collection" open="(" close=")" separator="," item="item">#{item}</foreach>
</select>List<User> find4(List<Integer> uids);//collection="collection"
//或者
List<User> find4(@Param("uids") List<Integer> uids);//collection="uids"

8.2数组

<select id="find5" resultType="com.itheima.domain.User"><!--collection:数据源【当参数为一个数组时,此值为array】此处的值也可以通过@Param("自定义")的方式在Dao层方法上自己指定--><!--select * from user where uid in (1,2,3)-->select * from user where uid in<foreach collection="array" open="(" close=")" separator="," item="item">#{item}</foreach>
</select>List<User> find5(Integer[] uids);//collection="array"
//或者
List<User> find5(@Param("uids") Integer[] uids);//collection="uids"

8.3 pojo

<select id="find6" resultType="com.itheima.domain.User">select * from user where uid in<foreach collection="uids" open="(" close=")" separator="," item="item" index="index">#{item}</foreach>
</select>List<User> find6(User user);//在user中封装了一个List<Integer> uids属性

总结:foreach的使用主要是用来遍历数据源。关键点在于数据源的指定:

  • 如果是集合,使用collection
  • 如果是数组,使用array
  • 如果是pojo,使用pojo中的属性名称

小结

动态SQL的最终目的其实就是通过Mybatis提供的标签实现sql语句的动态拼装。

为了保证拼接准确,我们最好首先要写原生的sql语句出来,然后再通过mybatis动态sql对照着改。

9.多表关系

9.1 多表关系分析

数据库设计的三种表间关系分别为: 一对一 、一对多(多对一)、 多对多关系。

常见示例:一对一:人和身份证、QQ号码和QQ详情一对多:用户和登录日志、部门和员工多对多:用户和角色、老师和学生

明确:我们今天只涉及实际开发中常用的关联关系,一对多和多对多。而一对一的情况,在实际开发中几乎不用。

9.2 多对一

多对一的映射方式一般有下面几种:

  • 采用外键+别名的形式进行映射
  • 采用resultMap形式进行映射
  • 采用resultMap + association 形式进行映射 【推荐】

下面分别来看。

9.2.1 采用外键+别名的形式进行映射

<select id="findAll" resultType="com.itheima.domain.LoginInfo"><!--select * from user u,login_info l where u.uid = l.uid;-->selectl.*,u.uid "user.uid",u.name "user.name",u.password "user.password",u.email "user.email",u.phoneNumber "user.phoneNumber",u.birthday "user.birthday"from user u,login_info l where u.uid = l.uid
</select>

9.2.2 采用resultMap形式进行映射

//注意此方式还支持继承
<resultMap id="loginInfoMap" type="com.itheima.domain.LoginInfo"><id property="lid" column="lid" /><result property="ip" column="ip" /><result property="loginTime" column="loginTime" /><result property="user.uid" column="uid" /><result property="user.name" column="name" /><result property="user.password" column="password" /><result property="user.email" column="email" /><result property="user.phoneNumber" column="phoneNumber" /><result property="user.birthday" column="birthday" />
</resultMap><select id="findAll2" resultMap="loginInfoMap">select * from user u,login_info l where u.uid = l.uid;
</select>

9.2.3 采用resultMap + association 形式进行映射

<resultMap id="loginInfoMap3" type="com.itheima.domain.LoginInfo"><id property="lid" column="lid" /><result property="ip" column="ip" /><result property="loginTime" column="loginTime" />//property代表属性名称  javaType对应真实的类型 <association property="user" javaType="com.itheima.domain.User"><result property="uid" column="uid" /><result property="name" column="name" /><result property="password" column="password" /><result property="email" column="email" /><result property="phoneNumber" column="phoneNumber" /><result property="birthday" column="birthday" /></association>
</resultMap><select id="findAll3" resultMap="loginInfoMap3">select * from user u,login_info l where u.uid = l.uid;
</select>

9.3 一对多

一对多的映射方式一般采用resultMap + collection形式进行实现。

<resultMap id="userMap2" extends="userMap" type="com.itheima.domain.User"><id property="uid" column="uid"/><result property="name" column="name"/><result property="password" column="pass"/><result property="email" column="email"/><result property="phoneNumber" column="phoneNumber"/><result property="birthday" column="birthday"/>//loginInfos配置属性  ofType配置指定的类<collection property="loginInfos" ofType="com.itheima.domain.LoginInfo"><id property="lid" column="lid" /><result property="ip" column="ip" /><result property="loginTime" column="loginTime" /></collection>
</resultMap><select id="findAll" resultMap="userMap2">select * from user u left join login_info l on u.uid = l.uid;
</select>

9.4 多对多

多对多的配置跟一对多很相似,难度在于SQL语句的编写。

<resultMap id="userMap3" type="com.itheima.domain.User"><id property="uid" column="uid"/><result property="name" column="name"/><result property="password" column="pass"/><result property="email" column="email"/><result property="phoneNumber" column="phoneNumber"/><result property="birthday" column="birthday"/><collection property="roles" ofType="com.itheima.domain.Role"><id property="rid" column="rid" /><result property="name" column="name" /><result property="description" column="description" /></collection>
</resultMap><select id="findAll3" resultMap="userMap3">SELECT * FROM USER u LEFT JOIN user_role ur ON u.uid = ur.uid JOIN role r ON ur.`rid` = r.`rid`
</select>

10.嵌套查询

10.1 什么是嵌套查询?

嵌套查询就是将原来多表查询中的联合查询语句拆成单个表的查询,再使用mybatis的语法嵌套在一起。

举个例子:需求:  查询日志的同时查询到用户信息联合查询: select * from login_info l,user u where l.uid = u.uid 改成嵌套查询:   1) 先查询到日志信息(这里面包含了一个跟用户相关的外键uid) select * from login_info    得到uid2)根据上一拿到的uid查询相关的用户信息select * from user where uid = #{第一步得到的uid}3)使用Mybatis提供的关键字,将上面两步嵌套起来。。。。

10.2 多对一的嵌套查询

目标: 查询日志的同时查询到用户信息

未使用嵌套之前:

<resultMap id="baseMap" type="com.itheima.domain.LoginInfo"><id property="lid" column="lid" /><result property="ip" column="ip" /><result property="loginTime" column="loginTime" />
</resultMap><resultMap id="loginInfoMap1" type="com.itheima.domain.LoginInfo" extends="baseMap"><association property="user" javaType="com.itheima.domain.User"><id property="uid" column="uid" /><result property="name" column="name" /><result property="password" column="password" /><result property="email" column="email" /><result property="phoneNumber" column="phoneNumber" /><result property="birthday" column="birthday" /></association>
</resultMap><select id="findAll1" resultMap="loginInfoMap1">select * from login_info l,user u where l.uid = u.uid
</select>

改成嵌套查询:

  1. 对日志表进行单表查询
       <!-- LoginInfoMapper.xml --><resultMap id="loginInfoMap2" type="com.itheima.domain.LoginInfo" extends="baseMap"><association><!--此处暂时留空--></association></resultMap><select id="findAll2" resultMap="loginInfoMap2">select * from login_info</select>
  1. 在用户表的Mapper文件中:使用uid查询用户
       <!--UserMapper.xml--><select id="findByUid" resultType="com.itheima.domain.User">select * from user where uid = #{uid}</select>
  1. 使用Mybatis提供的关键字,将上面两步嵌套起来
       <!--在LoginInfoMapper.xml中进行嵌套关联--><resultMap id="loginInfoMap2" type="com.itheima.domain.LoginInfo" extends="baseMap"><!--select   指定联合查询的方法(根据uid查询User)column   要传递的参数属性字段(将此字段的值作为参数传给select方法)--><association property="user" column="uid"select="com.itheima.mapper.UserMapper.findByUid"></association></resultMap>

10.3 一对多的嵌套查询

目标: 查询用户的同时查询到关联的日志信息未使用嵌套之前:

<resultMap id="baseMap" type="com.itheima.domain.User"><id property="uid" column="uid"/><result property="name" column="name"/><result property="password" column="password"/><result property="email" column="email"/><result property="phoneNumber" column="phoneNumber"/><result property="birthday" column="birthday"/>
</resultMap><resultMap id="userMap1" type="com.itheima.domain.User" extends="baseMap"><collection property="loginInfos" ofType="com.itheima.domain.LoginInfo"><id property="lid" column="lid"/><result property="ip" column="ip"/><result property="loginTime" column="loginTime"/></collection>
</resultMap><select id="findAll1" resultMap="userMap1">select * from user u left join login_info l on u.uid = l.uid;
</select>

改成嵌套查询:

  1. 对用户表进行单表查询
       <!--UserMapper.xml--><resultMap id="userMap2" type="com.itheima.domain.User" extends="baseMap"><collection><!--此处暂时留空--></collection></resultMap><select id="findAll2" resultMap="userMap2">select * from user</select>
  1. 在日志表的Mapper文件中:使用uid查询日志
       <!--LoginInfoMapper.xml--><select id="findByUid" resultType="com.itheima.domain.LoginInfo">select * from login_info where uid = #{uid}</select>
  1. 使用Mybatis提供的关键字,将上面两步嵌套起来
       <!--UserMapper.xml--><resultMap id="userMap2" type="com.itheima.domain.User" extends="baseMap"><collection property="loginInfos" column="uid" select="com.itheima.mapper.LoginInfoMapper.findByUid"></collection></resultMap>

11.加载策略

11.1 什么是加载策略

当多个模型之间存在联系时,在加载一个模型的数据的时候,是否随之加载与其相关模型数据的策略,我们称之为加载策略。

在Mybatis中,常用的加载策略有立即加载和延迟加载(懒加载)两种。

举个例子:现在有用户和用户登录日志两个模型,当加载1个用户的时候,是否需要立即加载与其相关的登录日志的信息呢?

如果需要,我们把这种加载策略成为立即加载,

如果不需要,等到真正要使用日志信息的时候再加载,我们把这种加载策略成为立即加载(懒加载)。

11.2 Mybatis加载策略

Mybatis的默认加载策略是立即加载,也就是在加载一个对象的时候会立即联合加载到其关联的对象。

当然,Mybatis也提供了修改加载策略的方法。

  • 在Mybatis 的配置文件中可以使用setting修改全局的加载策略。
  • 在< association >和< collection >元素中都有一个fetchType属性,该值会覆盖掉全局参数的配置。
    • fetchType=“lazy” 懒加载策略

    • fetchType=“eager” 立即加载策略

      <association fetchType="eager|lazy"></association>
      <collection fetchType="eager|lazy"></collection>
      

注意:

  • 在配置了延迟加载策略后,即使没有调用关联对象的任何方法,当你调用当前对象的equals、clone、hashCode、toString方法时也会触发关联对象的查询。
  • 在配置文件中可以使用lazyLoadTriggerMethods配置项覆盖掉mybatis的默认行为。
    <setting name="lazyLoadTriggerMethods" value="getUid,toString"/>

12 缓存机制

缓存是用来提高查询效率的,所有的持久层框架基本上都有缓存机制。

Mybatis有两级缓存,一级缓存是SqlSession级别的,二级缓存是SqlSessionFactory级别的。

12.1 一级缓存

一级缓存是SqlSession级别的缓存,是默认开启且无法关闭的。

public void testFindByUid(){UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user1 = mapper.findByUid(1);//发送SQLSystem.out.println("==============");User user2 = mapper.findByUid(1);//未发送SQLSystem.out.println(user1 == user2);//true
}
  • 同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。
  • 当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。
  • 不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

注意:

  • 调用SqlSession的clearCache(),或者执行C(增加)U(更新)D(删除)操作,都会清空缓存。
  • 查询语句中这样的配置< select flushCache=“true”/>也会清除缓存。

12.2 二级缓存

二级缓存是sqlSessionFactory级别的缓存,是默认开启,但是可以关闭的。

二级缓存是多个SqlSession共享的,不同的sqlSession两次执行相同namespace下的sql语句,第一次执行完毕会将数据库中查询的数据写到二级缓存(内存),第二次会从二级缓存中获取数据,而不再从数据库查询,从而提高查询效率。

二级缓存的设置:

<!--主配置文件中配置cacheEnable为true,这也是默认配置,可以不加-->
<setting name="cacheEnabled" value="true|false"/><!--在Mapper.xml文件中加入cache标签 -->
<cache />

验证:

public void testFindByUid2(){SqlSession sqlSession1 = MybatisUtil.openSession();User user1 = sqlSession1.getMapper(UserMapper.class).findByUid(1);//发送SQLsqlSession1.close();System.out.println("===================");SqlSession sqlSession2 = MybatisUtil.openSession();User user2 = sqlSession2.getMapper(UserMapper.class).findByUid(1);//不发送SQLsqlSession2.close();//注意这里得到的对象是从缓存中拷贝出来的,如果直接使用一个会有线程安全问题System.out.println(user1 == user2);//false
}

13.配置文件

Mybatis的配置文件中有很多可配置项,我们只研究几个常用的。

注意:配置文件中的各个配置项要严格按照dtd中规定的顺序书写,可以省略某些项,但是不能颠倒顺序。

13.1 properties:引入外部配置文件

用来引入外部的配置。

我们经常把一些敏感信息单独放在一个文件中,然后通过properties 引入到配置文件。

举个例子:我们跟数据库相关的信息单独配置

1)创建一个db.properties存放相关信息。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/es
jdbc.username=root
jdbc.password=adminadmin

2)在mybatis的配置文件中引入配置文件

    <!--还支持使用<properties url="">的方式引入网络上的配置文件--><properties resource="db.properties" />

3 ) 在需要的地方使用el表达式引入需要的值

    <dataSource type="POOLED"><property name="driver" value="${jdbc.driver}" /><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource>

13.2 typeAlias:配置别名

mybatis支持别名机制。所谓别名,就是给比较长的名字起一个比较短的名字,在使用的时候二者等价而已。

默认支持的别名
自定义别名
mybatis的配置文件中有一个typeAliases标签,在这个标签下可以自定义别名。

  • 单个定义别名
  • 使用包的形式批量定义别名

当定义完别名后,下面两种使用方式就是等价的。

<select id="findByUid" resultType="com.itheima.domain.LoginInfo"></select>
<select id="findByUid" resultType="loginInfo"></select>

13.3 mappers:注册映射配置

对于将我们的Mapper文件注册到主配置文件,mybatis支持三种方式:

<!--1、直接注册Mapper映射文件-->
<mapper resource="com/itheima/dao/UserMapper.xml"/><!--2、直接注册Mapper接口类-->
<mapper class="com.itheima.dao.UserMapper"/><!--3、注册mapper包,包下的配置文件会全部被注册-->
<package name="com.itheima.mapper"/>

13.4 setting:一些常用配置

<settings><!--开启懒加载,默认是false--><setting name="lazyLoadingEnabled" value="true"/><!--覆盖默认的导致懒加载失效的方法--><setting name="lazyLoadTriggerMethods" value=""/>
</settings>

13.5 transactionManager:事务控制

Mybatis底层使用的是JDBC的事务管理,只不过在上面封装了一下。

<transactionManager type="JDBC"></transactionManager>
  • 在JDBC中我们可以通过调用Connection的setAutoCommit()方法来开关对事务的支持。
    true代表自动提交事务(默认),false代表需要手动调用commit()\rollback()等事务控制方法。

  • mybatis框架是对JDBC的封装,底层也是调用Connection的setAutoCommit()方法来控制事务的。
    只不过,Mybatis在JDBC的基础上做了修改,默认手动提交事务。

    //Mybatis的事务控制很简单主要是下面三个API
    SqlSession sqlSession = SqlSessionFactory.openSession(true);//开启事务(此事务自动提交)
    SqlSession sqlSession = SqlSessionFactory.openSession();//提交事务(此事务需要手动提交)

    SqlSession.commit();//提交事务
    SqlSession.rollback();//回滚事务

13.6 dataSource: 数据源

<dataSource type="POOLED"><!--这里的name都是数据源声明好的,必须按照这个写,每一种数据源都应该提供这些基本的参数接受项--><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>
</dataSource>mybatis中使用了标准的 JDBC 数据源(javax.sql.DataSource)接口来配置 JDBC 连接对象,可以通过mybatis的配置文件dataSource的type属性来指定。可配置项有三个:
  • UNPOOLED:不使用连接池的数据源。这个数据源的实现只是每次被请求时打开和关闭连接。
  • POOLED:使用连接池的数据源。这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。它继承并扩展了UNPOOLED,增加了连接池的功能。
  • JNDI:不做了解,知道就行。

思考:如何来更换连接池呢?比如我们想druid连接池,怎么办?

步骤:

  1. 引入druid坐标

    com.alibaba druid 1.1.15

2)自定义一个工厂类继承UnpooledDataSourceFactory,在构造方法中返回DruidDataSource数据源

public class DruidDataSourceFactory extends UnpooledDataSourceFactory {public DruidDataSourceFactory() {this.dataSource = new DruidDataSource();}
}

3)修改配置文件

<!--这里的type其实就是自定义产生数据源的工厂-->
<dataSource type="com.itheima.datasource.DruidDataSourceFactory"><!--这里的name的值要根据DruidDataSource规定的方式写--><property name="driverClass" value="${jdbc.driver}"/><property name="jdbcUrl" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>
</dataSource>

使用注解书写SQL:

Mybatis除了支持在xml中书写SQL,也支持使用注解的形式编写SQL。

但这样的形式只适合书写非常简单sql语句。

public interface UserMapper {@Insert("insert into user(name) values(#{name})")public void insertT(User user);@Delete("delete from user where id=#{id}")public void deleteById(int id);@Update("update user set name=#{name} where id=#{id}")public void updateT(User user);@Select("select * from user where id=#{id}")public User getUserById(int id);@Select("select * from user")public List<User> getAll();
}
  • 使用注解不能完成动态SQL
  • 如果SQL非常复杂,那么使用注解写在java文件中,反而感觉不伦不类
  • 硬编码在java文件中,如果需要修改SQL语句,必须要重新编译。

Mybatis 从入门到入魔相关推荐

  1. c++框架有哪些_Java Mybatis框架入门教程_v20200726

    MyBatis 的前身是 Apache 的开源项目 iBatis.MyBatis 几乎可以代替 JDBC,是一个支持普通 SQL 查询,存储过程和高级映射的基于 Java 的优秀持久层框架.MyBat ...

  2. MyBatis学习总结(1)——MyBatis快速入门

    2019独角兽企业重金招聘Python工程师标准>>> 一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所 ...

  3. MyBatis基础入门《九》ResultMap自动匹配

    MyBatis基础入门<九>ResultMap自动匹配 描述: Mybatis执行select查询后,使用ResultMap接收查询的数据结果. 实体类:TblClient.java 接口 ...

  4. mybatis的入门

    mybatis的环境搭建 第一步:创建maven工程并导入坐标         第二步:创建实体类和dao的接口         第三步:创建Mybatis的主配置文件                 ...

  5. Mybatis最入门---代码自动生成(generatorConfig.xml配置)

    [一步是咫尺,一步即天涯] 经过前文的叙述,各位看官是不是已经被Mybatis的强大功能给折服了呢?本文我们将介绍一个能够极大提升我们开发效率的插件:即代码自动生成.这里的代码自动生成包括,与数据库一 ...

  6. mybatis select count(*) 一直返回0 mysql_Mybatis教程1:MyBatis快速入门

    点击上方"Java技术前线",选择"置顶或者星标" 与你一起成长 一.Mybatis介绍 MyBatis是一个支持普通*SQL*查询,存储过程和高级映射的优秀持 ...

  7. MyBatis学习笔记(一)——MyBatis快速入门

    转自孤傲苍狼的博客:http://www.cnblogs.com/xdp-gacl/p/4261895.html 一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优 ...

  8. Mybatis从入门到精通下篇

    Mybatis从入门到精通下篇: 输入类型: 输出类型: ResultMap: 动态sql: if标签: where标签: sql片段: foreach标签: 关联查询: 以订单作为主体: 一对一查询 ...

  9. Java基础-SSM之mybatis快速入门篇

    Java基础-SSM之mybatis快速入门篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 其实你可能会问什么是SSM,简单的说就是spring mvc + Spring + m ...

  10. Mybatis的入门到精通这一篇文章就够了

    Mybatis的产生 idea 的配置 idea当中配置好maven工具 1Idea当中配置自动编译 Mybatis是什么? Mybatis的执行原理图 Mybatis的入门Demo 对象的详解 Re ...

最新文章

  1. html style属性
  2. 《超越需求:敏捷思维模式下的分析》—第1章 1.2节交付价值
  3. java code viewer_Java CodeView类代码示例
  4. 闭包---在函数内部再定义一个函数
  5. 测试人员要了解的知识
  6. 彻底理解 Cookie、Session、Token
  7. spring4.0:@Configuration的使用
  8. C罗捧得史上首个区块链得分王奖杯 1600名支付宝用户获“同款”
  9. CCF认证2014-9-2 画图
  10. 2006吴山庙会-怎么都是人啊?
  11. 软考高级 真题 2013年下半年 信息系统项目管理师 综合知识
  12. 为SM30视图创建TCODE
  13. 品牌出海:如何做好本土化运营?
  14. 面试小纸条(MySQL第一弹)
  15. 微信兔子,比较下来算是比较好用的工具
  16. EDA12--DC脚本命令(一)
  17. 支持向量机检测DGA
  18. [NLP --- 3] 文档检索算法TF-IDF
  19. 计算机技术变化太快,这世界变化太快!Ps修图进入“智能时代”!
  20. ReportViewer在Chrome 浏览器中无法显示的解决方法

热门文章

  1. PAT Basic 1031
  2. 青少年Python编程
  3. 计算机内存有何组成,电脑4个4g内存条组成16g内存和16g内存有什么不同?
  4. Navicat使用总结(2022.9)
  5. led灯光衰怎么解决_影响LED灯具光衰的原因及解决方法
  6. python获取第一个字符_Python:获取列表中第一个字符串的第一个字符?
  7. mysql audit log_关于MySQL AUDIT(审计)那点事
  8. 域名查询服务商的方法
  9. 大话西游免费版最新服务器是,2020年4月1日服务器数据互通公告
  10. 千峰JAVA逆战班Day32