• Mybatis框架学习


    什么是mybatis?

    mybatis是一款用于持久层的、轻量级的半自动化ORM框架,封装了所有jdbc操作以及设置查询参数和获取结果集的操作,支持自定义sql、存储过程和高级映射

    mybatis用来干什么?

    用于处理java和数据库的交互

    使用mybatis的好处

    1. 与JDBC相比,减少了50%以上的代码量。
    2. MyBatis灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL可以写在XML里(还可以以注解方式写到Java代码中),从程序代码中彻底分离,降低耦合度,便于统一管理和优化,可重用。
    3. 提供XML标签,支持编写动态SQL语句(XML中使用 if, else 等)

    使用mybatis

    下载Mybatis的jar包:Releases · mybatis/mybatis-3 (github.com)

    创建xml文件:mybatis-config.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
      PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
      "https://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
      <environments default="development">
        <environment id="development">
          <transactionManager type="JDBC"/>
          <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
          </dataSource>
        </environment>
      </environments>
      <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
      </mappers>
    </configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    修改内容:

    SqlSessionFactoryBuilder

    通过SqlSessionFactoryBuilder可创建多个SqlSessionFactory实例

    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream("mybatis-config.xml"));
    
    
    • 1
    • 2

    SqlSessionFactory

    通过SqlSessionFactory创建多个会话,SqlSession对象,每个会话就相当于我不同的地方登陆一个账号去访问数据库。

    一般SqlSessionFactory只需要创建一次

    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream("mybatis-config.xml"));
    
    • 1

    SqlSession

    使用 MyBatis 的主要 Java 接口就是 SqlSession。你可以通过这个接口来执行命令,获取映射器实例和管理事务。SqlSession实例可直接执行已映射的sql语句。

    确保SqlSession关闭的标准模式:

    //try-with-resource
    try(SqlSession session = sqlSessionFactory.openSession()) {
                
    }
    
    • 1
    • 2
    • 3
    • 4

    XML映射语句

    基于XML映射语句,便可满足sqlsession的调用

    例如简单的select语句

    
    DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="TestMapper">
        <select id="selectEmployee" resultType="com.test.entity.Employee">
            select * from employee where id = #{id}
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    参数符号:#{id},这就告诉 MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个?来标识,并被传递到一个新的预处理语句中.

    MybatisUtil工具类

    创建MybatisUtil工具类,以便集中创建SqlSession

    public class MybatisUtil {
        private static SqlSessionFactory sqlSessionFactory;
        static {
            try {
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream("mybatis-config.xml"));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取一个新的会话
         * @param autoCommit 是否开启自动提交(跟JDBC是一样的,如果不自动提交,则会变成事务操作)
         * @return SqlSession对象
         */
        public static SqlSession getSession(boolean autoCommit){
            return sqlSessionFactory.openSession(autoCommit);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    方便使用:

    public class Main {
        public static void main(String[] args) throws FileNotFoundException {
            try(SqlSession session = MybatisUtil.getSession(true)) {
                TestMapper mapper = session.getMapper(TestMapper.class);
                
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    映射器接口

    映射器是一些用来绑定映射语句的接口。创建com.test.mapper包,创建接口TestMapper。

    这样做好处是将映射器的结果快速转换为需要的实体类,通过XML中的namespace绑定接口

    • 全限定名(比如 “com.test.mapper.TestMapper")将被直接用于查找及使用。
    <mapper namespace="com.test.mapper.TestMapper">
        <select id="selectEmployee" resultType="com.test.entity.Employee">
            select * from employee
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    将接口与XML放在同包资源中,修改mybatis-config.xml文件

        <mappers>
            <mapper resource="com/test/mapper/TestMapper.xml"/>
        mappers>
    
    • 1
    • 2
    • 3

    XML配置属性typeAliases

    typeAliases,类型别名,简化Mapper的缩写

    <configuration>
        <typeAliases>
            <typeAlias type="com.test.entity.Employee" alias="Employee"/>
        typeAliases>
        <environments default="development">
            ........
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    或直接扫描包,默认别名为小写的开头

        <typeAliases>
            <package name="com.test.entity"/>
        typeAliases>
    
    • 1
    • 2
    • 3

    也可写注解来指定别名

    @Alias("employee")
    public class Employee {
        ...
    }
    
    • 1
    • 2
    • 3
    • 4

    ResultMap

    当类中定义的字段与数据库的字段不统一时,用ResultMap来映射到对应的实体

    column:数据库中的字段,property:java中编写的字段名称

    <mapper namespace="com.test.mapper.TestMapper">
        <resultMap id="Test" type="Employee">
            <result column="id" property="xxx"/>
        resultMap>
        <select id="selectEmployee" resultMap="Test">
            ....
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    多个构造器如何选择

    当有多个构造器,优先选择满足全部字段的构造器,如果没有则报错

    在这里插入图片描述

    如果只有一个构造器,不管满足几个字段,都会调用

    那如果就是没有满足的呢?那就要用constructor

    <mapper namespace="com.test.mapper.TestMapper">
        <resultMap id="Test" type="Employee">
            <constructor>
                <arg column="id" javaType="Integer"/>
                <arg column="name" javaType="String"/>
            constructor>
          	<result column="id" property="xxx"/>
        resultMap>
            .......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注意:这样写的话,构造器中形参需要对象类型,比如如果用int不用Interger,会报错:

    在这里插入图片描述

    另外,这样写的话构造器中的字段并不会映射到,因为映射器已经交给构造器来处理了。

    bug:绑定错误

    Exception in thread “main” org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

    解决:select中id与接口绑定错误,记得要一致

    在这里插入图片描述

    在这里插入图片描述

    增删改查

    创建接口实例

    TestMapper mapper = session.getMapper(TestMapper.class);
    
    • 1

    TestMapper.xml中编写sql语句

    <mapper namespace="com.test.mapper.TestMapper">
        <select id="getEmployeeId" resultType="Employee">
            select * from employee where id = #{id}
        select>
    
        <insert id="addEmployee" parameterType="Employee">
            insert into employee (id, name, sex, age, salary) values (#{id}, #{name}, #{sex}, #{age}, #{salary})
        insert>
    
        <delete id="deleteEmployee">
            delete from employee where id = #{id}
        delete>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    通过接口实例直接调用接口中的方法

    update操作

    mapper:

    <update id="updateStudent" parameterType="student">
    	update student set sex = #{sex} where sid = #{sid}
    update>
    
    • 1
    • 2
    • 3

    mapper接口:

    int updateStudent(Student student);
    
    • 1

    测试:

    Student student = mapper.getStudentSid(1940618805);
    student.setSex("女");
    mapper.updateStudent(student);
    
    • 1
    • 2
    • 3

    复杂查询

    多表查询一对多:例如查询一个老师教多少个学生

    collection:用于一对多关系,即实体里面放集合

    <resultMap id="asTeacher" type="Teacher">
            <id column="tid" property="tid"/>
            <result column="tname" property="name"/>
            <collection property="studentList" ofType="Student">
                <id property="sid" column="sid"/>
                <result column="name" property="name"/>
                <result column="sex" property="sex"/>
            collection>
    resultMap>  
        <select id="getTeacherByTid" resultMap="asTeacher">
            select *, teacher.name as tname from student inner join teach on student.sid = teach.sid
                                            inner join teacher on teacher.tid where teach.tid = #{tid}
        select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    id用于唯一标识一个老师

    多表查询多对一:如果要求学生类增加一个老师属性,即根据教师id来找教师的所有学生

    association:用于多对一关系

        <resultMap id="test2" type="Student">
            <id column="sid" property="sid"/>
            <result column="name" property="name"/>
            <result column="sex" property="sex"/>
            <association property="teacher" javaType="Teacher">
                <id column="tid" property="tid"/>
                <result column="tname" property="name"/>
            association>
        resultMap>
    
        <select id="selectStudent" resultMap="test2">
            select *, teacher.name as tname from student left join teach on student.sid = teach.sid
                left join teacher on teach.tid = teacher.tid where teach.tid = #{tid}
        select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    事务处理

    自动提交关闭:

    SqlSession session = MybatisUtil.getSession(false)
    
    • 1

    手动提交

    session.commit();
    
    • 1

    动态SQL

    if

    test 为if的条件,如果为真,则拼接if标签里面的and语句,如查询学号是偶数的男同学

        <select id="getStudentSid" resultType="student">
            select * from student where sid = #{sid}
            <if test="sid % 2 == 0">
                and sex = '男'
            if>
        select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    choose,when,otherwise

    类似于switch,case,default

    <select>
        <choose>
        	<when test = "">
            	and...
        	when>
        	<when test = "">
            	and...
        	when>
        
        	<otherwise>
            	and...
        	otherwise>
    	choose>
    select>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    where

    where元素只会在有子元素有返回的情况下才会插入where,并且如果子元素的开头是and,or,(显然sql语句中 where and…是语法错误的吧),where元素会将他们去除

            select * from student
            <where>
                <if test="sid != null">
    
                    sid = #{sid}
                if>
            where>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    set
        <update id="updateStudent">
            update student
            <set>
                <if test="name != null">
                    name = #{name}
                if>
            set>
            where sid = #{sid}
        update>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    缓存机制

    查询出的对象存入SqlSession的一级缓存,如果后续有相同操作,则之间从缓存中获取,不需要重新构造一个对象,提高效率

    Student student = mapper.getStudentSid(1940618805);
    Student student1 = mapper.getStudentSid(1940618805);
    System.out.println(student1 == student); // true
    
    • 1
    • 2
    • 3

    但是,如果在该两次相同的操作中,对数据库内容进行了增,删,改操作,则会清除缓存

    并且,每一个SqlSession会话的一级缓存是分开来的。即在不同的会话下,上述操作得到的不是同一个对象

    一级缓存也叫做本地缓存,它只对一个会话的数据进行缓存。

    要使用全局的缓存,是需要开启二级缓存。只需要在mapper文件中加上

    <cache/>
    
    • 1
    <cache
      eviction="FIFO"
      flushInterval="60000"
      size="512"
      readOnly="true"/>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

    开启二级缓存后,不同会话中连续的相同操作也会直接从缓存中获取对象。(同样的,如果在这两次操作之间对数据库内容进行了增,删,改操作,则会清除缓存)

    但是,多个会话时,只有当一个会话结束了才会将数据从一级缓存写入二级缓存:

    public class Main {
        public static void main(String[] args) throws FileNotFoundException {
            try(SqlSession session1 = MybatisUtil.getSession(true);
                SqlSession session2 = MybatisUtil.getSession(true)) {
                TestMapper mapper1 = session1.getMapper(TestMapper.class);
                TestMapper mapper2 = session2.getMapper(TestMapper.class);
    
                Student student1 = mapper1.getStudentSid(1940618806);
                Student student2 = mapper2.getStudentSid(1940618806);
                System.out.println(student1 == student2); // false 获取对象时会话未结束,二级缓存没有内容
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    注解开发

    去除mapper xml文件,直接在mapper接口方法上写上注解

    @Insert("insert into student(sid, name, sex) values(#{sid}, #{name}, #{sex})")
    int addStudent(Student student);
    
    • 1
    • 2

    使用注解开发需要修改mybatis配置文件的mappers

        <mappers>
            <mapper class="com.test.mapper.TestMapper"/>
    
        mappers>
    
    • 1
    • 2
    • 3
    • 4

    自定义映射规则

    @Results
        @Results({
                @Result(id = true, column = "sid", property = "sid"),
                @Result(column = "name", property = "name"),
                @Result(column = "sex", property = "sex")
        })
        @Select("select * from student where sid = #{sid}")
        Student getStudentSid(int sid);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    一对多查询,如一个教师教的学生

    @Many(select="")

    这个就是一对多关系中联结查询的条件

        @Results(value = {
                @Result(column = "tid", property = "tid"),
                @Result(column = "name", property = "name"),
                @Result(column = "tid", property = "studentList", many =
                @Many(select = "getStudentByTid"))
        })
        @Select("select * from teacher where tid = #{tid}")
        Teacher getTeacherByTid(int tid);
    
        @Select("select * from student inner join teach on student.sid = teach.sid where tid = #{tid}")
        List<Student> getStudentByTid(int tid);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    @Param()

    当select语句有多个参数条件时,使用@Param()

        @Select("select * from student where sid = #{sid} and sex = #{sex}")
        Student getStudentBySidAndSex(@Param("sid")int sid, @Param("sex")String sex);
    
    • 1
    • 2

    否则报错

    ### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'sid' not found. Available parameters are [arg1, arg0, param1, param2]
    
    • 1

    这是因为多个参数时,mybatis并不知道这些属性是哪里来的,“Parameter xxx not found”

  • 相关阅读:
    Java实战:Spring Boot自动配置原理与定制
    SQL生成自然数,日历序列 浅析
    牛客网专项练习30天Pytnon篇第16天
    计算机毕业设计springboot+vue基本微信小程序的码高教育课后在线小程序
    着色器(Shader)
    1-Docker基础
    构建系列之webpack窥探上
    分组卷积/转置卷积/空洞卷积/反卷积/可变形卷积/深度可分离卷积/DW卷积/Ghost卷积/
    docker之传染病可视化系统
    A-LOAM学习
  • 原文地址:https://blog.csdn.net/weixin_63301726/article/details/133011913