【MyBatis学习笔记 一】MyBatis基本概念及项目集成

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 【MyBatis学习笔记 一】MyBatis基本概念及项目集成

Java Web编程系列的Blog中,我们学习了使用JDBC去通过代码操作数据库,我们知道JDBC是一组规范,一组API,也在使用的过程中发现一些代码控制上的不友好感,例如我们需要自己去写代码控制连接的生命周期;获取到结果集需要自己去转换数据类型再进行模型的设置;sql语句强嵌入到代码中不好维护等等诸多问题。其实这些也是可以理解的,JDBC只是提供了一组规范,细粒度的解决代码访问数据库这个问题,最佳实践还仰赖ORM框架来实现,所以其实后续有很多封装的ORM框架作为DAO层和数据库Model的管理者。例如我早期在5年前使用过的Hibernate。当然今时不同往日,现在技术上比较留下的是MyBatis这种半ORM框架,再加上5年前的记忆和理解程度都不算太深了,这次通过MyBatis来再次对这些内容进行一个梳理吧。

业务对象和数据对象

什么是业务对象,什么又是数据对象,这两个为什么要区分开呢?其实在【Java Web编程 十四】深入理解MVC架构模式中我就将Model有意的区分为两个Model:

这两个Model也就是业务对象和数据对象:

  • 业务对象,也就是Service Model是面向对象的,它本身是一个对象,包含一些属性和基本的方法,在上层更符合程序员的认知,因为我们是面向对象开发的。比如Person这个Model就是包含了人的一些属性,姓名、年龄、手机号、银行账号、兴趣爱好等,我们从业务使用上去理解和构造这个Model。
  • 数据对象,也就是DAO Model是面向关系的(关系型数据库),它虽然也是一个对象,但其实是对数据表的映射,表间并没有对象的认知,它们只是一组组关系,例如业务的Person对应过来,可能会对应好几个DAO Model,例如PersonInfo【人员基本信息表】、PersonAccount【人员账户表】、PersonLike【人员兴趣表】,它们可能都是通过人员主键关联的,但如果不是熟悉的开发人员,从这些定义想象不出一个完备的业务对象是什么样的

所以其实我们下文中提到的对象都是数据对象,一般而言,一个数据类对应一张表,一个数据对象对应于表中的一条数据。

持久层的概念

数据为什么要持久化?持久化是将程序数据从瞬时状态向持久状态和间转换的机制

  • 方式:把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)
  • 过程:将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等
  • 实现:JDBC就是一种持久化机制。文件IO也是一种持久化机制。

为什么需要持久化服务呢?那是由于内存本身的缺陷引起的,内存断电后数据会丢失,但有一些数据是无论如何都不能丢失的,比如用户信息、银行账号等,但是我们无法保证内存永不掉电,所以我们需要持久化,那么什么是持久层呢?

  • 完成持久化工作的代码块,也就是上图的 DAO层 ,DAO (Data Access Object) 数据访问对象

我们的系统架构中,应该有一个相对独立的逻辑层面,专注于数据持久化逻辑的实现,与系统其他部分相对而言,这个层面应该具有一个较为清晰和严格的逻辑边界,说白了就是用来操作数据库存在的

为什么使用MyBatis

除了JDBC可以做DAO层外,我们知道还有很多基于JDBC规范的ORM框架

ORM框架的优势

什么是ORM框架呢?ORM(Object Relational Mapping)框架采用元数据来描述对象与关系映射的细节,元数据一般采用XML格式,并且存放在专门的对象一映射文件中,简单理解为一种框架的格式。使用ORM框架后,应用程序不再直接访问底层数据库,而是以面向对象的方式来操作持久化对象,而ORM框架则会通过映射关系将这些面向对象的操作转换成底层的SQL操作

那么比起JDBC,ORM的优势是什么呢?

  • 传统的JDBC操作 , 有很多重复代码。比如 : 数据取出时的封装 , 数据库的建立连接等。通过框架可以减少重复代码,提高开发效率
  • 传统的JDBC操作 , sql语句与代码紧耦合,不利于维护,如果数据表结构变了,那么得全局搜索相关表调整sql语句
  • 传统的JDBC操作,不能让程序员像面向对象一样的方式操作数据

而ORM框架就可以轻松的解决以上的问题,更多区别参照为什么使用MyBatis

Hibernate和MyBatis比较

MyBatis比起Hibernate来说其实是半自动的ORM框架,sql语句还是需要我们来维护的,它的优势如下:

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

Hibernate是一个全自动的ORM框架,它的优点如下:

  • 配置全面:消除了代码的映射规则,它全部被分离到XML或者注解里面去配置,无需再管理数据库连接,它也配置到XML里面。
  • 操作简单:一个会话中,不需要操作多个对象,只要操作Sesison即可,关闭资源只需要关闭一个Session即可。

我们知道框架没有最好,只有最合适的,技术往往对应于某种业务场景,那么该如何依照需求选用框架呢?

  • Hibernate作为Java ORM框架,编程简易,消除了代码的映射规则,完全通过配置实现。同时无需编写SQL确实开发效率优于MyBatis。而且它也提供了缓存、日志、级联、等强大的功能,但是Hibernate的缺陷也是十分的明显的。就是在多表关联复杂的SQL时、数据系统权限限制时、需要动态SQL时、存储过程使用用时Hibernate十分不便,而性能又难以通过SQL来优化。所以Hibernate一般只适用于场景不太复杂的、性能要求不太苛刻的系统
  • MyBatis 是一个灵活的、可以动态生成映射关系的框架,它几乎可以替代JDBC。拥有动态列、动态表名,存储过程都支持。同时提供了简易的缓存(如(默认)一级缓存,还有二级缓存)、日志、级联。但是它的缺陷是需要自己提供映射规则和SQL,所以它的开发工作量一般要比Hibernate略大一些,适用于场景复杂,使用灵活的大型系统

总之需要根据项目的实际情况去选择框架。MyBatis具有高度灵活、可优化、易维护等特点。生产环境下系统的复杂度一般较高,所以我们更多的是在使用MyBatis

MyBatis框架使用步骤

最终实现的项目结构如下,包含核心配置文件,mapper配置文件,核心配置文件读取类,mapper对应的Dao接口,Model和测试类以及数据库表。

1 创建一个Java Web项目

首先第一步还是创建一个Java Web项目,创建步骤和之前一样,我们创建一个名为MyBatis的项目:

2 搭建数据库表

我们继续使用在之前的Java Web部分使用的库表来进行学习:

并且我们提前插入两条数据。

3 配置Maven依赖

然后再pom.xml中配置Maven依赖,来获取MyBatis的包导入到项目中来,我使用的数据库是MySQL8的版本,而mybatis当前的最新版本是3.5.7【2021年8月8日】:

<dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.18</version>
        </dependency>

4 编写核心配置文件

在资源目录下创建mybatis-config.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核心配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test?userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

5 编写核心配置读取类

有了核心配置文件后我们还需要配置文件的读取类,二者结合起来类似于JDBC之前使用的JdbcUtils类,只不过我们这里做了解耦操作:

//sqlSessionFactory --> sqlSession
public class MybatisUtils {
    static SqlSessionFactory sqlSessionFactory = null;
    static {
        try {
            //使用Mybatis第一步 :获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例.
    // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

这里涉及两个重要对象:

  • SqlSessionFactory:是单个数据库映射关系经过编译后的内存镜像,其主要作用是创建SqlSession对象。SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象来构建,而SqlSessionFactoryBuilder则可以通过XML配置文件或一个预先定义好的Configuration实例构建出SqlSessionFactory的实例。SqlSessionFactory对象是线程安全的,它一旦被创建,在整个应用执行期间都会存在。如果我们多次地创建同一个数据库的SqlSessionFactory,那么此数据库的资源将很容易被耗尽。为了解决此问题,通常每一个数据库都会对应一个SqlSessionFactory,所以在构建SqlSessionFactory实例时,建议使用单例模式。
  • SqlSession:是应用程序与持久层之间执行交互操作的一个单线程对象,其主要作用是执行持久化操作。SqlSession对象包含了数据库中所有执行SQL操作的方法,由于其底层封装了JDBC连接,所以可以直接使用其实例来执行已映射的SQL语句。每一个线程都应该有一个自己的SqlSession实例,并且该实例是不能被共享的。同时,SqlSession实例也是线程不安全的,因此其使用范围最好在一次请求或一个方法中,决不能将其放在一个类的静态字段、实例字段或任何类型的管理范围(如Servlet的HttpSession)中使用。使用完SqlSession对象之后,要及时关闭它,通常可以将其放在finally块中关闭。

可以理解为会话对象,在一次会话操作中需要使用该对象开启连接和操作。

6 编写数据对象实体类

我们需要创建一个数据对象实体类来和数据表Person进行映射匹配。

package com.example.MyBatis.dao.model;
import lombok.Data;
/*
 * person表对应对象
 * */
@Data
public class Person {
    private int id;
    private String username;
    private String password;
    private int age;
    private int phone;
    private String email;
}

7 编写Mapper接口

接下来就是编写实体类对应的操作接口:

package com.example.MyBatis.dao.mapper;
import com.example.MyBatis.dao.model.Person;
import java.util.List;
public interface PersonDao {
    List<Person> getPersonList();
}

8 编写Mapper接口实现类

由原来的UserDaoImpl转变为一个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">
<!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.example.MyBatis.dao.mapper.PersonDao">
    <select id="getPersonList" resultType="com.example.MyBatis.dao.model.Person">
        select * from person
    </select>
</mapper>

我们编写好mapper配置文件后需要注册到核心配置文件中:

<?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核心配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test?userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mappers/personMapper.xml"/>
    </mappers>
</configuration>

9 编写测试类

最后我们编写一个测试类,测试一下效果:

import com.example.MyBatis.dao.mapper.PersonDao;
import com.example.MyBatis.dao.model.Person;
import com.example.MyBatis.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import java.util.List;
public class PersonTest  {
    @Test
    public void test(){
        //1.获取SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //2.执行SQL
        PersonDao personDao = sqlSession.getMapper(PersonDao.class);
        List<Person> personList = personDao.getPersonList();
        for (Person person : personList) {
            System.out.println(person);
        }
        //关闭sqlSession
        sqlSession.close();
    }
}

打印结果如下,可以看到数据库内的两条数据被打印出来了:

Mybatis框架操作数据库过程总结

依据以上过程我们可以梳理Mybatis框架操作数据库步骤

  1. 读取Mybatis配置文件mybatis-config.xmlmybatis-config.xml作为Mybatis的全局配置文件,配置Mybatis的运行环境等信息,其中主要内容是获取数据库连接。
  2. 加载映射文件Mapper.xmlMapper.xml文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在mybatis-config.xml中注册才能执行。mybatis-config.xml可以加载多个配置文件,每个配置文件对应数据库中的一张表。
  3. 构建会话工厂。通过Mybatis的环境等配置信息构建会话工厂SqlSessionFactory。
  4. 创建SqlSession对象。由会话工厂创建SqlSession对象,该对象中包含执行SQL的所有方法。

到这里一个连接数据库的会话就创建完成了,因为拥有数据库的账号密码所以会话可以连接数据库成功,模拟我们登录数据库成功,又因为它内部注册了PersonMapper,所以可以通过getMapper获取到PersonDao对象实例,然后调用getPersonList方法,该方法的实现由我们编写的sql语句提供。

  1. Mybatis底层定义了一个Executor接口来操作数据库,它会根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。
  2. 在Executor接口的执行方法中,包含一个MappedStatement类型的参数,该参数对映射信息的封装,用于存储要映射的SQL语句的id、参数等。Mapper.xml文件中一个SQL对应一个MappedStatement对象,SQL的id即是MappedStatement的id。
  3. 输入参数映射。在执行方法时,MappedStatement对象会对用户执行SQL语句的输入参数进行定义(可以定义为Map、List类型、基本类型和POJO类型),Executor执行器会通过MappedStatement对象在执行SQL前,将输入的Java对象映射到SQL语句中。这里对输入参数的映射过程就类似于JDBC编程中对preparedStatement对象设置参数的过程。
  4. 输入结果映射。在数据库中执行完SQL语句后,MappedStatement对象会对SQL执行输出的结果进行定义(可以定义为Map和List类型、基本类型、POJO类型),Executor执行器会通过MappedStatement对象在执行SQL语句后,将输出结果映射至Java对象中。这种将输出结果映射到Java对象的过程就类似于JDBC编程中对结果的解析处理过程。

这里我们没有用到参数映射,通过id的定义我们能找到方法的映射,然后执行完对应sql语句后,将返回对象映射到结果Model上即可。

总结一下

MyBatis使用过之后才发现确实灵活,因为具体的某个方法的实现都是通过我们编写sql脚本实现的,它做到了sql与代码的解耦,也做到了我们自定义sql的能力,确实非常灵活可维护。对比Hibernate我们可以看到二者是有很强的相似性,从核心配置到Model类到Model类的配置文件。最重要的区别是:Hibernate操作都是通过Hibernate自己封装的HQL语言或者Session实现的,相当于Hibernate提供了一组封装了对sql的api给DAO层,而MyBatis则还是通过sql语句实现的

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
312 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
|
1月前
|
存储 JavaScript 数据库
ToB项目身份认证AD集成(一):基于目录的用户管理、LDAP和Active Directory简述
本文介绍了基于目录的用户管理及其在企业中的应用,重点解析了LDAP协议和Active Directory服务的概念、关系及差异。通过具体的账号密码认证时序图,展示了利用LDAP协议与AD域进行用户认证的过程。总结了目录服务在现代网络环境中的重要性,并预告了后续的深入文章。
|
1月前
|
人工智能 JavaScript 网络安全
ToB项目身份认证AD集成(三完):利用ldap.js实现与windows AD对接实现用户搜索、认证、密码修改等功能 - 以及针对中文转义问题的补丁方法
本文详细介绍了如何使用 `ldapjs` 库在 Node.js 中实现与 Windows AD 的交互,包括用户搜索、身份验证、密码修改和重置等功能。通过创建 `LdapService` 类,提供了与 AD 服务器通信的完整解决方案,同时解决了中文字段在 LDAP 操作中被转义的问题。
|
1月前
|
jenkins Shell 持续交付
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(二)
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(二)
69 0
|
1月前
|
前端开发 Java 数据库连接
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
本文是一份全面的表白墙/留言墙项目教程,使用SpringBoot + MyBatis技术栈和MySQL数据库开发,涵盖了项目前后端开发、数据库配置、代码实现和运行的详细步骤。
46 0
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
|
1月前
|
安全 Java 测试技术
ToB项目身份认证AD集成(二):快速搞定window server 2003部署AD域服务并支持ssl
本文详细介绍了如何搭建本地AD域控测试环境,包括安装AD域服务、测试LDAP接口及配置LDAPS的过程。通过运行自签名证书生成脚本和手动部署证书,实现安全的SSL连接,适用于ToB项目的身份认证集成。文中还提供了相关系列文章链接,便于读者深入了解AD和LDAP的基础知识。
|
2月前
|
XML Java 关系型数据库
springboot 集成 mybatis-plus 代码生成器
本文介绍了如何在Spring Boot项目中集成MyBatis-Plus代码生成器,包括导入相关依赖坐标、配置快速代码生成器以及自定义代码生成器模板的步骤和代码示例,旨在提高开发效率,快速生成Entity、Mapper、Mapper XML、Service、Controller等代码。
springboot 集成 mybatis-plus 代码生成器
|
1月前
|
Java Shell 开发工具
git集成IDEA,托管项目实现版本管理
git集成IDEA,托管项目实现版本管理
33 0
|
1月前
|
jenkins Shell 持续交付
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(一)
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(一)
149 0
|
2月前
|
存储 NoSQL 数据处理
组合和继承怎么集成一个性能较好的项目
组合与继承是面向对象编程的核心概念,前者通过对象间关联实现高效解耦,后者则重用代码以节省空间和内存。组合常用于现代项目,利用代理与依赖注入简化代码管理;而继承简化了子模块对父模块资源的应用,但修改会影响整体。随着分层解耦及微服务架构如SpringCloud的出现,这些技术进一步优化了数据处理效率和服务响应性能,尤其在分布式存储与高并发场景下。同步异步调用、Redis分布式应用等也广泛运用组合与继承,实现代码和内存空间的有效复用。