了解MyBatis框架

Posted 你这家伙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了了解MyBatis框架相关的知识,希望对你有一定的参考价值。

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

JDBC编程存在的问题

在此之前我们了解的JDBC的操作为: 1. 首先创建数据库连接池DataSource 2. 通过DataSource获取数据库连接Connection 3. 编写要执行带“?”占位符的SQL语句 4. 通过Connection及SQL创建操作命令对象Statement 5. 替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值 6. 执行SQL语句 7. 处理结果集 8. 释放资源

存在问题
对用不同业务来说,他的CRUD操作类型基本上都是以上的步骤,只有如下部分是不一样的:

  • 带占位符的SQL语句
  • 替换占位符:一般使用基础类型或Java对象,需要明确替换哪个占位符,哪个值来替换
  • 如果是查新。一般会将结果集转换为Java对象,需要提供转换的急Java类型,及与结果集字段的映射关系

可以看出少量的代码真正用于业务功能,大部分的代码都是样板代码。不过,这些样板代码非常重要,清理资源和处理错误确保了数据访问的健壮性,避免了资源的泄露。

解决方案

  1. 最简单的,涉及工具类提供统一的功能:获取数据库连接,释放资源
  2. 使用模版设计模式,父类的模板方法提供统一的逻辑,子类提供不同实现。但这部分统一代码逻辑都会比较复杂。
  3. 更进一步的考虑,其实可以通过 AOP 技术,自动的生成代理类,代理类的方法中织入了统一的样板代码。

框架会采用第三种解决方案,自动的生成样板代码,我们只需要提供 sql ,要替换占位符的数据,返回结果集要转换的 java 类型

ORM框架

ORM(Object Relational Mapping),即对象关系映射。在面向对象编程语言中,将关系型数据库中的数据与对象建立起映射关系,进而自动的完成数据与对象的互相转换:

  1. 将输入数据(即传入对象)+SQL 映射成原生 SQL
  2. 将结果集映射为返回对象,即输出对象

ORM 把数据库映射为对象
数据库表(table)–> 类(class)
记录(record,行数据)–> 对象(object)
字段(field) --> 对象的属性(attribute)

常见的ORM框架

常见的ORM框架有Mybatis和Hibernate

Mybatis

Mybatis是一种典型的半自动的 ORM 框架,所谓的半自动,是因为还需要手动的写 SQL 语句,再由框
架根据 SQL 及 传入数据来组装为要执行的 SQL。其优点为:

  1. 因为由程序员自己写 SQL,相对来说学习门槛更低,更容易入门。
  2. 更方便做 SQL的性能优化及维护。
  3. 对关系型数据库的模型要求不高,这样在做数据库模型调整时,影响不会太大。适合软件需求变更
    比较频繁的系统,因此国内系统大部分都是使用如 Mybatis 这样的半自动 ORM 框架。
    其缺陷为:
    不能跨数据库,因为写的 SQL 可能存在某数据库特有的语法或关键词

Hibernate

Hibernate是一种典型的全自动 ORM 框架,所谓的全自动,是 SQL 语句都不用在编写,基于框架的
API,可以将对象自动的组装为要执行的 SQL 语句。其优点为:

  1. 全自动 ORM 框架,自动的组装为 SQL 语句。
  2. 可以跨数据库,框架提供了多套主流数据库的 SQL 生成规则。
    其缺点为:
    学习门槛更高,要学习框架 API 与 SQL 之间的转换关系
    对数据库模型依赖非常大,在软件需求变更频繁的系统中,会导致非常难以调整及维护。可能数据
    库中随便改一个表或字段的定义,Java代码中要修改几十处。
    很难定位问题,也很难进行性能优化:需要精通框架,对数据库模型设计也非常熟悉。

Mybatis的使用

  1. 首先创建一个Maven项目 mybatis-study,在pom.xml中引入SpringBoot及Mybatis需要的依赖包,
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <!-- 默认使用的Spring Framework版本为5.2.10.RELEASE -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.5.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>org.example</groupId>
  <artifactId>mybatis-study</artifactId>
  <version>1.0-SNAPSHOT</version>
  <properties>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>

    <!-- spring-boot-starter-web: 基于SpringBoot开发的依赖包,
                             会再次依赖spring-framework中基本依赖包,aop相关依赖包,web相关依赖包,
                             还会引入其他如json,tomcat,validation等依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <!-- 排除tomcat依赖 -->
      <exclusions>
        <exclusion>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <!-- 添加 Undertow 依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>

    <!--引入AOP依赖-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    <!-- mybatis-spring-boot-starter: Mybatis框架在SpringBoot中集成的依赖包,
                            Mybatis是一种数据库对象关系映射Object-Relationl Mapping(ORM)框架,
                            其他还有如Hibernate等 -->
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.1.3</version>
    </dependency>

    <!-- Mybatis代码生成工具 -->
    <dependency>
      <groupId>org.mybatis.generator</groupId>
      <artifactId>mybatis-generator-core</artifactId>
      <version>1.3.5</version>
    </dependency>

    <!-- druid-spring-boot-starter: 阿里Druid数据库连接池,同样的运行时需要 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>1.2.3</version>
    </dependency>

    <!-- JDBC:mysql驱动包 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.49</version>
      <scope>runtime</scope>
    </dependency>

    <!-- spring-boot-devtools: SpringBoot的热部署依赖包 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <!-- 不能被其它模块继承,如果多个子模块可以去掉 -->
      <optional>true</optional>
    </dependency>

    <!-- lombok: 简化bean代码的框架 -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>

    <!-- spring-boot-starter-test: SpringBoot测试框架 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!-- SpringBoot的maven打包插件 -->
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      <!-- 明确指定一些插件的版本,以免受到 maven 版本的影响 -->
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
      <plugin>
        <artifactId>maven-deploy-plugin</artifactId>
        <version>2.8.2</version>
      </plugin>
      <plugin>
        <artifactId>maven-install-plugin</artifactId>
        <version>2.5.2</version>
      </plugin>
      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.2.0</version>
      </plugin>
      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>
      <plugin>
        <artifactId>maven-site-plugin</artifactId>
        <version>3.3</version>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.22.2</version>
      </plugin>

    </plugins>
  </build>

</project>
  1. 准备数据库
    在根目录下创建一个dp包,然后创建一个init.sql的文件,然后放入数据的初始化语句
drop database if exists mybatis_study;
create database mybatis_study character set utf8mb4;

use mybatis_study;

drop table if exists user;
create table user(
     id int primary key auto_increment,
     username varchar(20) not null unique comment '账号',
     password varchar(20) not null comment '密码',
     nickname varchar(20) comment '用户昵称',
     sex bit default 0 comment '性别,0/false为女,1/true为男',
     birthday date comment '生日',
     head varchar(50) comment '头像地址',
     create_time timestamp default now() comment '创建日期,默认为插入时的日期'
) comment '用户表';

insert into user(username, password) values ('a1', '11');
insert into user(username, password) values ('a2', '12');
insert into user(username, password) values ('b', '2');
insert into user(username, password) values ('c', '3');
  1. 准备一个SpringBoot的启动类
    在java目录创建org.example.Application的启动类
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  1. 准备SpringBoot的配置文件
    在resource目录下创建一个application.properties文件,其中账号和密码为你自己MySQL的账号和密码
debug=true
# 设置打印日志的级别,及打印sql语句
#日志级别:trace,debug,info,warn,error
#基本日志
logging.level.root=INFO
#扫描的包并按debug日志级别打印:druid.sql.Statement类和org.example包
logging.level.druid.sql.Statement=DEBUG
logging.level.org.example=DEBUG

#数据库连接池配置:
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_study?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=******

#指定Mybatis表和实体映射关系xml配置文件,包含表与实体的映射,字段和属性的映射,及各个sql语句
mybatis.mapper-locations=classpath:mapper/**Mapper.xml
  1. 然后在对database进行配置,配置完成之后在init.sql页面点击右键run,然后就可以在database里面看到自己刚刚创建的数据库


6. 最后运行Spring启动类,不报即可

Mybatis开发

Java实体类

需要根据数据库表建立 Java类,表中的每个字段需要对应类中的属性。之后会在 Mybatis 的映射文件中配置字段与属性的映射关系。
这部分实体类对象,对应以前面图中绿色的两个部分,一般会以如下方式呈现:

  1. 作为传入数据,表现在方法参数上。
  2. 作为结果集转换的输出数据,表现在方法的返回值上

配置Mybatis数据操作Mapper接口

这部分对应以上图中淡黄色的部分:由开发人员提供执行SQL的接口,框架会自动为接口生成代理类,
代理类中会包含模版方法的代码逻辑

在 org.example.mapper 包下创建的用户类的 Mybatis 映射 Mapper 接口:
注意接口需要使用注解 @Mapper 及 @Component 。在SpringBoot启动后,SpringBoot会自动扫描到启
动类包下的注解类,且Mybatis框架会进一步完成生成代理类的工作。

@Mapper
@Component
public interface UserMapper {
    User selectById(Integer id);
}

配置Mybatis实体映射文件

映射文件是xml文件,且在 application.properties 中指定了路径,是在classpath:mapper/**Mapper.xml ,该路径是在类加载路径下的mapper文件夹下,所有以Mapper.xml 结尾的文件。

所以可以在 src/main/resources 下新建 mapper 文件夹,并创建文章类的映射文件UserMapper(这个名字最好和Mapper接口类中的UserMapper一致)

<?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="org.example.mapper.UserMapper">
    <resultMap id="Basespace" type="org.example.model.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
    </resultMap>

</mapper>
  • mapper标签:需要指定 namespace 属性,表示命名空间,值为 mapper 接口的全限定名,包括全包名.类名
  • resultMap标签:配置查询结果集与 Java 类的映射关系,包括如下配置:
  1. id 绑定该映射的键,之后可以通过 id 值来使用。在本命名空间使用可以直接使用,其他命
    名空间需要以 命名空间.resultMap的id 的方式来使用。之后的部分会进一步使用。这里先大
    概介绍。
  2. type 绑定该结果集映射的 Java 类。
  3. id标签指定结果集的唯一标识,一般为数据库主键。
  4. result标签指定结果集字段与 Java 类中属性的映射,其中 column 指定结果集字段,
    property 指定 Java 属性。

配置SQL

之后还需要在 Mybatis 映射文件中配置 SQL,需要配置在mapper>标签下,与<resultMap标签同级

对 CRUD 操作来说,每个不同类型的操作都有对应的标签

  • select标签:查询语句
  • insert标签:插入语句
  • update标签:修改语句
  • delete标签:删除语句
    <select id="selectById" parameterType="java.lang.Integer"
            resultMap="BaseResultMap">
        select * from User where id=#{id}
    </select>

注意:

  1. 都必须指定 id 属性,该属性会绑定 mapper 接口方法,值和方法名相同。本质上 Mybatis 是根据 mapper包名 查找对应的命名空间,之后在调用方法时,在该命名空间下根据 接口方法名 查找对应 id 的 sql。
  2. 使用 parameterType 指定传入数据的类型,该配置只能在mapper接口方法参数只有一个时使用,也可以省略。如果有多个方法参数,需要另行设置,
  3. .查询语句可以使用 resultMap 属性绑定结果集映射,值为结果集映射的 id 。注意返回结果集有一行和多行数据时,都可以使用 resultMap 绑定映射,对应接口方法的返回值是一个对象(结果集为一行),或 List<类型>(结果集为多行)
  4. 对插入,修改,删除这样的更新操作来说,返回的值都是 int,表示更新成功的数量,所以不用指定 resultMap 。
  5. 传入数据可以在 sql 语句中使用 #{方法参数名} 的方式获取。

单元测试

可以在Spring中方便的使用JUnit来完成单元测试,在src/test/java下,创建一个单元测试类
//指定为Spring环境中的单元测试
@RunWith(SpringRunner.class)
//指定为SpringBoot环境的单元测试,Application为启动类
@SpringBootTest(classes = Application.class)
//使用事务,在SpringBoot的单元测试中会自动回滚
@Transactional
public class UserMapperTest {

    @Autowired
    private UserMapper userMapper;
    @Test
    public void selectByIdTest(){
        User user = userMapper.selectById(1);
        System.out.println(user);
    }
}

执行结果:

多个方法参数(模糊匹配)

当接收多个方法参数时,xml 映射文件中的 sql 配置不能再使用 parameterType 属性,需要在方法参
数上使用 @Param(“参数的名称”) ,之后在 sql 中使用 #{参数的名称} 的方式来替换占位符。

@Mapper//启动时会扫描该注解的接口,动态生成代理类
@Component
public interface UserMapper {

    User selectById(Integer id);
    int insert(User user);

    List<User> selectLike(@Param("username") String username,@Param("password") String password);
}

User.xml映射文件的配置

    <select id="selectLike" resultMap="BaseResultMap">
        select * from user where
        username like #{username} and  password like #{password}
    </select>

单元测试类1
例如这里模糊匹配username为“a”开头的,password为“1”开头的

    @Test
    public void selectLikeTest(){
        String username = "a%";
        String password = "1%";
        List<User> users = userMapper.selectLike(username,password);
        System.out.println(users);
    }

运行结果

#{参数} vs ${参数}

例如上述示例,假如我们模糊查询出来的数据想让它进行降序排序,那么我们要怎么做呢?

对于 #{参数} 的使用来说,如果参数是字符串,在替换占位符时,会在 sql 语句中加上单引号。如果是不能使用单引号的字符串,例如 sql 语句是 order by 字段 {传入参数} ,此时 {传入参数} 就需要使用 ${传入参数} 这样的占位符,替换时不会带上单引号。

示例:在以上模糊查询语句中,加上根据文章标题排序的功能,要求是根据方法参数传入的字符串 asc 或 desc 来排序

  1. 首先将UserMapper接口方法调整为
@Mapper//启动时会扫描该注解的接口,动态生成代理类
@Component
public interface UserMapper {

    User selectById(Integer id);
    int insert(User user);

    List<User> selectLike(@Param("username") String username,
                          @Param("password") String password,
                          @Param("orderBy") String orderBy);
}

  1. 然后再将UserMapper.xml调整为:
    <select id="selectLike" resultMap="BaseResultMap">
        select * from user where
        username like #{username} and  password like #{password}
        order by password ${orderBy}
    </select>
  1. 然后在调整单元测试代码
    @Test
    public void selectLikeTest(){
        String username = "a%";
        String password = "1%";
        List<User> users = userMapper.selectLike(username,password,"desc");
        System.out.println(users);
    }
}

插入时获取自增主键

在之前插入数据时,如果是自增主键,插入后无法获取到。此时需要在 Mybatis 映射文件中,insert标签配置如下属性

  • useGeneratedKeys:这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
  • keyColumn:设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
  • keyProperty:指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或insert 语句的 selectKey 子元素设置它的值,默认值:未设置( unset )。如果生成列不止一个,可以用逗号分隔多个属性名称。
    如:我们将插入的数据使用id进行自增
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into user(username,password,nickname,
        sex,birthday,head
        )values (
        #{username},#{password},#{nickname},
        #{sex},#{birthday},#{head}
        )
    </insert>

以上是关于了解MyBatis框架的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis框架—动态 SQL配置文件事务

Spring+SpringMVC+MyBatis+Maven框架整合

SSM框架专题-MyBatis框架从零入门老杜版笔记(上)

MYBATIS05_ifwherechoosewhentrimsetforEach标签sql片段

自己实现一个简化版Mybatis框架

从 0 开始手写一个 Mybatis 框架,三步搞定!