wy132
wy132
发布于 2024-07-14 / 2 阅读
0

MyBatis 笔记

1. 基本使用方法

  1. 创建核心配置文件mybatis-config.xml

    image-20240510162321140

    • mybatis-config.xml中配置mybatis的核心配置文件

    • mappers文件夹存放映射文件xml,其文件被mybatis-config.xml引入

    <?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="development">  
            <environment id="development">  
                <transactionManager type="JDBC"/>  
                <dataSource type="POOLED">  
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>  
                    <property name="url" value="jdbc:mysql://localhost:3306/MyBatis"/> 
                    <property name="username" value="root"/>  
                    <property name="password" value="123456"/>  
                </dataSource>  
            </environment>  
        </environments>  
        <!--引入mappers文件夹的映射文件-->  
        <mappers>  
            <mapper resource="mappers/UserMapper.xml"/>  
        </mappers>  
    </configuration>
  2. 创建接口类DAO,UserMapper

    package com.atguigu.mybatis.mapper;  
    public interface UserMapper {  
        /**  
        * 添加用户信息  
        */  
        int insertUser();  
        int deleteUser();
    }
  3. 创建DAO接口类的映射配置文件 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">  
    <!--指定为哪个DAO接口类配置-->
    <mapper namespace="com.atguigu.mybatis.mapper.UserMapper">  
        <!--int insertUser();id值为指定接口的方法-->  
        <insert id="insertUser">  
            <!--编写sql insert 语句-->
            insert into t_user values(null,'张三','123',23,'女')  
        </insert>  
        <!--删除、更新、选择语句的标签-->
        <delete></delete>
        <updata></updata>
        <select></select>
    </mapper>
  4. service层调用DAO接口,进行数据操作

        public void testInsertUser() throws IOException {
            //读取MyBatis的核心配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            //获取SqlSessionFactoryBuilder对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory,生产SqlSession对象
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
            //获取sqlSession,此时通过SqlSession对象所操作的sql都必须手动提交或回滚事务
            //SqlSession sqlSession = sqlSessionFactory.openSession();
            //创建SqlSession对象,此时通过SqlSession对象所操作的sql都会自动提交  
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            //通过代理模式创建UserMapper接口的代理实现类对象
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配映射文件中的SQL标签,并执行标签中的SQL语句
            int result = userMapper.insertUser();
        }
    • userMapper.insertUser()调用DAO类UserMapper的接口方法,然后根据UserMapper.xml配置的sql语句,底层进行对数据库的操作

2. 日志依赖

和Spring一样,MyBatis有自己专属的日志功能

3. 简单的增删查改

<insert id="insertUser">
    insert into t_user values(null,'admin','123456',23,'男','12345@qq.com')
</insert>
<delete id="deleteUser">
     delete from t_user where id = 6
 </delete>
 <update id="updateUser">
     update t_user set username = '张三' where id = 5
 </update>
<select id="getUserById" resultType="com.atguigu.mybatis.bean.User">  
    select * from t_user where id = 2  
</select>
  • 查询返回实体的设定:属性

    • resultType:自动映射,属性名和表中字段名必须一致的情况

    • resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况


4. sql语句获取参数

获取若干个参数:

  1. 接口方法:使用@Param注解,为每个形参建立键值对,username - unamepassword - pwd

    User CheckLoginByParam(@Param("username") String uname, @Param("password") String pwd);
  2. sql语句:使用#{键},获取键值

        <select id="CheckLoginByParam" resultType="User">
            select * from t_user where username = #{username} and password = #{password}
        </select>
  3. 使用:

    mapper.CheckLoginByParam("admin","123456");

执行原理:@Param为形参创建键值对,则 sql 中使用语法糖来调用键值对

获取实体类参数:

#{username}会自动获取形参实体的属性值,需参数和属性名相同

<insert id="insertUser">
    insert into t_user values(null,#{username},#{password},#{age},#{sex},#{email})
</insert>
    User user = new User(null,"Tom","123456",12,"男","123@321.com");
    mapper.insertUser(user);

5. 各种查询功能

  1. 查询一个实体类:

    User getUserById(@Param("id") int id);
  2. 查询多个实体类:

    List<User> getUserList();
  3. 查询一个实体类(键值对返回):会将实体类的属性转换为键值对形式

    Map<String, Object> getUserToMap(@Param("id") int id);
    //结果:{password=123456, sex=男, id=1, age=23, username=admin}
  4. 查询多个实体类(键值对返回):

    List<Map<String, Object>> getAllUserToMap();

6. 特殊sql

模糊查询:"%"#{}"%"拼接方式方式实现

<select id="getUserByLike" resultType="User"> 
    select * from t_user where username like "%"#{mohu}"%"
</select>

批量删除:只能使用${},不能使用#{}

<delete id="deleteMore">
    delete from t_user where id in (${ids})
</delete>

按表查询: 只能使用${},不能使用#{}

<select id="getUserByTable" resultType="User">
    select * from ${tableName}
</select>

获取添加的数据的自增主键:

  • 在mapper.xml中设置两个属性

  • useGeneratedKeys:设置使用自增的主键

    • keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性中

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
    insert into t_user values (null,#{username},#{password},#{age},#{sex},#{email})
</insert>

7. 自定义resultMap

:解决数据库表的字段名和实体名不一致问题

基本使用:

<!--resultMap中
    empResultMap:自定义名,给select的resultMap调用
    Emp:实体类
    eid:主键,Map属性,唯一设定
    empName:实体类中的属性
    emp_name:数据库中的表的字段
-->
<resultMap id="empResultMap" type="Emp"> 
    <id property="eid" column="eid"></id>
    <result property="empName" column="emp_name"></result>
    <result property="age" column="age"></result>
    <result property="sex" column="sex"></result>
    <result property="email" column="email"></result>
</resultMap>
<!--List<Emp> getAllEmp();-->
<select id="getAllEmp" resultMap="empResultMap">
    select * from t_emp
</select>

8. 多对一映射处理

:多个员工对应一个部门

:查询的实体类的属性有另一个实体的问题,如下

public class Emp {  
    private Integer eid;  
    private Dept dept;  //实体类
}

方式一:级联association

<resultMap id="empAndDeptResultMapTwo" type="Emp">
    <id property="eid" column="eid"></id>
    <!--
        dept:实体类的dept属性
        Dept:dept的实体类型
        里面设定Dept的属性名映射
    -->
    <association property="dept" javaType="Dept">
        <id property="did" column="did"></id>
        <result property="deptName" column="dept_name"></result>
    </association>
</resultMap>

方法二:分步查询

:通过调用另一个查询方法来组合查询

Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);  //根据员工id查询返回Emp类
Dept getDeptAndEmpByStepOne(@Param("did") Integer did); //根据部门id查询返回Dept

Emp中的xml设定:

<resultMap id="empAndDeptByStepResultMap" type="Emp">
    <id property="eid" column="eid"></id>
    <!--association里的表示:Emp的dept属性调用select值的查询方法进行查询,传参为did-->
    <association property="dept"
                 select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
                 column="did"></association>
</resultMap>

Dept中的xml设定不用改变

9. 一对多映射处理

:一个部门实体类对应多个员工实体类

public class Dept {
    private Integer did;
    private List<Emp> emps;
}

分步查询:collection标签

<resultMap id="DeptAndEmpByStepOneResultMap" type="Dept">
    <id property="did" column="did"></id>
    <result property="deptName" column="dept_name"></result>
    <collection property="emps"
                select="com.atguigu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
                column="did"></collection>
</resultMap>

10. 延迟加载

例子:

实体类:

public class Dept {
    private Integer did;
    private List<Emp> emps;
}

分步查询的xml:

<resultMap id="DeptAndEmpByStepOneResultMap" type="Dept">
    <id property="did" column="did"></id>
    <collection property="emps"
                select="com.atguigu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
                column="did"></collection>
</resultMap>

使用查询接口:

Dept dept = mapper.DeptAndEmpByStepOneResultMap(1);

输出属性:

System.out.println(dept.getDeptDid());

开启延迟加载:如果只使用查询结果的dept.did属性,不使用emp的属性,则不会再执行collection里的分步查询select,如果使用dept.emp[0].idemp的属性,才会执行绑定的分步查询

这叫做延迟加载

分步查询可以实现延迟加载

<settings>
    <!--全局配置,开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>

局部控制延迟加载:fetchType = "lazy(延迟加载)|eager(立即加载)"

    <association property="dept"
                 select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
                 column="did"
                 fetchType="lazy">
    </association>

11. 动态sql

if: 如果true则拼接,否则不拼接

  • test属性:判断语句

<select id="getEmpByCondition" resultType="Emp">
    select * from t_emp where 1=1
    <if test="empName != null and empName !=''">
        and emp_name = #{empName}
    </if>
    <if test="age != null and age !=''">
        and age = #{age}
    </if>
</select>

where:与 if 结合使用,如果没有 if 为true,则不添加where,否则添加where

<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="Emp">
    select * from t_emp
    <where>
        <if test="empName != null and empName !=''">
            emp_name = #{empName}
        </if>
        <if test="age != null and age !=''">
            and age = #{age}
        </if>
    </where>
</select>

trim:最灵活的标签

  • prefix:设置在trim标签前添加的内容

  • suffix:在trim标签中的内容的后面添加某些内容

  • prefixOverrides:在trim标签中的内容的前面去掉某些内容

  • suffixOverrides:在trim标签中的内容的后面去掉某些内容

abc 
<trim prefix="where" 
      suffix="#"
      prefixOverrides="@"
      suffixOverrides="%"
      > @@jni%%</trim>
abc 

得到的sql语句:abc #@joi% abc

<select id="getEmpByCondition" resultType="Emp">
    select * from t_emp
    <trim prefix="where" suffixOverrides="and|or">
        <if test="empName != null and empName !=''">
            emp_name = #{empName} and
        </if>
        <if test="age != null and age !=''">
            age = #{age} and
        </if>
    </trim>
</select>

choose、when、otherwise:

相当于:if .... else if ..... else

foreach:

:实现批量操作

  • collection:设置@Param的参数数组

  • item:表示数组的每一个值

  • separator:设置每一个内容的分隔符

  • open、close:设置foreach标签的前后的符号

<!--int deleteMoreByArray(@Param("eids") Integer[] eids);-->
<!--对数组eids的每个值进行删除操作-->
<delete id="deleteMoreByArray">
    delete from t_emp where eid in
    <foreach collection="eids" item="eid" separator="," open="(" close=")">
        #{eid}
    </foreach>
</delete>

得到的sql语句:delete from t_emp where eid in (1,2,3,4,5)

sql片段:

:设置一公共sql片段,在其他地方插入

<!--使用sql标签-->
<sql id="empColumns">eid,emp_name,age,sex,email</sql>
<!--使用include标签,在sql语句中插入-->
<select id="getEmpByCondition" resultType="Emp">
    select <include refid="empColumns"></include> from t_emp
</select>

12. MyBatis的缓存

 public void testInsertUser() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        int result = userMapper.insertUser();
    }

一级缓存:

SqlSession级别:通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问

SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
Emp result1 = userMapper1.FindEmpById(1);
UserMapper userMapper2 = sqlSession1.getMapper(UserMapper.class);
Emp result1 = userMapper2.FindEmpById(1);
  • userMapper1.FindEmpById(1)查询后的结果被缓存到sqlSession1空间,userMapper2.FindEmpById(1)会直接从缓存中读取数据,非从数据库

  • 同一个SqlSession两次查询期间执行了任何一次增删改操作会清空缓存

二级缓存:

SqlSessionFactory级别:比SqlSession更大,需手动开启,每一个SqlSessionFactory可创建多个SqlSession,每个SqlSession关闭或提交后,才将其缓存转到SqlSessionFactory中,才能被其他SqlSession使用

SqlSessionFactoryd的配置属性:

  • eviction:缓存的回收策略

  • flushInterval:刷新间隔

  • size:缓存大小

  • readOnly:设置只读或读写

查询语句的查询顺序:

先查询二级缓存,再查询一级缓存,最后查询数据库

缓存命中率: 指查询的结果在一级缓存中能查找到的概率

整合第三方缓存EHCache:了解

13. MyBatis的逆向工程

:逆向工程:先创建数据库表,由框架负责根据数据库表,自动生成对应的POJO、mapper类

14. 分页插件