Mybatis框架

Mybatis入门

1、概述

Mybatis是一个持久层框架,用java编写
它封装了JDBC操作的很多细节,使开发者只需要关注sql语句本身
而无需关注注册驱动,创建连接等繁杂过程
它使用了ORM思想实现了结果集的封装
ORM:Object Relational Mapping 对象关系映射,就是把数据库表和实体类中的属性对应起来

2、环境搭建
(1)创建Maven工程,配置依赖

//配置Mybatis的jar包依赖,会自动去中央仓库下载jar包
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.5</version>
</dependency>

(2)创建JavaBean和DAO的接口

package dao;
import domain.User;
import java.util.List;
/**
 * Mybatis允许直接定义以下类型返回值,而不需要在Mapper映射文件中设置resultType属性
 * void、Integer、Long、Boolean以及他们的基本数据类型
 */
public interface UserDao {
     List<User> findAll();
     void addUser(User user);
     void updateUser(User user);
     Boolean delUser(Integer id);
}

(3)创建Mybatis的主配置文件:SqlMapConfig.xml

<!--Mybatis的主配置文件,该约束是config约束-->
<?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">
<!--配置mybatis的运行环境,数据源、事务等-->
<configuration>
 <!-- 和spring整合后 environments配置将废除-->
    <!--
     1、mybatis可以使用properties标签来引入外部properties位置文件内容
          resource属性:引入类路径下的资源
          url属性:引入网络路径或磁盘路径下资源
     <properties resource="myproperties.properties"></properties>
        //若使用该标签引入外部配置文件,property标签中的value属性要使用${key}格式来获取值
        //例如 ${jdbc:driver}    获取到的就是myproperties配置文件中的    jdbc:driver对应的值com.mysql.jdbc.Driver

    2、settings标签:包含很多重要的设置项
            name属性:设置项名
            value属性:设置项值
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        // 表示使用驼峰命名规范  user_name  <===>   userName
    </settings>
    
    3、typeAlias标签:为某个java类起别名
        1)
        <typeAlias type="domain.User" alias="user"></typeAlias>
            type属性:指定要起别名的类的全类名,默认别名为类名小写   user
            alias:自定义别名
        2)
        <package name="domain"/>    //为该包以及子包下的所有类起默认别名
        3)
        @Alias("别名")       //通过注解形式,为该类起别名
    <typeAliases>
        <typeAlias type="domain.User" alias="user"></typeAlias>
        <package name="domain"/>
    </typeAliases>

    4、environments:mybatis可以配置多种环境,default指定使用哪一种环境,可以达到快速切换的作用
        environment:配置一个具体的环境信息,id表示当前环境的唯一标识。该标签下必须有两个标签
            transactionManager:事务管理器    
                type属性:有两种取值    JDBC\MANAGED
            dataSource:数据源  
                type属性:有三种取值    UNPOOLED\POOLED\JNDI
                
    5、databaseIdProvider:支持多数据库厂商
            type="DB_VENDOR"    该属性的作用是得到数据库厂商的标识
           <databaseIdProvider type="DB_VENDOR">
                 //为不同的数据库厂商起别名,之后只需要在Mapper映射文件中的标签中,设置属性databaseId="mysql"。说明该sql语句在哪一个数据库下使用
                <property name="Mysql" value="mysql"/>
                <property name="Oracle" value="oracle"/>
           </databaseIdProvider>
    -->

 <environments default="mysql">
     <environment id="mysql">
         <!-- 使用jdbc事务管理-->
         <transactionManager type="JDBC"></transactionManager>
         <!-- 数据库连接池-->
         <dataSource type="POOLED">
             <property name="driver" value="com.mysql.jdbc.Driver"/>
             <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
             <property name="username" value="root"/>
             <property name="password" value="123456"/>
         </dataSource>
     </environment>
 </environments>
 
 <!--指定映射配置文件的位置,映射配置文件指的是每个DAO接口独立的配置文件-->
<mappers>
    <mapper resource="dao/UserMapper.xml"/>
</mappers>
</configuration>

(4)创建映射配置文件: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">
<!--sql的映射文件-->

<!--命名空间,用于隔离sql语句-->
<mapper namespace="dao.UserDao">
    <!--
        配置UserDao接口中的findAll方法,并说明返回值类型
        该接口findAll方法的返回值是一个list集合,但是这里的resultType要写集合中的元素类型
        mybatis会自动将这些元素封装到list集合中
    -->
    <select id="findAll" resultType="domain.User">
        select * from user
    </select>
    
<!--配置UserDao接口中的addUser方法,并说明提供的参数类型-->
    <!-- 
        mybatis支持获取自增主键的值,原理上也是利用statement.useGeneratedKeys()获取
        useGeneratedKeys="true":使用自增主键获取主键值策略
        keyProperty="id":mybatis获取到主键后,将这个值封装给javaBean的id属性
        order属性:设置该标签体先于父标签体执行,还是后于父标签体执行
        resultType:设置该标签体执行后的返回值类型

        若主键的值不是自增的,可以使用selectKey标签,设置keyProperty="id",resultType="Integer"
        标签体写入查询主键的sql,将查到的值封装为Integer对象,再赋值给了javaBean的id属性
    -->
    <insert id="addUser" parameterType="domain.User" useGeneratedKeys="false" keyProperty="id">
        <!--通过User类中的属性来设置可变参数值-->
        insert into user(username,sex,birthday,address) values(#{username},#{sex},#{birthday},#{address});
        //该标签会返回一个Integer类型的id,封装到javaBean中的id属性,在父标签之后执行
        <selectKey keyProperty="id" order="AFTER" resultType="Integer">
            select id from user where username="kon";
        </selectKey>
    </insert>
    
    <!--配置UserDao接口中的updateUser方法,提供的参数类型可以省略-->
    <update id="updateUser">
        update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address}
        where id=#{id};
    </update>

    <!--配置UserDao接口中的delUser方法-->
    <delete id="delUser">
        delete from user where id=#{id};
    </delete>
</mapper>

环境搭建注意事项

  1. 在为每一个dao接口创建映射配置文件Mapper时,名称可以不同,即UserDao.xml和UserMapper.xml是一样的
  2. Mybatis的映射配置文件位置必须和dao接口的包结构相同
  3. 映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
  4. 映射配置文件的操作配置(select),id属性的值必须是dao接口中的方法名

通过注解配置mapper

  1. 在dao接口的方法上使用注解
@Select("select * from user")
List<User> findAll();
  1. SqlMapConfig.xml中配置该mapper,class值为接口的全类名
<mappers>
<mapper class="dao.UserDao"/>
</mappers>

3、入门案例

//1、读取SqlMapConfig配置文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//2、创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
//3、使用工厂生产SqlSession对象,空参的方法默认提交方式为手动提交事务
SqlSession session = factory.openSession();
//4、使用SqlSession创建Dao接口的代理对象
UserDao userdao = session.getMapper(UserDao.class);
//5、使用代理对象执行方法
List<User> users = userdao.findAll();
for (User user : users) {
 System.out.println(user);
}
//6、释放资源
session.close();
is.close();

方法详解
在这里插入图片描述

4、自定义mybaits框架(了解mybatis中执行细节)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Mybatis基本使用

单表CRUD操作

(1)添加数据

  1. dao接口中添加方法
void addUser(User user);
  1. 配置mapper映射配置文件
<!--配置UserDao接口中的addUser方法,并说明提供的参数类型-->
<insert id="addUser" parameterType="domain.User">
    <!--通过User类中的属性来设置可变参数值-->
    insert into user(username,sex,birthday,address) 
    values(#{username},#{sex},#{birthday},#{address});
</insert>
  1. 使用dao接口的代理对象,执行方法,提交事务
User user=new User();
  user.setUsername("zs");
  user.setAddress("北京");
  user.setBirthday(new Date());
  user.setSex("男");

  //5、使用代理对象执行方法,记得要提交事务,默认的提交事务方式为手动
  proxyuser.addUser(user);
  sqlsession.commit();

参数和返回值

  1. 单个参数:mybatis不会做特殊处理 #{参数名}:取出参数值
  2. 多个参数:mybatis会做特殊处理,多个参数会被封装成一个map集合
    key:param1…paramN
    value:传入的参数值
    #{param1}:取出第一个参数对应的参数值
  3. 命名参数:多个参数被封装成一个map,明确指定封装参数时map的key
    使用注解标注:@Param(“id”) 标注在接口中的每一个参数上
    key:该参数被指定的参数名
    value:参数值
    #{key}:取出对应的参数值
<select id="findByID_Name" resultType="domain.User">
   /*
       多个参数时,mybatis会将多个参数封装为一个map数组
       key=param1,param2...
       value=传递的参数值
   */
   /*select * from user where id=#{param1} and username=#{param2}*/

   /*在Dao接口中的参数上,使用了注解@Param,这样mybatis将多个参数封装为map数组时,key的值=注解中指定的值*/
   select * from user where id=#{id} and username=#{name}
</select>

传入何种参数?

  1. POJO
    若多个参数正好是业务逻辑的数据模型,我们可以直接传入pojo(JavaBean)
  2. Map
    若多个参数不是业务逻辑中的数据模型,没有对应的pojo,不经常使用,为了方便,也可以直接传入Map
  3. TO
    若多个参数不是业务逻辑的数据,但是经常使用,推荐编写一个TO(Transfer Object)数据传输对象

小练习

  1. public Employee getEmp(@Param(“id”) Integer id,String lastName);
    取值:id==>#{id/param1} ,lastName==>#{param2}
  2. public Employee getEmp(Integer id,@Param(“e”)Employee emp);
    取值:id==>#{param1} ,lastName==>#{parame2.lastName/e.lastName}
  3. public Employee getEmpById(List< Integer> ids);
    取值:id==>#{list[0]/collection[0]}

特别注意

若参数是Collection(List,Set)或者是数组类型,也会特殊处理,即把传入的Collcetion或者数组封装在Map中

  1. 若参数类型是Collection: key=collection,若是List集合,key还可以等于list
  2. 若参数类型是数组: key=array

参数的获取

  1. #{}:是以预编译的形式,将参数设置到sql语句中,类似于PreparedStatement,可以防止SQL注入
  2. ${}:取出的值直接拼接在SQL上,会有安全问题
  3. 使用情景:
    大多数情况,使用#{};当原生JDBC不支持占位符的地方,我们可以使用${}进行取值,例如,查询年份表,设置排序方式
    select * from ${year}_year select * from ${year}_salary order by
    salary ${order}

#{}更丰富的用法
可以规定参数的一些规则:jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName,expression(未来准备的功能)

jdbcType通常需要在某些特定情况下被设置:
在我们数据为null时,有些数据库不识别mybatis对null的默认处理。例如Oracle

报错:JdbcTpye OTHER:无效的类型;因为mybatis对所有的null都映射为了OTHER类型

全局配置文件中:jdbcTypeForNull默认为OTHER,oracle不支持

两种解决方式:
1、mapper中:
#{password,jdbcType=NULL};获取password的值,若为null,映射为null
2、SqlMapConfig中:
< setting name=“jdbcTypeForNull” value=“NULL”/>;null类型数据,都映射为null

resultType

该属性中返回的是 期望类型的类的全限定类名或者别名
注意如果是集合,那应该是集合可以包含的类型,而不是集合本身
该属性和resultMap,不能同时使用

resultMap

自定义结果集规范,外部resultMap的命名引用,不能和resultType同时使用
当数据库中的字段和javaBean中的属性对应不上时,可使用自定义结果集规范

 <!--
    自定义某个javaBean的封装规则
    type:自定义规则的java类
    id:唯一id方便引用
-->
<resultMap id="myresult" type="domain.User">
    <!--指定主键列的封装规则,使用id定义主键会有优化-->
    <!--
        column:指定数据库的哪一个字段
        property:指定对应的javaBean属性
    -->
    <id column="id" property="id"/>
    <!--
        定义普通列封装规则,其他不指定的列会自动封装,建议都写上
    -->
    <result column="username" property="username"/>
    <result column="sex" property="sex"/>
</resultMap>

<select id="findAll" resultMap="myresult">
    select * from user;
</select>

3、DAO编写
4、配置的细节,几个标签的使用

Mybatis多表查询

1)、association多对一:查询User的同时查询用户对应的部门,涉及到两张表
User–>Department,每一个员工都有对应的部门信息

 <resultMap id="UserAndDep" type="domain.User">
     <!--级联属性封装结果集,在User类中还有Department类,两者通过d_id和dep_id外键关联-->
     <id column="id" property="id"/>
     <result column="username" property="username"/>
     <result column="sex" property="sex"/>
     <result column="birthday" property="birthday"/>
     <result column="address" property="address"/>
     <result column="d_id" property="d_id"/>
     <result column="dep_id" property="department.dep_id"/>
     <result column="dep_name" property="department.dep_name"/>
<!--
    也可以使用association来指定联合的javaBean对象
    property:说明user中的哪一个属性是联合属性
    javaType:说明该联合属性的java类型,会自动封装为该类型
-->
     <association property="department" javaType="domain.Department">
         <id column="d_id" property="dep_id"/>
         <result column="dep_name" property="dep_name"/>
     </association>
</resultMap>
<select id="findUserAndDepById" resultMap="UserAndDep">
    <!--
        这条sql查出来的字段有:id,username,sex,birthday,address,d_id,dep_id,dep_name
        若使用resultType返回值,无法封装返回值中的信息,这时使用resultMap自定义封装信息
    -->
    select * from user u,department dep where u.d_id = dep.dep_id and u.id=#{id}
</select>

2)、association多对一进行分步查询:先查询user表中的信息,再根据表中的d_id查询department表中的信息*

<!-- 该mapper为根据部门id查询该部门信息 -->
<mapper namespace="dao.DepartmentDao">
    <select id="findById" resultType="domain.Department">
        select * from department where dep_id=#{id}
    </select>
</mapper>
 <resultMap id="selectByStep" type="domain.User">
	<!--分步查询,先将查到的user表中的数据封装为一个user对象-->
	  <id column="id" property="id"/>
	  <result column="username" property="username"/>
	  <result column="sex" property="sex"/>
	  <result column="birthday" property="birthday"/>
	  <result column="address" property="address"/>
	  <result column="d_id" property="d_id"/>

  <association property="department" select="dao.DepartmentDao.findById" column="d_id">
  <!--
       将查询到的d_id字段,传递给另一条select标签(是DepartmentMapper下的一个select标签,根据id查找部门表中的信息)
       再封装给property指定的属性
       property:说明哪一个属性是联合属性,最后会将数据封装到该属性中
       select:说明调用哪里的select标签,使用那个标签所在的namespace+id
       column:将这一列参数传入select标签中
    -->
  </association>
</resultMap>

<select id="findById" resultMap="selectByStep">
  <!--
      这条sql查出来的字段有:id,username,sex,birthday,address,d_id
      使用分布查询的方式,先将这些数据封装到User中,再将查到的d_id,去执行另一个select标签
  -->
  select * from user where id=#{id}
</select>

3)、延迟加载(懒加载、按需加载)

我们每次查询User对象时,Department也会被查出并封装为对象
部门信息在我们使用的时候再去查询
我们可以在分步查询的基础上,添加两个配置

<settings>
    <!--设置延迟加载,联合属性的值只有在调用的时候才会查询-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

4)、collection一对多: 查询部门表时,查询出该部门的所有用户,涉及两张表;使用了嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则
Department–>User,每一个部门对应多个用户信息

<resultMap id="findUserByDepID" type="domain.Department">
    <id column="dep_id" property="dep_id"/>
    <result column="dep_name" property="dep_name"/>
    <!--
        将User表中的字段封装为每一个User对象,并存入List集合,这时只能使用collection,而不能用association
        collection可以封装多个对象,association只能封装一个对象
        property:说明关联属性
        ofType:说明关联属性集合中对应的java类型
    -->
    <collection property="user" ofType="domain.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="sex" property="sex"/>
        <result column="address" property="address"/>
    </collection>
</resultMap>
<select id="findUserByDepID" resultMap="findUserByDepID">
    <!--
        一对多:一个部门id对应多个User对象
        以下sql查询出来的字段包括:department:dep_id,dep_name||user:id,username,sex,address
        只能使用自定义结果集,分别封装数据
    -->
    SELECT d.dep_id,dep_name,u.id,username,sex,address
    FROM department d
    LEFT JOIN user u
    on d.dep_id = u.d_id
    where d.dep_id=#{id}
</select>

4)、collection一对多进行分步查询: 先查询department表中的信息,再根据department表中的id,查询user表中所有符合该id的记录

<resultMap id="findUserByStep" type="domain.Department">
   <!--
       先将查询到的部门信息封装为一个department对象
   -->
   <id column="dep_id" property="dep_id"/>
   <result column="dep_name" property="dep_name"/>

   <!--
       再根据查询到的dep_id到User表中查询所有符合的记录,封装给property指定的属性
       property:指定关联的属性  ==》 department javaBean类中的 List<User> user 属性
   -->
   <collection property="user" select="dao.UserDao.findUserByDepIdStep" column="dep_id">
   </collection>

</resultMap>

<select id="findDepByIdStep" resultMap="findUserByStep">
     <!-- 该sql返回的字段有dep_id,dep_name -->
     select * from department where dep_id=#{id};
</select>

5)、扩展知识:

 <collection property="user" select="dao.UserDao.findUserByDepIdStep" column="{id=dep_id}" fetchType="lazy">
   <!--
       若要向select中传递多列的值,可以传递一个map
       写法:column="{key1=column1,key2=column2}"
       此处的key要等于接收方接受的key
       因为在select指向的标签中,参数的key=id,所以这里要传入map时,key要指定为id
       fetchType:
                   “lazy”  延迟加载
                   “eager” 立即加载,而不需要修改全局配置
   -->
</collection>

5)、扩展知识discriminator鉴别器: mybatis可以使用discriminator判断某列的值,根据值改变封装行为

<!--
    mybatis可以使用discriminator判断某列的值,根据值改变封装行为
    例如:若查出的是女生,就把用户信息查询出来封装user对象
    若查出的是男生,封装user对象时,将username赋值给email

    javaType:列值对应的java类型
    column:指定判定的列名
    resultType:指定封装的结果类型
-->
<resultMap id="discriminator" type="domain.User">
    <discriminator javaType="string" column="sex">
        <case value="" resultType="domain.User">

        </case>
        <case value="" resultType="domain.User">
            <id column="id" property="id"/>
            <result column="username" property="username"/>
            <result column="username" property="address"/>
        </case>
    </discriminator>
</resultMap>
<select id="findById" resultMap="discriminator">
    select * from user where id=#{id}
</select>

动态SQL

几种常用标签:

  1. if:判断;包含test属性
  2. where:将查询条件包括在内,mybatis就会自动去除第一个多出来的and或者or,防止sql拼接出问题
  3. choose(when,otherwise):类似于switch case default,只会进入一个分支执行
  4. trim:用来拼接sql字符串;包含 prefix,profixOverrides,suffix,suffixOverrides属性
  5. set:将更新的条件放入set中,mybatis会自动去除最后一个多余的逗号
  6. foreach:可以遍历一个list或者map,取出里面的每一个元素,动态拼接到sql上;包含 collection,item,open,close,separator,index属性
  7. include:可以引用已经抽取的sql标签中的内容,还可以设置一些属性property,供sql标签中使用,用${}方式来取属性,而不能用#{}
<select id="findUserByDynSql" parameterType="domain.User" resultType="domain.User">
<!--
   根据传入的user对象中包含的属性,动态拼装sql

   test:写boolean类型的表达式,可以是OGNL表达式
-->
   select * from user where
   <!-- 此处的id是传入过来的参数中的id属性 -->
   <if test="id!=null">
       id=#{id}
   </if>
   <if test="username!=null and username!=''">
       and username like #{username}
   </if>
   <if test="sex!=null">
       and sex=#{sex}
   </if>
 </select>

注意:
查询的时候,若某些条件没带,sql的拼装可能会出问题,例如以上例子中,若不带id,拼接的sql是这样的

select * from user where and username like ? and sex=?

更新的时候,也会出现sql拼装问题,例如

update user set username=?, where id=?

解决方式:
1、给where条件后面加上1=1,以后的条件都有and拼接
2、使用where标签
将所有的查询条件包括在内,mybatis会自动去除第一个多出来的and或者or在where标签中

<select id="findUserByDynSql" parameterType="domain.User" resultType="domain.User">
    <!--
        根据传入的user对象中包含的属性,动态拼装sql

        test:写boolean类型的表达式,可以是OGNL表达式
        使用where标签,将查询条件包括在内,mybatis就会自动去除第一个多出来的and或者or,防止sql拼接出问题
        也可以给where条件后面加上1=1,以后的条件都有and拼接
    -->
    select * from user
    <where>
    <!-- 此处的id是传入过来的参数中的id属性 -->
    <if test="id!=null and id!=0">
        id=#{id}
    </if>
    <if test="username!=null and username!=''">
        and username like #{username}
    </if>
    <if test="sex!=null">
        and sex=#{sex}
    </if>
    </where>
</select>

3、使用trim标签
拼接sql的万能方式

<select id="findUserByDynSql" parameterType="domain.User" resultType="domain.User">
    <!--
        根据传入的user对象中包含的属性,动态拼装sql

        test:写boolean类型的表达式,可以是OGNL表达式
                    
        使用trim标签,可以在trim包裹的sql字符串上拼接内容
        prefix:前缀,在trim包裹的sql字符串上添加前缀
        profixOverrides:去掉sql字符串前缀字符
        suffix:给sql字符串添加后缀
        suffixOverrides:去掉整个sql字符串后面多余的字符
    -->
    select * from user
    <trim prefix="where" prefixOverrides="and" suffix="" suffixOverrides="and">
    <!-- 此处的id是传入过来的参数中的id属性 -->
    <if test="id!=null and id!=0">
        id=#{id} and
    </if>
    <if test="username!=null and username!=''">
        username like #{username} and
    </if>
    <if test="sex!=null">
        sex=#{sex} and
    </if>
    </trim>
</select>

4、使用choose标签
该标签类似于switch case,只会进入某一分支执行

<where>
    <choose>
        <when test="id!=null and id!=0">
        	id=#{id}
        </when>
        <when test="username!null and username!=''">
        	username=#{username}
        </when>
        <otherwise>1=1</otherwise>
    </choose>
</where>

5、使用set标签
将要更新的条件放入改标签体中,mybatis会自动去除多余的逗号

<!--
    动态拼接更新数据的sql语句,将要更新的条件放入set标签中,mybatis会自动去除多余的逗号
-->
<update id="updateUser" parameterType="domain.User">
    update user
    <set>
        <if test="username!=null and username!=''">
            username=#{username},
        </if>
        <if test="sex!=null and sex!=''">
            sex=#{sex},
        </if>
    </set>
    where id=#{id}
</update>

6、使用foreach标签
可以遍历传入的参数值,动态生成sql语句

<!--
    动态拼接sql,根据传入的id集合,查询记录

    collection:指定要遍历的集合,list类型集合会特殊处理封装在map中,map的key就叫list
    item:将遍历出的元素赋值给指定变量
    separator:每个元素之间的分隔符
    open:在标签体执行之前,拼接一个指定字符
    close:在标签体执行结束后,拼接一个指定字符
    index:遍历list的时候,index就是索引,item就是值
           遍历map的时候,index就是map的key,item就是map的value
    #{变量名}:就能取出变量的值即当前遍历出的元素
-->
<select id="findUserByIds" parameterType="list" resultType="domain.User">
    select * from user where id in
    <!-- 遍历传入的list集合 -->
     <foreach collection="list" item="item_id" open="(" separator="," close=")">
         #{item_id}
     </foreach>
</select>
<!--
    动态拼接sql,根据传入的user集合,添加记录

    mysql支持 values(),(),()的语法来批量添加记录
-->
<insert id="addUsers">
    insert into user (username,sex,birthday,address,d_id)
    values
    <foreach collection="users" item="user" separator=",">
    (#{user.username},#{user.sex},#{user.birthday},#{user.address},#{user.d_id})
    </foreach>
</insert>

<!--
	第二种批量操作方式
	不仅可以批量添加,还可以批量删除,更新等等,sql语句之间用分号分隔
	这样需要设置数据库连接属性allowMultiQueries=true 允许多个sql语句执行
-->
<insert id="addUsers">
    <foreach collection="users" item="user" separator=";">
        insert into user (username,sex,birthday,address,d_id)
    	values(#{user.username},#{user.sex},#{user.birthday},#{user.address},#{user.d_id})
    </foreach>
</insert>

7、内置参数_parameter&_databaseId
mybatis在传递参数时,会再传递两个参数

  1. _parameter:代表整个参数
    1. 单个参数:_parameter就是这个参数
    2. 多个参数:参数会被封装为map集合,_parameter就是这个map
  2. _databaseId:若配置了databaseIdProvider标签
    _databaseId就代表当前数据库的别名
<foreach collection="users" item="user" separator=",">
   <if test="_parameter!=null and _database=='mysql'">
       (#{user.username},#{user.sex},#{user.birthday},#{user.address},#{user.d_id})
   </if>
</foreach>

8、使用include标签
引用被sql标签抽取的sql片段,还可以设置一些property,供sql标签使用

<!--
    动态拼接sql,根据传入的user集合,添加记录

    mysql支持 values(),(),()的语法来批量添加记录
    include标签:可以引入外部sql,可以自定义一些property,供外部sql标签使用
-->
<insert id="addUsers">
    insert into user
    <include refid="parameter">
        <property name="d_id" value="d_id"/>
    </include>
    values
    <foreach collection="users" item="user" separator=",">
    (#{user.username},#{user.sex},#{user.birthday},#{user.address},#{user.d_id})
    </foreach>
</insert>

<!--抽取sql片段,可以调用include标签中定义的property属性,只能使用${},而不能使用#{}-->
<sql id="parameter">
    (username,sex,birthday,address,${d_id})
</sql>

Mybatis缓存和注解开发

一级缓存和二级缓存

一级缓存(本地缓存)
1、概念

SqlSession级别的缓存,该缓存一直是开启的
与数据库同一次会话期间查询到的数据会放在本地缓存中
以后若需要获取相同数据,直接从缓存中拿,不用再发sql语句查询数据库

2、一级缓存失效情况(即没有使用到缓存中的数据,再次向数据库发送了sql)

  1. sqlSession不同
  2. sqlSession相同,查询条件不同,即当前一级缓存中没有该数据
  3. sqlSession相同,两次查询期间,进行了增删改操作(这些操作可能对数据有影响)
  4. sqlSession相同,手动清除了一级缓存 sqlSession.clearCache()

二级缓存(全局缓存)
1、概念

基于namespace级别的缓存,一个namespace对应一个二级缓存
工作机制:

  1. 一个会话,查询一条记录,这个数据会被放在当前会话的一级缓存中
  2. 若会话关闭,一级缓存中的数据保存到二级缓存中,新的会话查询可以参照二级缓存中的数据
  3. sqlSession
    === UserMapper = =>User
    === DepartmentMapper= =>Department
    不同的namespace查出的数据会放在自己对应缓存中

2、使用
1、 在全局配置文件中设置为开启二级缓存

< setting name=“cacheEnabled” value=“true”/>

2、在mapper.xml文件中加入< cache >标签

cache标签中的几个属性:

  1. eviction:缓存的回收策略
    1. LRU 最近最少未使用,移除最长时间不被使用的对象。默认值
    2. FIFO 先进先出,按对象进入缓存的顺序来移除
    3. SOFT 软引用,移除基于垃圾回收器状态和软引用规则的对象
    4. WEAK 弱引用,更积极的移除基于收集器状态和弱引用规则的对象
  2. flushInterval:缓存刷新间隔
    缓存多长时间清空一次,单位毫秒值;默认不清空
  3. readOnly:是否只读
    1. true:mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据 为了加快获取速度,直接将该数据的引用交给用户;不完全,速度快
    2. false:默认值;mybatis认为获取的数据可能会被修改,会使用序列化&反序列化的技术,克隆一份新的数据给用户;安全,速度慢
  4. size:说明该缓存最大容量上限
  5. type:使用自定义的缓存机制,值为全类名;自己写一个类,实现cache接口

3、我们的POJO需要实现序列化接口

因为有可能涉及到javaBean的序列化操作

 User user1 = proxyuser.findById(1);
 SqlSession session2 = factory.openSession();
 UserDao proxyuser2 = session2.getMapper(UserDao.class);

 //此时sqlsession被关闭,一级缓存中数据保存到二级缓存中
 session.close();
 //此处拿到的对象是mybatis从二级缓存中克隆了一份新的数据,所以user1!=user2
 User user2 = proxyuser2.findById(1);
 System.out.println(user1==user2);

注意:

二级缓存若要被使用,必定是在一次会话关闭后,一级缓存中的数据保存到二级缓存

4、和缓存有关的设置/属性

  1. cacheEnabled=true/false 开启/关闭二级缓存
  2. 每个select标签中都有useCache=true/false 使用/不使用二级缓存
  3. 每个增删改查标签中都有flushCache=true/false 增删改查操作执行后是否清除缓存,增删改默认清除,查询标签默认不清除
  4. sqlSession.clearCache();只是清除当前sqlSession的缓存
  5. localCacheScope;本地缓存作用域,默认值SESSION,即一级缓存,可以设置为STATEMENT,即禁用了一级缓存

5、缓存原理图示
在这里插入图片描述

Mybatis逆向工程

  • Mybatis Generator:简称MBG,是一个专门为mybatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及javabean类。支持基本的增删改查、以及QBC风格的条件查询。但是表连接、存储过程等复杂sql的定义需要手工编写
  • 官方文档地址
    http://www.mybatis.org/generator/
  • 官方工程地址
    http://github.com/mybatis/generator/releases

Mybatis运行原理

1、获取SqlSessionFactory对象

解析文件的每一个信息保存在Configuration对象中,返回包含Configuration对象的DefaultSqlSessionFactory对象

在这里插入图片描述

2、获取SqlSession对象

返回一个DefaultSqlSession对象,该对象中包含了Executor和Configuration对象
这一步会创建Executor对象

在这里插入图片描述
3、获取接口的代理对象(MapperProxy)

getMapper,使用MapperProxyFactory创建一个MapperProxy的代理对象
该对象中包含了,DefaultSqlsession(Executor)

在这里插入图片描述
4、执行接口中的的方法

运行原理总结

  1. 根据配置文件(全局,sql映射)初始化出Configuration对象
  2. 创建一个DefaultSqlSession对象
    它里面包含Configuration以及
    Executor(根据全局配置文件中的defaultExecutorType创建对应的executor)
  3. DefaultSqlSession.getMapper();拿到Mapper接口对应的MapperProxy对象
    MapperProxy对象里面有(DefaultSqlSession)
  4. 执行增删改查方法
    1. 调用DefaultSqlSession的增删改查(Executor)
    2. 创建一个StatementHandler对象(同时也会创建出ParameterHandler和ResuletSetHandler)
    3. 调用StatementHandler预编译参数以及设置参数值,使用ParameterHandler来给sql设置参数
    4. 调用StatementHandler的增删改查方法
    5. ResultSetHandler封装结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值