首页 > 技术文章 > MvBatis从入门到精通

zhgc 2020-03-04 10:29 原文

1. 回顾jdbc:

jdbc操作步骤总结如下:

1、 加载数据库驱动

2、 创建并获取数据库链接

3、 创建jdbc statement对象

4、 设置sql语句

5、 设置sql语句中的参数(使用preparedStatement)

6、 通过statement执行sql并获取结果

7、 对sql执行结果进行解析处理

8、 释放资源(resultSet、preparedstatement、connection)

jdbc问题总结如下:

1、 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

2、 Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。

3、 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。

对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。

2. Mybatis框架概述

 

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。 [1]

 

特点:

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
  • 提供映射标签,支持对象与数据库的orm字段关系映射
  • 提供对象关系映射标签,支持对象关系组建维护
  • 提供xml标签,支持编写动态sql [2] 

 

Mybatis入门

Mybatis环境搭建

   <groupId>com.xkrj</groupId>

    <artifactId>mybatisless01</artifactId>

    <version>1.0-SNAPSHOT</version>

    <packaging>jar</packaging>

    <dependencies>

        <dependency>

            <groupId>org.mybatis</groupId>

            <artifactId>mybatis</artifactId>

            <version>3.4.5</version>

        </dependency>

        <dependency>

            <groupId>mysql</groupId>

            <artifactId>mysql-connector-java</artifactId>

            <version>5.1.6</version>

        </dependency>

        <dependency>

            <groupId>log4j</groupId>

            <artifactId>log4j</artifactId>

            <version>1.2.12</version>

        </dependency>

        <dependency>

            <groupId>junit</groupId>

            <artifactId>junit</artifactId>

            <version>4.10</version>

        </dependency>

    </dependencies>

数据库创建

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '用户名称',
  `birthday` datetime DEFAULT NULL,
  `sex` char(1) COLLATE utf8_bin DEFAULT NULL,
  `address` varchar(256) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

实体对象User的创建

 

配置日志文件

在resources文件下面放入log4j.properties文件:

### 设置###

log4j.rootLogger = debug,stdout,D,E

 

### 输出信息到控制抬 ###

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

log4j.appender.stdout.Target = System.out

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

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

 

配置Mybatis的主配置文件:

配置位置在resources文件下面直接配置

 

<?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"></transactionManager>

            <dataSource type="POOLED">

                <property name="driver" value="com.mysql.jdbc.Driver"/>

                <property name="url" value="jdbc:mysql://localhost:3306/gqk?useUnicode=true&characterEncoding=utf8"/>

                <property name="username" value="root"/>

                <property name="password" value="root"/>

            </dataSource>

        </environment>

    </environments>

    <mappers>

 

       <!-- <mapper resource="com/xkrj/dao/UserMapper.xml"></mapper>-->

        <mapper class="com.xkrj.dao.UserDao"></mapper>

    </mappers>

</configuration>

注意:resource配置的是普通映射,class配置的注解映射

 

编写dao层接口

com.xkrj.dao

public interface UserDao {

   // @Select("select * from user")

    List<User> findAll();

}

 

配置Mybatis的关系型映射文件

注意在配置映射文件的时候,映射文件的路径包需要和dao层接口的文件路径包保持一致:

<?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="UserDao">

    <select id="findAll" resultType="User">

        select  * from user

    </select>

</mapper>

测试:

//读取配置文件

//创建SqlSessionFactory对象

//创建SqlSession对象

//使用SqlSession对象创建Dao代理对象

//使用代理对象执行方法:

 

3. Mybatis执行过程简单介绍

4. Mybatis实现Dao层接口的写法:

 

5. Mybatis的CURD操作

parameterType:

将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器推断出具体传入语句的参数,默认值为未设置(unset

resultType

从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType resultMap,但不能同时使用。

 

获取添加时ID的值:

<selectKey keyProperty="id" resultType="int" order="BEFORE"> 

SELECT LAST_INSERT_ID();

</selectKey>

使用实体类作为查询条件

实体对象QueryVo:

 

映射文件:

 

6.  ResultMap接收结果映射

默认情况下,MyBatis 会在幕后自动创建一个 ResultMap,再基于属性名来映射列到 JavaBean 的属性上。如果列名和属性名没有精确匹配,可以在 SELECT 语句中对列使用别名(这是一个基本的 SQL 特性)来匹配标签。比如:

<select id="selectUsers" resultType="User"> 

select user_id as "id", user_name as "userName", hashed_password as "hashedPassword" from some_table where id = #{id}
</select>

ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。 上面这些简单的示例根本不需要下面这些繁琐的配置。 但出于示范的原因,让我们来看看最后一个示例中,如果使用外部的 resultMap 会怎样,这也是解决列名不匹配的另外一种方式。

<resultMap id="userResultMap" type="User"> 

<id property="id" column="user_id" /> 

<result property="username" column="user_name"/> 

<result property="password" column="hashed_password"/>

</resultMap>

 

而在引用它的语句中使用 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:

<select id="selectUsers" resultMap="userResultMap"> 

select user_id, user_name, hashed_password from some_table where id = #{id}

</select>

7. Mybatis主配置文件的配置

属性(properties)

 

jdbc_driverClassName=com.mysql.jdbc.Driver

jdbc_url = jdbc:mysql://localhost:3306/sports_bill

jdbc_username=root

jdbc_password=root

 

 

 

类型别名(typeAliases)

类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如

 

指定别名后 类型是忽略大小写的:

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

 

8. 连接池的作用 

1)通过数据库连接池,应用程序可以重用池中现有的连接中现有的连接,而不必反复与数据库建立新的连接。

2)使用连接池可显著提高应用程序的可伸缩,因为有限数量的数据库连接可以为数量大得多的客户端提供服务。例如:数据库应用里面,上万的用户可能只需要少量的连接,因为客户端不可能同时并发的大量的访问数据库。故少量的连接可以支持大量的服务,提高程序的可伸缩性。

3)同时,由于可以节省建立新连接所需的大由于可以节省建立新连接所需的大量时间,使用连接池还能够改善性能。 

9. Mybatis中使用链接池

有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):

 

POOLED这种数据源的实现利用的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。

 

UNPOOLED这个数据源的实现只是每次被请求时打开和关闭连接。虽然有点慢,但对于在数据库连接可用性方面没有太高要求的简单应用程序来说,是一个很好的选择。 不同的数据库在性能方面的表现也是不一样的,对于某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形

 

JNDI:服务器提供的技术来实现获取DataSource对象不同的服务器所获取的DataSource值不一样 ,如果不是maven或者war工程不能使用

10. Mybatis多表查询

一个用户对应多个订单(多个订单对应一个用户)

一个用户对应一个身份证

一个学生对应多个老师,一个老师对应多个学生

创建实验表

account

CREATE TABLE `account` (
  `ID` int(11) NOT NULL,
  `UID` int(11) DEFAULT NULL,
  `MONEY` double DEFAULT NULL,
  PRIMARY KEY (`ID`),
  KEY `FK_Reference` (`UID`),
  CONSTRAINT `FK_Reference` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

创建实体类对象Account.java

 

因为Account类中没有用户的信息 所以一编写AccountUser 继承Account类

 

 

配置映射文件:

 

 

以上是通过继承的方式编写的,比较麻烦不推荐使用:

关联多对一关系

更改实体对象Account类:

 

更改配置文件信息

 

多对一测试

  @Test

    public void findAll(){

       List<Account> accounts =  accountDao.findAll();

       for(Account account : accounts){

           System.out.println(account+"   "+account.getUser());

 

       }

    }

关联一对多关系

关联实体类修改

 

修改User的映射文件

 

测试执行

 

关联多对多关系

多对多的关系最终还是两个一对多 引入中间表完成只是sql语句发生了变化;

一个用户对应多个角色,一个角色对应多个用户:

创建实验表

Role表:

CREATE TABLE `role` (
  `id` int(11) NOT NULL,
  `role_name` varchar(30) COLLATE utf8_bin DEFAULT NULL,
  `role_desc` varchar(60) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

用户表User

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '用户名称',
  `birthday` datetime DEFAULT NULL,
  `sex` char(1) COLLATE utf8_bin DEFAULT NULL,
  `address` varchar(256) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

中间表:

CREATE TABLE `user_role` (
  `uid` int(11) NOT NULL,
  `rid` int(11) NOT NULL,
  PRIMARY KEY (`uid`,`rid`),
  KEY `rid` (`rid`),
  CONSTRAINT `user_role_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`),
  CONSTRAINT `user_role_ibfk_2` FOREIGN KEY (`rid`) REFERENCES `role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

配置一个角色对应多个用户

注意:此处配置完成后必须要在主配置文件中进行映射

 

测试查询

  @Test

    public void queryAll() throws Exception {

        List<Role> roles = roleDao.findAll();

        for (Role r : roles) {

            System.out.println(r);

        }

        session.close();

    }

演示一个用户对应多个角色 省略

11. Mybatis的动态sql

12. Mybatis的延迟加载和立即加载

立即加载:

就是立马执行sql语句:

下面的日志一次会执行所有的sql语句

 

 

 

延迟加载

根据需求 加载需要的sql语句:

开启延迟加载:

 

配置account的映射文件:

 

配置User的映射文件

 

测认:

 

生成的日志文件只有查询account的sql语句

 

13. Mybatis缓存技术

mybatis提供查询缓存,用于减轻数据库压力,提高数据库性能

1. 什么样的数据使用缓存:

经常查询的数据;

不经常更改的数据

不是很重要的数据

2. 什么样的数据不适应缓存

l 经常改变的数据

l 数据很重要

l 不经常查询的数据

3. 一级缓存工作原理

sqlsession级别的缓存

在操作数据库时,需要构造sqlsession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据

 不同的sqlsession之间的缓存区域是互相不影响的。一级缓存自动开启不需要配置任何信息

 

第一次发起查询sql查询用户id1的用户,先去找缓存中是否有id1的用户,如果没有,再去数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。

如果sqlsession执行了commit操作(插入,更新,删除),会清空sqlsession中的一级缓存,避免脏读

第二次发起查询id1的用户,缓存中如果找到了,直接从缓存中获取用户信息

mybatis默认支持一级缓存。

 

测试:

 

结果:

 

打印的对象是属于同一个对象,sql语句只执行一次:

当Session关闭 缓存中的数据 就不存在了:

 

获取得到的结果为false;两次不一样执行多次sql语句:

 

4. 二级缓存实现原理

 

测试案例:多个sqlSession执行同一个方法:

 

配置开启二级缓存

 

还需要在 Mapper 对应的xml中添加 cache 标签,表示对哪个mapper 开启缓存

<!-- 表示DEPT表查询结果保存到二级缓存(共享缓存) --> <cache/>

 

 

获取的日志为:

 

二级缓存存储的是数据不是对象;

14. Mybatis注解开发

@Insert:实现新增

@Update:实现更新

@Delete:实现删除

@Select:实现查询

@Result:实现结果集封装

@Results:可以与@Result 一起使用,封装多个结果集

@ResultMap:实现引用@Results 定义的封装

@One:实现一对一结果集封装

 

@Many:实现一对多结果集封装

推荐阅读