暂无图片
暂无图片
2
暂无图片
暂无图片
暂无图片

MyBatis的使用详解

一叶扁舟 2020-11-03
2933

大纲

一、Hello MyBatis

1、HelloMyBatis

1.1、导入依赖

<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version> </dependency>

1.2、从 XML 中构建 SqlSessionFactory 与 从SqlSessionFactory中获取 SqlSession

SqlSessionFactoryBuilder 可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例

  • 编写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> <!--开发环境test/dev/prod 可配置多套环境 default代表选择的环境--> <environments default="development"> <environment id="development"> <!--MyBatis事务管理有两种:JDBC/MANAGED,默认的事务管理,是使用的JDBC的事务管理,可以直接使用JDBC的提交和回滚设置--> <transactionManager type="JDBC"/> <!--MyBatis默认的连接池是POOLED--> <dataSource type="POOLED"> <!--驱动、url、username、password--> <property name="driver" value=""/> <property name="url" value=""/> <property name="username" value=""/> <property name="password" value=""/> </dataSource> </environment> </environments> <!--每一个xml文件都需要在mybatis核心配置文件中注册--> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>
  • 编写工具类(构建实例)
public class MyBatisUtils { private static SqlSessionFactory sqlSessionFactory; static{ try { // SqlSessionFactoryBuilder根据mybatis-config.xml文件创建sqlSessionFactory对象 String resource = "org/mybatis/example/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } // sqlSessionFactory对象调用openSession()方法创建sqlSession public static SqlSession getSqlSession(){ // 也可以调用有参的openSession(true),代表自动提交事务,如下无参的需要手动提交事务 SqlSession sqlSession = sqlSessionFactory.openSession(); // SqlSession 包含了执行sql所需的所有方法 return sqlSession; } }

1.3、SqlSession执行sql

  • 实体类(略)

  • Dao接口(略)

  • 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" > <!--namespac命名空间,指定了DAO接口,将xml配置文件与DAO绑定起来了,相当于实现了这个接口--> <mapper namespace="com.enmo.nimbus.base.dao.BaseDAO" > <!--id对应方法名, 相当于重写该方法--> <select id="selectByParam" resultType="com.yang.test.User"> select count(*) from cs_user </select> </mapper>
  • 使用

    private MyBatisUtils myBatisUtils; // 获取SqlSession SqlSession sqlSession = myBatisUtils.getSqlSession(); // 执行sql UserDao userDao = sqlSession.getMapper(UserDao.calss); List<User> userList = userDao.selectByParam(); // 关闭SqlSession sqlSession.close();

2、MyBatis配置文件详解

【功能】

  • 配置多个环境「environments
  • 数据库配置引用外部配置文件「properties
  • 设置缓存、驼峰名、日志具体实现「settings
  • 实体类起别名「typeAliases
  • 注册Mapper文件「mappers

2.1、环境配置(environments

  • environments

    可以配置多个环境,并指定所需环境

  • transactionManager

    MyBatis事务管理有两种:JDBC/MANAGED,默认的事务管理,是使用的JDBC的事务管理,可以直接使用JDBC的提交和回滚设置

  • dataSource

    有三种数据源:UNPOOLED(没有连接池)、POOLED(有连接池)、JNDI。MyBatis默认使用POOLED

    【连接池的作用】避免了创建新的连接实例时所必需的初始化和认证时间。

2.2、属性(properties)

​ 引入外部配置文件

  • 编写配置文件

    driver=com.msql.jdbc.Driver
    url=
    username=
    password=
    
  • 在核心配置文件中引入

    <!--放在第1位--> <properties resource="db.properties"/> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!--可以直接饮用文件中变量:驱动、url、username、password--> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
  • 补充:也可以配置文件和properties标签都写配置

    <!--如果配置文件中也有password等属性,则会优先引用配置文件中的,这里写的无效--> <properties resource="db.properties"> <property name="username" value="yang"/> <property name="password" value="123456"/> </properties>

2.3、设置(settings)

<settings> <!--功能一:二级缓存,开启全局缓存(全局缓存默认是开启的,也可以在这写上)--> <setting name="cacheEabled" value="true"/> <!--开启自动驼峰命名--> <serting name="mapUnderscoreToCamelCase" value=true/> <!--指定日志具体实现--> <serting name="logImpl" value="LOG4J"/> </settings>

2.4、别名(typeAliases)

用于减少mapper中实体类名完全限定的冗余

<!--放在第3位--> <typeAliases> <!--方式一:直接指定别名--> <typeAlias alias="User" type="com.yang.project.user.model.User"/> <!--方式二:直接指定一个包,这个包下的别名默认为首字母小写的类名(user)--> <package name="com.yang.project.user.model"/> </typeAliases>
// 方式二限定后可以使用注解自定义指定别名 @Alias("User") public class User{}

第一种可以自定义指定别名,第二种自动使用默认,如果要自定义需要使用注解在类上指定

2.5、映射器(mappers)

注册绑定Mapper文件(不绑定会报错:not know to the MapperRegisry)

  • 四种方式(用第一种就行了)

    <mappers> <!-- 使用相对于类路径的资源引用 【推荐】--> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <!-- 使用映射器接口实现类的完全限定类名 【DAO接口和Mapper.xml文件必须在同一包下】--> <mapper class="org.mybatis.builder.AuthorMapper"/> <!-- 将包内的映射器接口实现全部注册为映射器 【DAO接口和Mapper.xml文件必须在同一包下】--> <package name="org.mybatis.builder"/> <!-- 使用完全限定资源定位符(URL) 【这种不用】--> <mapper url="file:///var/mappers/AuthorMapper.xml"/> </mappers>

二、SpringBoot整合MyBatis

1、依赖配置

1.1、导入依赖

  • posthresql驱动依赖

    <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.2.5</version> <scope>runtime</scope> </dependency>
  • 连接池依赖

    <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency>
  • mybatis依赖

    <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>

1.2、配置文件

注意:一定不要忘了在type-aliases-package:配置所有实例类所在目录

mybatis: mapper-locations: classpath*:mybatis/*.xml # 配置映射文件位置(映射器) executor-type: simple config-location: classpath:mybatis-config.xml type-aliases-package: com.example.firstspringboot.mybatistest.model # 配置实体类位置 configuration: map-underscore-to-camel-case: true # 配置驼峰 # 指定连接数据库的信息 spring: datasource: url: jdbc:postgresql://192.168.X.XXX:5432/emcs # 指定驱动 driver-class-name: org.postgresql.Driver username: password: type: com.alibaba.druid.pool.DruidDataSource hikari: login-timeout: 30 connection-timeout: 10000 idle-timeout: 600000 max-lifetime: 1800000 connection-test-query: select 1

2、项目使用流程

2.1、准备实体类(model)

@Data public class ATest { private String t; private String u; private String v; }

2.2、创建接口并配置扫描(dao)

public interface TestDao { ATest queryUByT(@Param("t") String t); }

2.3、配置映射文件

<?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="com.example.firstspringboot.dao.TestDao"> <select id="queryUByT" resultType="com.example.firstspringboot.mybatistest.ATest"> select t, u, v from a_test where t = #{t} </select> </mapper>

2.4、扫描映射文件

@MapperScan(basePackages = "com.example.firstspringboot.dao") public class FirstSpringbootApplication { public static void main(String[] args) { SpringApplication.run(FirstSpringbootApplication.class, args); } }

2.5、使用

  • 原生mybatis使用

    SqlSession sqlSession = MybatisUtils.getSqlSession(); TestDao testDao = sqlSession.getMapper(TestDao.class); ATest aTest = testDao.queryUByT(t); // 增删改需要提交事务 // sqlSession.commit(); sqlSession.close();
  • springboot中的使用

    private TestDao testDao; ATest aTest = testDao.queryUByT(t)

3、注解方式整合Mybatis

3.1、原理

​ 底层使用反射:通过实体类反射解析出方法及方法上注解的value。

本质:反射机制

底层:动态代理

3.2、方法

@Insert("") @Delete("") @Update("") @Select("") @InsertProvider("") @DeleteProvider("") @UpdateProvider("") @SelectProvider("")

3.3、使用场景

两个方法可以同时存在,复杂的sql用xml,简单的sql用注解

三、MyBatis原理

1、生命周期和作用域

生命周期和作用域是至关重要的,因为错误的使用会导致并发问题

image.png

  • SqlSessionFactotyBuilder

    • 【特点】:一旦创建了SqlSessionFactory,就不再需要
    • 【作用域】:局部方法变量
  • SqlSessionFactory

    • 可以想象为数据库连接池(创建、回收再利用Sqlsession)
    • 【特点】:一旦被创建运行期间一直存在没有任何理由丢弃它或创建另一个实例(相当于就有一个连接池,多个的话浪费资源)
    • 【作用域】:全局变量(最简单的是使用单例模式或者静态单例模式,保证全局只有一个SqlSessionFactory)
  • SqlSession

    • 相当于连接到连接池的一个请求
    • 【特点】:创建使用后需要关闭,否则占用资源
    • 【作用域】:局部方法变量(实例线程是不安全的,所以是不能被共享的)

四、MyBatis标签

1、CURD

1.1、Select

<select id="dao层的方法名" resultType="sql语句返回值类型" parameterType="参数类型,区别重载"> </select>
  • 模糊查询

    select ... from table where name like concat('%', #{search}, '%')

1.2、Insert

<insert id="dao层的方法名" parameterType="参数类型,区别重载"></insert>

1.3、Update

<update id="dao层的方法名" parameterType="参数类型,区别重载"></update>

1.4、Delete

<delete id="dao层的方法名" parameterType="参数类型,区别重载"></delete>

补充、万能Map

  • dao

    User findByParam(Map<String, Object> map)
  • xml

    <select id="findByParam" parameterType="map"> select ... from table where id = #{a} and name = #{b} </select>
  • service

    private TestDao testDao; Map<String, Object> map = new HashMap<String, Object>() map.put("id", 1); map.put("name", "yang") ATest aTest = testDao.findByParam(map)

2、ResultMap结果集映射

2.1、起别名

MyBatis会根据数据库字段在幕后创建一个ResultMap,

实体类的属性名映射到JavaBean的属性上,

两者进行相互匹配,如果字段名与属性名不能精确匹配,

那么有两种方式解决:

  • 1、sql起别名,这样别名将作为字段名,自动创建为ResultMap

    <select id="getUserById" resultType="User"> select id, name, pwd password from cs_user </select>
  • 2、自定义ResultMap

    <resultMap id="UserMap" resultType="User"> <!-- column:数据库字段,property:model中字段 --> <result column="pwd" property="password"/> </resultMap> <select id="getUserById" resultMap="UserMap"> select id, name, pwd from cs_user </select>

2.2、实现多对一

association:关联(多对一)

  • 实体类

    多个学生对应一个教师

    • student

      @Data @AllArgsConstructor @NoArgsConstructorpublic  class Student {     private int id;     private String name;     private Teacher teacher; }
    • teacher

      @Data @AllArgsConstructor @NoArgsConstructor public class Teacher { private int id; private String name; }
  • xml

    • 方式一:按查询嵌套处理

      <mapper namespace="com.msdn.dao.StudentMapper"> <select id="getStudent" resultMap="studentTeacher"> select * from student </select> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{tid} </select> <resultMap id="studentTeacher" type="student"> <id property="id" column="id" /> <result property="name" column="name" /> <association property="teacher" javaType="Teacher" column="tid" select="getTeacher" /> </resultMap> </mapper>
    • 方式二:按结果嵌套处理

      <mapper namespace="com.msdn.dao.StudentMapper"> <select id="getStudent2" resultMap="studentMap"> select s.id sid,s.name sname,t.id ttid,t.name tname from student s,teacher t where s.tid=t.id;     </select> <resultMap id="studentMap" type="student"> <id property="id" column="sid" /> <result property="name" column="sname" /> <association property="teacher" javaType="Teacher" > <id property="id" column="ttid" /> <result property="name" column="tname" /> </association> </resultMap> </mapper>
  • 执行结果

    Student(id=1, name=张三, teacher=Teacher(id=1, name=hresh)) Student(id=2, name=李四, teacher=Teacher(id=1, name=hresh)) Student(id=3, name=王武, teacher=Teacher(id=1, name=hresh)) Student(id=4, name=张散散, teacher=Teacher(id=1, name=hresh))

2.3、实现一对多

collection:包含(一对多)

  • 实体类

    一个教师对应多个学生

    • student

      @Data @AllArgsConstructor @NoArgsConstructorpublic  class Student { private int id; private String name; private int tid; }
    • teacher

      @Data @AllArgsConstructor @NoArgsConstructorpublic  class Teacher { private int id; private String name; private List<Student> students; }
  • xml

    • 方式一:按照嵌套处理

      <mapper namespace="com.msdn.dao.TeacherMapper"> <select id="getTeacherById2" resultMap="TeacherStudent2"> select * from mybatis.teacher where id = #{id} </select> <select id="getStudents" resultType="Student"> select * from mybatis.student where tid = #{tid} </select> <resultMap id="TeacherStudent2" type="Teacher"> <id property="id" column="id" /> <result property="name" column="name" /> <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudents" /> </resultMap> </mapper>
    • 方式二:按照结果嵌套处理

      <mapper namespace="com.msdn.dao.TeacherMapper"> <select id="getTeacherById" resultMap="TeacherStudent"> select s.id sid,s.name sname,t.id tid,t.name tname from mybatis.student s,mybatis.teacher t where s.tid = t.id and t.id =#{id} </select> <resultMap id="TeacherStudent" type="Teacher"> <id property="id" column="tid" /> <result property="name" column="tname" /> <!--注意List<Student>,用ofType="Student"--> <collection property="students" ofType="Student" > <result property="id" column="sid" /> <result property="name" column="sname" /> <result property="tid" column="tid" /> </collection> </resultMap> </mapper>
  • 执行结果

    Teacher(id=1,  name=hresh,  students=[Student(id=1, name=张三, tid=1),  Student(id=2, name=李四, tid=1),  Student(id=3, name=王武, tid=1),  Student(id=4, name=张散散, tid=1)] )

3、动态SQL

根据不同条件生成不同的SQL

3.1、if

功能:条件判断(可多条件成立)

where state = 'pass' <if test="search != null"> and title = #{search} </if>

3.2、trim(where|set)

  • where

    功能:去掉开头的and/or

    <where> <if test="state != null"> and state = 'pass' </if> <if test="search != null"> and title = #{search} </if> </where>
  • set

    功能:去掉无用逗号

    <set> <if test="name != null"> name = #{name} </if> <if test="age != null"> age = #{age} </if> </set>
  • trim

    功能:定制where和set

    <trim prefix="WHERE" prefixOverrides="AND | OR"> ... </trim>
    <trim prefix="SET" suffixOverrides=","> ... </trim>

3.3、choose|when|otherwise

功能:条件选择(只能单条件成立)

<where> <choose> <when test="type == 'create'">result = #{title}</when> <when test="name != null">and result = #{name}</when> <otherwise>and result = #{views}</otherwise> </choose> </where>

3.4、sql片段

功能:提取sql片段方便复用

  • 提取片段

    <sql id="if-name-age"> <if test="name != null"> name = #{name} </if> <if test="age != null"> age = #{age} </if> </sql>
  • 使用

    <set> <include refid="if-name-age"></include> </set>
  • 注意:不要包含标签

3.5、foreach

功能:对集合遍历循环

<select id="selectUser" resultType="com.yang.User"> select name, age from cs_user where id in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
  • 实例

    • 批量插入

      <!--将List<Malog>批量插入数据库--> <insert id="insert" parameterType="MaLog"> insert into ma_log(finger_print, ip, country) values <foreach collection="list" item="malog" separator=","> (#{malog.fingerPrint}, #{malog.ip}, #{malog.country}) </foreach> </insert>

3.6、script

功能:注解中使用动态sql

@Update("<script> update knowledge_group set sort=sort-1 <where> <if groupId != null>group_id=#{groupId}</if> and sort > #{newSort} </where> </script>") int updateBaseGroupSortMinum(Group group);

五、MyBatis缓存

1、概述

MyBatis中默认定义了两级缓存,默认只开启一级缓存,二级缓存需要手动开启,为了提高扩展性,MyBatis定义了缓存接口Cache, 我们可以通过实现Cache接口来自定义二级缓存。

2、一级缓存

  • 概述

SqlSession级别的缓存,也称本地缓存

  • 开启

默认开启,无需操作,也无法关闭

  • 缓存作用域
SqlSession sqlSession = MybatisUtils.getSqlSession(); //...在sqlSession开启到关闭之间的查询会被缓存 sqlSession.close();

全过程:创建连接池,创建连接,进行sql操作(缓存作用域),关闭连接,将连接放回连接池。

  • 缓存失效因素

    • select不同数据

    • insert、update、delete会刷新缓存

    • 查询不同的mapper

    • 手动清除缓存

      sqlSession.claerCache(); // 手动清理缓存

3、二级缓存

  • 概述

基于namespace级别的缓存

  • 开启

    <!--全局缓存默认是开启的,也可以在mybatis配置xml中写上--> <settings> <setting name="cacheEabled" value="true"/> </settings> <!--只需要在Mapper.xml文件中加入标签即可--> <cache/>
    <!--可以自定义功能:先进先出,60秒自动刷新一次,最多存512个对象/列表的引用,只读--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> <!--select可以单独控制要不要开启缓存--> <select id="" resultType="" useCache="false"></select> <!--update,delete,insert只能定义刷新缓存--> <update id="" parameterType="" falshCache="false"></update>
    • 清除策略
      • LRU—最近最少使用:移除最长时间不被使用的对象
      • FIFO—先进先出:按对象进入缓存的顺序来移除
      • SOFT—软引用:基于垃圾回收器状态和软引用规则移除对象
      • WEAK—弱引用:更积极的基于垃圾回收器状态和弱引用规则移除对象
  • 缓存作用域

    是基于命名空间namespace的,会在整个Mapper.xml中生效(一般一个会写在一个Mapper.xml中)

    全过程:所有缓存会缓存到一级缓存,当一级缓存结束(sqlSession关闭),会将缓存放入二级缓存,当开启一个新的sqlSession会走二级缓存。

  • 注意:需要将实体类序列化,否则会报错

    Causer by:java.io.NotSerializbleException:com.yang.pojo.User

4、MyBatis缓存原理

MyBatis缓存原理.png

5、MyBatis缓存与redis缓存

在spring整合mybatis之后,会自动管理sqlsession, 也就是说sql执行完后会自动关闭sqlsession,这样一来,一级缓存实际上是没用的,所以需要开启二级缓存,而MyBatis的二级缓存是缓存在服务器上的,占用服务器资源,并且不能做分布式的缓存(只存在当前服务器),所以使用redis代替mybatis的二级缓存,减少服务器资源占用,并能实现分布式缓存。

最后修改时间:2021-08-03 14:29:22
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论