MyBatis

MyBatis

1、简介

1.1、什么是MyBatis

  • MyBatis 是一款优秀的持久层框架
  • 它支持自定义 SQL,存储过程及高级映射。
  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
  • MyBatis 可以通过简单的 XML 注解来配置和映射原始类型,接口和 Java POJO 为数据库中的记录

1.2、MyBatis 使用

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

GitHub:https://github.com/mybatis/mybatis-3/releases

中文文档:https://mybatis.org/mybatis-3/zh/index.html

1.3、MyBatis 优点

  • 简单易学,灵活
  • SQL 和代码的分离,提高了可维护性
  • 提供了映射标签,支持对象与数据库的 ORM 字段关系映射
  • 提供对象关系映射标签,支持对象关系组建维护
  • 提供 xml 标签,支持编写动态 SQL

2、MyBatis 项目搭建

2.1、创建 maven 项目并导入 依赖

<!-- 导入依赖 -->
<dependencies>
    <!-- mysql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.17</version>
    </dependency>
    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.5</version>
    </dependency>
    <!-- junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

2.2、创建一个模块

  • 编写 MyBatis 核心配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PULic "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <!-- configuration 核心配置文件 -->
    <configuration>
    
        <environments>
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;serverTime=Asia/shanghai&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                    <property name="username" value="username"/>
                    <property name="password" value="password"/>
                </dataSource>
            </environment>
        </environments>
    
        <!-- 每个 mapper.xml 都需要在 MyBatis 核心配置文件中注册 -->
        <mappers>
            <mapper resource="com/dao/UserMapper.xml"/>
        </mappers>
        
    </configuration>
    
  • 编写 MyBatis 工具类

    / sqlSessionFactory --> sqlSession
        public class MybatisUtil {
    
            private static SqlSessionFactory sqlSessionFactory;
    
            static {
                try {
                    // 使用 MyBatis,获取 SqlSessionFactory
                    String resource = "mybatis-config.xml";
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    Configuration config;
                    sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            public static SqlSession getSqlSession() {
                return sqlSessionFactory.openSession();
            }
    
        }
    
    

2.3、编写代码

  • DAO 接口

    package com.dao;
    
    import com.pojo.User;
    
    import java.util.List;
    
    public interface UserDao {
        List<User> getUserList();
    }
    
    
  • 接口 mapper 配置文件

    <?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 接口 -->
    <mapper namespace="com.dao.UserDao">
        <!-- 执行 select 语句 -->
        <select id="getUserList" resultType="com.pojo.User">
            SELECT * FROM mybatis.user;
        </select>
    </mapper>
    

3、CRUD

1、namespace

namespace 中的包名要与 Dao、mapper 接口的包名一致

2、select

选择,查询语句

3、update

4、delete

增删改需要提交事务

使用 Map 传递参数

使用 map 传递参数,直接在 SQL 中取出 key 即可,parameterType=“map”

对象传递参数,直接在 SQL 中取对象的属性即可

只有一个基本参数类型的情况下,可以直接在 SQL 中取到

多个参数用 Map 或者注解

4、配置解析

1、核心配置文件

mybatis-config.xml

configuration(配置)
properties(属性)
setting(设置)
typeAliases(类型别名)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)

2、环境变量(environments)

mybatis 可以配置多个环境变量,但每个 SqlSessionFactory 只能使用一个

MyBatis 默认事务管理是 JDBC,连接池:DBCP

3、属性(properties)

我们可以通过 properties 属性来实现引用配置文件

这些属性是可外部配置并且可以动态替换的,可以在 Java 文件中配置,也可通过 properties元素的子元素来传递 db.properties

编写配置文件

db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;serverTime=Asia/shanghai&amp;useUnicode=true&amp;characterEncoding=UTF-8
username=root
password=123456

在核心配置文件中引入

<properties resource="db.properties"/>
<!-- 也可以在其中增加属性配置,默认优先选择外部引入的配置文件 -->
<properties resource="db.properties">
	<properties name="username" value="root"/>
</properties>

4、类型别名(typeAliasses)

类型别名是为 java 类型设置一个短的名字,只和 xml 配置文件相关,用于减少类完全限定名的冗余

<!-- 可以用来个实体类起别名 -->
<typeAliases>
    <typeAlias type="com.pojo.User" alias="User"/>
	<!-- 扫描 package 下的包,默认别名为类的类名,首字母小写 -->
    <package name="com.pojo"/>
</typeAliases>

或者使用 @Alias(“别名”) 注解起别名

@Alias("user")
public class User{
    
}

5、设置(settings)

一个配置完整的 settings 元素示例:

<settings>
	<setting name="cacheEnabled" value="true"/>
    <setting name="lazyLoadingEnable" value="true"/>
    <setting name="multipleResultSetsEnabled" value="true"/>
    <setting name="userColumnLabel" value="true"/>
    <setting name="userGeneratedKeys" value="false"/>
    <setting name="autoMappingUnknownColumnBehavior" value="PARTIAL"/>
    <setting name="defaultExecutoType" value="SIMPLE"/>
    <setting name="defaultFetchSize" value="100"/>
    <setting name="defaultStatementTimout" value="25"/>
    <setting name="safeRowBoundsEnabled" value="false"/>
    <setting name="mapUnderscoreToCameCase" value="false"/>
    <setting name="localCacheScope" value="SESSION"/>
    <setting name="jdbcTypeForNull" value="OTHER"/>
    <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

6、其他配置

  • typeHndlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins插件
    • mybatis-generator-core
    • mybatis-plus
    • 通用 mapper

7、映射器(mappers)

  • 使用 resource 绑定

    <mappers>
    	<mapper resource="com/dao/UserMapper.xml"/>
    </mappers>
    
  • 使用 class 文件绑定注册

    • 接口与 mapper 配置文件必须同名
    • 接口与 mapper 文件必须位于统一个包下
    <mappers>
    	<mapper class="com.dao.UserMapper"/>
    </mappers>
    
  • 使用扫描包进行注册

    <mappers>
    	<mapper package="com.dao"/>
    </mappers>
    

8、生命周期和作用域

SqlSessionFactoryBuild

  • 用来创建 SqlSessionFactory,局部变量,创建完毕后没用

SqlsessionFactory

  • 创建后在应用的运行期间内应该一直存在!
  • 最简单的是使用单例模式或者静态单例模式

SqlSession

  • 连接到连接池的每个请求,使用完后需要关闭。作用域最好在方法内

5、表中字段与 bean 中字段不匹配解决方案

1、起别名,查询字段别名与实体类中字段一致

2、resultMap 结果集映射

<resultMap id="userMap" type="User">
    <!-- column 数据库中的字段,映射到实体类字段 -->
	<result column="id" property="userId" />
</resultMap>
  • resultMap 设计思想:对于简单的语句不需要配置显式的结果映射,对于复杂的语句只需要描述它们的关系就可以了

6、日志

6.1、日志工厂

logimpl:指定 mybatis 所用日志的具体实现,未指定时将自动查找

  • SLF4J
  • LOG4J
  • LOG4J2
  • jdk_logging
  • commons_logging
  • no_logging

在 mybatis 核心配置文件中配置日志

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

6.2、LOG4J

  1. 导入 LOG4J 的包

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. 通过配置文件 log4j.properties 进行配置

# priority  :debug<info<warn<error

#you cannot specify every priority with different file for log4j 

log4j.rootLogger=debug,stdout,info,debug,warn,error 
 
#console

log4j.appender.stdout=org.apache.log4j.ConsoleAppender 

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 

log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n

#info log

log4j.logger.info=info

log4j.appender.info=org.apache.log4j.DailyRollingFileAppender 

log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'

log4j.appender.info.File=./src/com/hp/log/info.log

log4j.appender.info.Append=true

log4j.appender.info.Threshold=INFO

log4j.appender.info.layout=org.apache.log4j.PatternLayout 

log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

#debug log

log4j.logger.debug=debug

log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender 

log4j.appender.debug.DatePattern='_'yyyy-MM-dd'.log'

log4j.appender.debug.File=./src/com/hp/log/debug.log

log4j.appender.debug.Append=true

log4j.appender.debug.Threshold=DEBUG

log4j.appender.debug.layout=org.apache.log4j.PatternLayout 

log4j.appender.debug.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

#warn log

log4j.logger.warn=warn

log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender 

log4j.appender.warn.DatePattern='_'yyyy-MM-dd'.log'

log4j.appender.warn.File=./src/com/hp/log/warn.log

log4j.appender.warn.Append=true

log4j.appender.warn.Threshold=WARN

log4j.appender.warn.layout=org.apache.log4j.PatternLayout 

log4j.appender.warn.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

#error

log4j.logger.error=error

log4j.appender.error = org.apache.log4j.DailyRollingFileAppender

log4j.appender.error.DatePattern='_'yyyy-MM-dd'.log'

log4j.appender.error.File = ./src/com/hp/log/error.log 

log4j.appender.error.Append = true

log4j.appender.error.Threshold = ERROR 

log4j.appender.error.layout = org.apache.log4j.PatternLayout

log4j.appender.error.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

7、分页

分页是为了减少数据的处理量

使用 limit 进行分页

SELECT * FROM user LIMIT startIndex,pageSize;
SELECT * FROM user LIMIT #{startIndex}, #{pageSize}

RowBounds 分页

不再使用 SQL 进行分页,数据全部查询后在 java 代码中进行分页

分页插件分页

mybatis 分页插件:pageHelper

8、使用注解开发

  1. 注解直接在接口上实现

    @Select("select * from user")
    List<User> getUsers();
    
  2. 在核心配置文件中绑定接口

    <mappers>
    	<mapper class="com.dao.UserMapper" />
    <mappers>
    

9、MyBatis 执行流程

  1. Resource 获取加载全局配置文件
  2. 实例化 SqlSessionFactoryBuild 构造器
  3. 解析配置文件流 XMLConfigBuild
  4. Configuration 读取所有配置信息
  5. SqlSessionFactory 实例化
  6. transaction 事务管理
  7. 创建 executor 执行器
  8. 创建 SqlSession
  9. 执行 CRUD 后查看是否成功,不成功事务回滚
  10. 成功提交事务
  11. 关闭

10、多对一处理

多个学生对应一个老师

按照查询嵌套处理:子查询

<resultMap id="StudentTeacher" type="Student">
	<result property="id" column="id"/>
    <result property="name" column="name"/>
    <!-- 对象:association 集合:collection -->
    <accociation property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<!-- 查询学生信息 -->
<select id="getStudent" resultMap="StudentTeacher">
	select * from student
</select>
<!-- 查询教师信息 -->
<select id="getTeacher" resultType="Teacher">
	select * from teacher where id = #{id}
</select>

按照结果嵌套处理:联表查询

<select id="getStudentAndTeacher" resultMap="StudentTeacher">
	select s.id sid, s.name sname, t,name tname from student s, teacher t where s.tid = t.id
</select>

<resultMap id="StudentTeacher" type="student">
	<result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="teacher" javaType="Teacher">
    	<result property="name" column="tname"/>
    </association>
</resultMap>

11、一对多处理

一个老师对应多个学生

按结果嵌套查询

<select id="getTeacher" resultMap="StudentTeacher">
	select s.id sid, s.name sname, t.name tname, t.id tid from student s, teacher t where s.tid = t.id and t.id = #{tid}
</select>
<resultMap id="StudentTeacher" type="Teacher">
	<result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!-- 集合中的泛型,使用 ofType 获取 -->
    <collection property="student" ofType="Student">
    	<result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>

按照条件嵌套查询

<!-- 查询学生信息 -->
<select id="getStudentByTeacher" resultMap="StudentTeacher">
	select * from student where tid = #{tid}
</select>
<resultMap id="StudentTeacher" type="Teacher">
	<collection property="student" javaType="ArrayList" ofType="Student" select="getStudentByTeacher" column="id"/>
</resultMap>
  • 关联:association 多对一
  • 集合:collection 一对多
  • javaType:用来指定实体类中属性的类型
  • ofType:泛型中的约束类型

12、动态 SQL

**if **

<select id="queryBlogIf" paramterType="map" resultType="Blog">
	select * from mybatis.blog where 1=1
    <if test="title != null">
    	and title = #{title}
    </if>
    <if test="author != null">
    	and author = #{author}
    </if>
</select>

where

<select id="queryBlogIf" paramterType="map" resultType="Blog">
    select * from mybatis.blog 
    <where>
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </where>
</select>

where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才会去插入 where 子句,语句的开头为 and 或者 or,where 会去除它

set

<update id="updateBlog" param

choose(when,otherwise)

<select id="queryBlogIf" paramterType="map" resultType="Blog">
    select * from mybatis.blog
    <where>
        <!-- 类似 switch -->
        <choose>
            <when test="title != null">
                and title = #{title}
            </when>
            <otherwise>
                and views = #{views}
            </otherwise>
        </choose>
    </where>
</select>

trim(where,set)

set:动态前置 set 关键字,也会删除无关的逗号

<update id="updateBlog" paramType="map">
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </set>
    where id = #{id}
</update>

trim:

  • prefix:给 SQL 语句拼接的前缀
  • suffix:给 SQL 语句拼接的后缀
  • prefixOverrides:去掉 SQL 语句前面的关键字或字符,关键字由 prefixOverrides 属性指定,并且插入 prefix 属性中指定的内容
  • suffixOverrides:去除 SQL 语句后面的关键字或字符,关键字由 suffixOverrides 指定
<trim prefix="where" prefixOverrides="AND |OR ">
  ...
</trim>

SQL 片段

抽取公共部分

<sql id="if-title">
	<if test="title != null">
    	title = #{title}
    </if>
</sql>

在需要使用的地方使用 include 标签引用

<select id="queryBlogIf" paramterType="map" resultType="Blog">
	select * from mybatis.blog
    <where>
    	<include refid="if-title"></include>
    </where>
</select>

foreach

<select id="queryBlogForeach" paramterType="map" resultType="Blog">
	select * from mybatis.blog
    <where>
    	<foreach collection="ids" item="id" open="and (" close=")" separator="or">
        	id = #{id}
        </foreach>
    </where>
</select>

13、MyBatis 缓存

MyBatis 默认定义了两极缓存:一级缓存二级缓存

  • MyBatis 定义了缓存接口 Cache,可以通过实现 Cache 接口来自定义二级缓存
  • 映射语句文件中的所有 select 语句的结果将被缓存,所有增删改语句会刷新缓存

一级缓存

  • 默认情况下开启一级缓存,sqlSession 级别的缓存,本地缓存
  • 本地缓存,默认开启,一次 SqlSession 有效,打开连接到关闭连接
    • 与数据库同一次会话期间查询到的数据会放在本地缓存中
    • 以后需要获取相同数据,从缓存中获取,不查询数据库

缓存失效的情况

  • 增删改可能会改变原来的数据,会刷新缓存
  • 查询不同的东西
  • 查询不同的 mapper.xml
  • 手动清理缓存 sqlSession.cleaarCache();

二级缓存

  • 二级缓存需要手动开启,基于 namespace 级别的缓存
  • 工作机制
    • 一个会话查询一条数据,数据放在会话的一级缓存中
    • 会话关闭后,一级缓存消失,数据保存到二级缓存中
    • 新的会话查询信息,从二级缓存中获取内容
    • 不同 mapper 查询出来的数据放在不同的 mapper.xml 中
  1. 开启全局缓存

    <settings>
    	<setting name="cacheEnable"/>
    </settings>
    
  2. 在 mapper.xml 中开启二级缓存

<cache />
<!-- 自定义参数 -->
<cache eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true">
上一篇:LOG4J的配置文件


下一篇:Elasticsearch入门Demo(一)