Mybatis 从入门到入魔

本文详细介绍了Mybatis的入门配置,包括创建工程、配置主配置文件、XML映射文件的编写,以及基于接口代理的CRUD操作和动态SQL的使用。涵盖了数据源配置、事务管理、返回主键、查询优化和多表关联等内容。
摘要由CSDN通过智能技术生成

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 创建sqlSession
	SqlSession 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;

    @Before
    public void init() {
        sqlSession = MybatisUtil.openSession();
    }

    @After
    public 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 into
            user(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 {
    
        //保存
        @Test
        public 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查询
        @Test
        public void testFindByUid() {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User user = userMapper.findByUid(1);
            System.out.println(user);
        }
    
    
        //根据UID修改
        @Test
        public void testUpdate() {
            User user = new User();
            user.setUid(1);
            user.setPassword("adminadmin");
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            userMapper.update(user);
        }
    
        //根据ID删除
        @Test
        public 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 into
      user(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 into
      user(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 into
  user(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;-->
    select
        l.*,
        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    得到uid
    2)根据上一拿到的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);//发送SQL
    System.out.println("==============");
    User user2 = mapper.findByUid(1);//未发送SQL

    System.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);//发送SQL
    sqlSession1.close();

    System.out.println("===================");
    SqlSession sqlSession2 = MybatisUtil.openSession();
    User user2 = sqlSession2.getMapper(UserMapper.class).findByUid(1);//不发送SQL
    sqlSession2.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语句,必须要重新编译。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值