MyBatis:热门的持久层框架

事先声明

本人根据 bilibili 视频博主楠哥教你学Java所发布的视频【极简入门】4小时学会MyBatis的课程内容进行学习及整理

本视频应该是我在B站找到的单独讲解MyBatis的所有视频中最精简,最快速的学习视频,非常适合于我一样,马上毕业了的大四小白🐶

在这里进行强烈推荐

本人为了便于学习,将其挂在了我的个人网站上,如对UP主的权益有所侵犯,请通过admin@xon.ink与我联系

MyBatis简介

MyBatis是 Apache 的一个开源项目 iBatis ,2010年这个项目由 Apache Software Foundation迁移到了 Google Code,并改名为 MyBatis,2013年11月迁移到了 Github

现在的MyBatis中依然有很多依然用iBatis命名的包名,没有改过来

MyBatis 是一个是一个实现了数据持久化的开源框架,简单理解就是对 JDBC 进行了封装

ORMapping:对象关系映射

ORMapping:对象关系映射 (Object Relationship Mapping)

对象指面向对象(Java)

关系指关系型数据库(MySQL或关系型数据库)

Java到MySQL的映射,开发者可以以面向对象的思想来管理数据库

MyBatis的优点

  • 与JDBC相比,减少了50%以上的代码量
  • MyBatis是最简单的持久化框架,小巧并且简单易学
  • MyBatis相当灵活,不会对应用程序或数据库现有的设计强加任何影响,SQL写在了XML中,从程序代码中彻底分离,降低耦合度,便于统一管理和优化,并可重用
  • 提供XML标签,支持动态编写SQL语句
  • 提供映射标签,支持对象与数据库的ORM字段关系映射

MyBatis的缺点

  • SQL语句的编写工作量较大,尤其是字段多、关联表多时,更是如此。对开发人员编写SQL语句的功底有一定的要求
  • SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库

MyBatis的核心接口和类

graph TD
SqlSessionFactoryBuilder -.build.-> SqlSessionFactory -.openSession.->SqlSession

MyBatis的开发方式

  • 使用原生接口
  • Mapper代理实现自定义接口

搭建MyBatis环境(IDEA)

创建简单的数据模型

因为是ORMapping

我们需要分别创建关系类型 (Relationship) ,以及对象 (Object) 类型的创建

并且两者的数据应该互相匹配在能在后期完成映射

创建Maven pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<configuration>
<dependencies>
<!-- 导入Mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<!-- 导入Mysql依赖(因为Mybatis基于JDBC) -->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<!-- 插入自定义规范依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<!-- Maven中无法读取main目录下的xml文件
所以在pom.xml文件中加入对应的<build>标签及内容 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</configuration>

创建数据表

1
2
3
4
5
6
create table t_account(
id int primary key auto_increment,
username varchar(11),
password varchar(11),
age int
)

创建对象

  1. 新建数据表对应的实体类Account
1
2
3
4
5
6
7
8
9
import lombok.Data;

@Data
public class Account {
private long id;
private String username;
private String password;
private int age;
}

创建MyBatis全局配置文件config.xml

文件名可以自定义

创建配置文件config.xml ,在项目src/main/resources目录中(Maven中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?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">
<!--XML头文件-->

<configuration>
<!-- 配置MyBatis运行环境 -->
<environments default="development">
<environment id="development">
<!-- 配置JDBC的事务管理 -->
<transactionManager type="JDBC"></transactionManager>

<!-- POOLED配置JDBC数据源链接池 -->
<dataSource type="POOLED">
<!-- 配置JDBC的四个步骤 -->
<!-- 1.配置JDBC驱动 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<!-- 2.配置JDBC链接路径
当然可以指定字符集,在链接末尾加入
?useUnicode=true&amp;characterEncoding=UTF-8 -->
<property name="url" value="jdbc:mysql://localhost:3306/databaseName"></property>
<!-- 3.配置数据库的用户名 -->
<property name="username" value="root"></property>
<!-- 4.配置数据库的密码 -->
<property name="password" value="1234"></property>
</dataSource>
</environment>

<!-- 为了适应多个数据源的链接
environment标签必须包含在environments标签中
<environment id="development1">
<transactionManager type=""></transactionManager>
<dataSource type=""></dataSource>
</environment>
-->
</environments>
</configuration>
graph TD
subgraph config.xml
    mysql(MySQL)
    subgraph environments
      transactionManager -.配置JDBC事务.-> JDBC
      dataSource -.配置JDBC数据池.-> JDBC
      JDBC --> mysql
  end
  subgraph mappers
      mapper -.注册mapper文件.-> mapper.xml
  end
end

使用原生接口

Mybatis框架需要开发者自定义SQL语句,写在Mapper.xml文件中,在实际开发中,会为每个实体类创建对应的Mapper.xml,定义管理该对象数据的SQL

新建对应类的Mapper

新建放置mapper文件的包,里面统一放置Mapper的xml文件

在包中新建一个xml文件,命名为XXXXMapper.xml,名字应与类对应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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">
<!-- XML头文件 -->

<mapper namespace="com.xon.mapper.AccountMapper">
<!-- Mybatis的每种操作在Mapper文件中都有对应的标签-->

<!-- 表添加动作的标签 -->
<insert id=""></insert>
<!-- 表修改动作的标签 -->
<update id=""></update>
<!-- 表删除动作的标签 -->
<delete id=""></delete>
<!-- 表查询动作的标签 -->
<select id=""></select>
</mapper>
  • namespace 通常设置为文件所在包+文件名的形式

  • 具体例子

    1
    2
    3
    4
    5
    6
    <mapper namespace="com.xon.mapper.AccountMapper">
    <insert id="save" parameterType="com.xon.entity.Account">
    insert into t_account(username,password,age)
    values(#{username},#{password},#{age})
    </insert>
    </mapper>
    • id 是实际调用Mybatis方式时需要调用的参数
    • parameterType 是调用对应方法时参数的数据类型

在全局配置中注册Mapper

config.xml文件中添加相应标签注册AccountMapper.xml

1
2
3
4
5
6
<configuration>
<!-- 在底部继续添加 -->
<mappers>
<mapper resource="com/xon/mapper/AccountMapper.xml"></mapper>
</mappers>
</configuration>

因为注册文件的路径需要添加.xml文件的后缀。如果用符号.对文件目录进行分割的话,就无法区分后缀还是子目录

所以目录之间用 / 代替

调用原生接口执行添加操作

新建放置repository文件的包(未完成)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class DemoByNative {
public static void main(String[] args) {
//加载MyBatis配置文件
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//指定Mapper的位置 路径为 namespace.id
String statement = "com.xon.mapper.AccoutMapper.save";
//创建数据对象
Account account = new Account(1L,"张三","123123",22);
//执行添加操作(Mapper路径,添加的数据)
sqlSession.insert(statement,account);
//提交事物
sqlSession.commit();
//关闭
sqlSession.close();
}
}

Mapper代理实现自定义接口

  • 自定义接口,定义相关业务方法
  • 编写与方法相对应的Mapper.xml
  • 在接口中声明对象 –> 在mapper.xml中添加方法8

自定义接口

创建包repository

创建接口 AccountRepository

1
2
3
4
5
6
7
8
9
10
11
12
public interface AccountRepository {
// 实现增加的方法
public int save(Account account);
// 实现修改的方法
public int update(Account account);
// 实现删除的方法,通过查找id的方式
public int deleteById(long id);
// 实现查询的方法
public List<Account> findAll();
// 实现查询的方法,通过查找id的方式
public Account findById(long id);
}

创建接口对应的Mapper.xml

定义接口方法对应的SQL语句

<statement> 标签可以根据SQL执行的业务选择 insertdeleteupdateselect

Mybatis 框架会根据规则自动创建接口实现类的代理对象

在repository中创建AccountRepository.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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">
<!-- XML头文件 -->

<mapper namespace="com.xon.repository.AccountRepository">
<insert id="save" parameterType="com.xon.entity.Account">
insert into t_account(username,password,age) values(#{username},#{password},#{age})
</insert>
<update id="update" parameterType="com.xon.entity.Account">
update t_account set username = #{username},password = #{password},age = #{age} where id = #{id}
</update>
<delete id="deleteById" parameterType="long">
delete from t_account where id = #{id}
</delete>
<select id="findAll" resultType="com.xon.entity.Account">
select * from t_account
</select>
<select id="findById" parameterType="long" resultType="com.xon.entity.Account">
select * from t_account where id = #{id}
</select>
</mapper>

规则

  • Mapper.xml 中 namespace.xml 为接口的全类名
  • Mapper.xml 中 statement 的 id 为接口中对应的方法名
  • Mapper.xml 中 statement 的 parameterType 和接口中对应的方法参数一致
  • Mapper.xml 中 statement 的 resultType 和接口中对应方法的返回值类型一致

小知识

insertdelete 返回执行语句的行数,肯定为int所以不用指定返回类型

delete中的parameterType也可以写全”java.lang.long”

查询语句不应该写<List> 应该写Account的范型

在全局配置文件中注册Mapper

1
2
3
4
<configuration>
<!-- 在底部继续添加 -->
<mappers resource="com/xon/repository/AccountRepository.xml"></mappers>
</configuration>

调用代理接口执行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class DemoByProxy {
public static void main(String[] args) {
//加载MyBatis配置文件
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取实现接口的代理对象
AccountRepository accountRepository = sqlSession.getMapper(AccountRepository.class);
//添加对象
Account account = new Account(2L,"李四","123123",23)
accountRepository.save(account);
//增删改操作一定要提交事物
sqlSession.commit();
sql.close();

//查找所有
List<Account> list accountRespository = findAll();
for(Account account:list){
System.out.println(account);
sqlSession.close();
}

//通过Id查询
Account account = accountRespository.findById(3L);
System.out.println(account);
sql.close();

//修改对象
Account account = accountRespository.findById(3L);
account.setUsername("小明");
account.setPassword("000");
account.setAge(18);
int result = accountRespository.update(account);
System.out.println(result);
sqlSession.commit();
sqlSession.close();

//通过Id删除对象
int result = accountRespository.deleteById(3L);
System.out.println(result);
sqlSession.commit();
sqlSession.close();
}
}

Mapper.xml

statement 标签

  • select : 查询
  • update : 修改
  • delete : 删除
  • insert : 添加

parameterType 参数数据类型

  • 基本数据类型通过Id查询 Account

    1
    2
    3
    <select id="findById" parameterType="long" resultType="com.xon.entity.Account">
    select * from t_account where id = #{id}
    </select>
  • String类型,通过 name 查询 Account

    1
    2
    3
    <select id="findByName" parameterType="java.lang.String"resultType="com.southwind.entity.Account">
    select * from t_account where username = #{username}
    </select>

    想实现name查找,在别忘了在接口中定义方法

  • 包装类,通过id查询Account

    1
    2
    3
    <select id="findById2" parameterType="java.lang.Long" resultType="com.southwind.entity.Account">
    select * from t_account where id = #{id}
    </select>

    其实和id查询的区别就是把long 换成了对象

    好处就是可以返回null值了

  • 多个参数,通过nameage查询 Account

    1
    2
    3
    <select id="findByNameAndAge" resultType="com.southwind.entity.Account">
    select * from t_account where username = #{param1} and age = #{param2}
    </select>

    因为多个参数无法指定一种类型,所以不用写参数类型

    sql中通过下标的方式来指定参数,下面是指定下标的两种方式

    • param:下标从 1 开始,{param1}{param2}
    • arg : 下标从 0 开始,{arg0}{arg1}
  • JavaBean

    1
    2
    3
    <update id="update" parameterType="com.xon.entity.Account">
    update t_account set username = #{username},password = #{password},age = #{age} where id = #{id}
    </update>

resultType 结果类型

  • 基本数据类型,统计Account总数

    1
    2
    3
    <select id="count" resultType="int">
    select count(id) from t_account
    </select>
  • 包装类,统计 Account 总数

    1
    2
    3
    <select id="count" resultType="java.lang.Integer">
    select count(id) from t_account
    </select>
  • String 类型,通过 id 查询 Account 的 name

    1
    2
    3
    <select id="findNameById" resultType="java.lang.String">
    select username from t_account where id = #{id}
    </select>
  • Java Bean

    1
    2
    3
    <select id="findById" parameterType="long" resultType="com.xon.entity.Account">
    select * from t_account where id = #{id}
    </select>

级联查询

一对多关系

请我喝杯咖啡吧~

支付宝
微信