MyBatis入门到实践

MyBatis入门到实践

概述

  • MyBatis是一款优秀的 持久层(Dao) 框架,用于简化JDBC的开发。
  • MyBatis本是Apache的一个开源项目iBatis,2010年这个项目由apache迁移到了googlecode,并且改名为MyBatis。2013年11月迁移到Github。
  • 官网:The MyBatis Blog

MyBatis入门

快速入门

使用Mybatis查询所有用户数据

  1. 准备工作(创建springboot工程、数据库表user、实体类User)

MyBatis FrameworkMySQL Driver

1
2
3
4
5
6
7
8
9
10
11
// com.pojo.User

// ptg插件一键生成JavaBean
// 有参/无参构造器、Getter&Setter、ToString
public class User{
private Integer id;
private String name;
private short age;
private short gender;
private String phone;
}
  1. 引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)

Data SourceDrivers

1
2
3
4
5
6
# resources.application.properties

spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/数据库名
spring.datasource.username = root
spring.datasource.password = 1234
  1. 编写SQL语句(注解/XML)
1
2
3
4
5
6
7
//com.mapper.UserMapper

@Mapper//在运行时,会自动实现该接口的实现类对象(代理对象),并且将该对象交给IOC容器管理
public interface UserMapper{
@Select("select * from user")
public List<User> list();
}
  1. 测试

Show Context Actions->inject language or reference->MySQL(SQL)——配置SQL语句联想报错提示

Data Source->MySQL->DataBase——配置数据库连接后表名、列名…联想提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootTest //springboot整合单元测试的注解
class SpringbootMybatisQuickstartApplictionTests {

@Autowired
private UserMapper userMapper;

@Test
public void testListUser() {
List<User> userList = userMapper.list();
userList.stream().forEach(user -> {
System.out.println(user);
})
}
}

JDBC介绍

什么是JDBC

JDBC:(Java Data Base Connectivity),就是使用Java语言操作关系型数据库的一套APl。

  • Sun公司官方定义的一套操作所有关系型数据库的规范,即接口。
  • 各个数据库厂商去实现这套接口,提供数据库 驱动jar包
  • 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

JDBC示例代码

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
//1.注册驱动
Class.forName("com/mysql.cj.jdbc.Driver");

//2.获取连接
String url = "jdbc:mysql://localhost:3306/mybatis";
String username = "root";
String password = "1234";
Connection conn= DriverManager.getConnection(url,username,password);

//3.获取执行SQL的对象Statement,执行SQL
String sql= "select * from user";
Statementstatement= conn.createstatement();
ResultSet resultSet= statement.executeQuery(sql);
List<User> userList= new ArrayList<>();
while (resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name );
short age = resuItSet.getShort("age");
short gender= resultSet.getShort("gender");
String phone = resultSet.getString("phone");
Useruser= new user(id,name,age,gender,phone);
userList.add(user);
}

//4.释放资源
statement.close();
connection.close();

数据库连接池

什么是数据库连接池?

  • 数据库连接池是个容器,负责分配、管理数据库连接(Connection)
  • 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
  • 释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏

当某用户从数据库连接池获取连接后,一段时间内该连接一直处于闲置状态,该连接会重回连接池。

连接池的优势

  • 资源重用
  • 提升系统响应速度
  • 避免数据库连接遗漏

标准接口:DataSource

  • 官方(sun)提供的数据库连接池接口,由第三方组织实现此接口。
  • 功能:获取连接 Connection getConnection() throws SQLException
常见产品
  • C3P0
  • DBCP
  • Druid (德鲁伊)
    • Druid连接池是阿里巴巴开源的数据库连接池项目
    • 功能强大,性能优秀,是Java语言最好的数据库连接池之一
  • Hikari (Springboot默认)

切换Druid数据库连接池

官方网址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter

1
2
3
4
5
6
7
<!-- pom.xml -->

<dependency>
<groupld>com.alibaba</groupld>
<artifactld>druid-spring-boot-starter</artifactld>
<version>1.2.23</version>
</dependency>
1
2
3
4
5
6
# application.properties

spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/mybatis
spring.datasource.username = root
spring.datasource.password = 1234

Lombok

什么是Lombok?

  • Lombok是一个实用的Java类库,能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,并可以自动化生成日志变量,简化java开发、提高效率。

  • @Getter/@Setter——为所有的属性提供get/set方法

  • @ToString——会给类自动生成易阅读的toString方法

  • @EqualsAndHashCode——根据类所拥有的非静态字段自动重写equals方法和hashCode方法

  • @Data——提供了更综合的生成代码

@Getter+@Setter+@ToString+@EqualsAndHashCode

  • @NoArgsConstructor——为实体类生成 无参的构造器 方法

  • @AllArgsConstructor——为实体类生成除了static修饰的字段之外 全参的构造器 方法

  • @Slf4j ——自动生成日志记录

  • 在 Java 类上使用这个注解,Lombok 库会在编译时自动为该类添加一个 SLF4J(Simple Logging Facade for Java)日志变量,通常这个变量的名字是 log

1
2
3
4
5
6
7
8
9
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MyClass {
public void doSomething() {
log.info("Doing something...");
// 其他代码
}
}

Lombok依赖文件

Lombok插件(较新版IDEA自带): Settings->Plugins

1
2
3
4
5
6
<!-- pom.xml -->

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

MyBatis增删改查

需求案例:员工管理的需求开发

环境准备
  • 准备数据库表emp
1
2
3
4
5
6
7
8
9
10
11
12
13
create table emp (
id int unsigned primary key auto_increment comment 'ID',
username varchar(20) not null unique comment '用户名',
password varchar(32) default '123456' comment '密码',
name varchar(10) not null comment '姓名',
gender tinyint unsigned not null comment '性别,说明:1 男,2 女')
image varchar(300) comment '图像',
job tinyint unsigned comment '职位,说明:1 班主任,2 讲师,3 学工主管,4 教研主管 5 咨询师',
entrydate date comment '入职时间',
dept_id int unsigned comment '部门ID',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间',
)comment '员工表';
  • 创建一个新的springboot工程,选择引入对应的起步依赖(mybatis、mysql驱动、lombok)

LombokMySQL DriverMyBatis Framework

  • application.properties中引入数据库连接信息
1
2
3
4
5
6
# application.properties

spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/mybatis
spring.datasource.username = root
spring.datasource.password = 1234
  • 创建对应的实体类Emp(实体类属性采用驼峰命名)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// com.pojo.Emp
// 下划线命名改驼峰命名,如:update_time --> updateTime

@Data
@NoArgsConstuctor
@AllArgsConstuctor
public class Emp {
private Integer id;
private String username;
private String password;
private String name;
private Short gender;
private String image;
private Short job;
private LocalDate entrydate; //date-年月日
private Integer deptId;
private LocalDateTime createTime; //datetime-年月日时分秒
private LocalDateTime updateTime;
}
  • 准备Mapper接口EmpMapper
1
2
3
4
5
// com.mapper.EmpMapper

@Mapper
public interface EmpMapper{
}
增删改查
删除
  • 根据主键ID删除
  • 根据主键ID批量删除(动态SQL)
1
2
3
4
# application.properties

# 指定mybatis输出日志的位置,输出到控制台
mybatis.configuration.log-impl = org.apache.ibatis.logging.stdout.StdOutImpl
1
2
3
4
5
6
7
8
9
// com.mapper.EmpMapper

@Mapper
public interface EmpMapper{

// 根据ID删除数据
@Delete("delete from emp where id = #{id}")
public int delete(Integer id);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// test.java.com

@SpringBootTest
class SpringbootMybatisCrudApplicationTests {

@Autowired
private EmpMapper empMapper;

@Test
public void testDelete(){
int delete = empMapper.delete(17);
System.out.println("根据ID删除数据的记录数:"+delete)
}
}
新增
1
2
3
4
5
6
7
8
9
10
11
// com.mapper.EmpMapper

@Mapper
public interface EmpMapper{

// 新增员工
@Insert("insert into"+
"emp(username,name,gender,image,job,entrydate,dept_id,create_time,update_time)"
+"values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
public int insert(Emp emp);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// test.java.com

@SpringBootTest
class SpringbootMybatisCrudApplicationTests {

@Autowired
private EmpMapper empMapper;

@Test
public void testInsert(){
// 构造员工对象
Emp emp = new Emp();
emp.setUsername("Tom");
......
emp.setGender((short)1); //(short)1:一个类型转换表达式,将整数值1转换为short类型
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());

// 执行新增员工信息操作
empMapper.insert(emp);
}
}
新增(主键返回)

主键返回:在数据添加成功后,需要获取插入数据库数据的主键。

如:

  • 先保存套餐信息,并获取套餐ID
  • 再保存套餐菜品关联信息(需要记录套餐ID、菜品ID)
1
2
3
4
5
6
7
8
9
10
11
12
// com.mapper.EmpMapper

@Mapper
public interface EmpMapper{

// 新增员工并返回主键
@Options(useGeneratedKeys = true,keyProperty = "id") //将生成的主键保存在emp类的id参数中
@Insert("insert into"+
"emp(username,name,gender,image,job,entrydate,dept_id,create_time,update_time)"
+"values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
public int insert(Emp emp);
}

更新

1
2
3
4
5
6
7
8
9
// com.mapper.EmpMapper

@Mapper
public interface EmpMapper{

// 根据ID删除数据
@Update("update emp set username = #{username},name = #{name},gender = #{gender},image = #{image},job = #{job},entrydate = #{entrydate},dept_id = #{deptId},update_time = #{updateTime} where id = #{id}")
public int update(Emp emp);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// test.java.com

@SpringBootTest
class SpringbootMybatisCrudApplicationTests {

@Autowired
private EmpMapper empMapper;

@Test
public void testInsert(){
// 构造员工对象
Emp emp = new Emp();
emp.setId(18);
emp.setUsername("Tom");
......
emp.setGender((short)1); //(short)1:一个类型转换表达式,将整数值1转换为short类型
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setUpdateTime(LocalDateTime.now());

// 执行新增员工信息操作
empMapper.update(emp);
}
}
查询(根据主键ID查询)
1
2
3
4
5
6
7
8
9
// com.mapper.EmpMapper

@Mapper
public interface EmpMapper{

// 根据主键ID查询员工
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// test.java.com

@SpringBootTest
class SpringbootMybatisCrudApplicationTests {

@Autowired
private EmpMapper empMapper;

@Test
public void testSelect(){
Emp emp = empMapper.getById(20);
System.out.println(Emp emp);
//其中查询返回的deptId,createTime,updateTime均为null
}
}
数据封装
  • 实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。

  • 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。

  • 方案一:给字段起别名,让别名与实体类属性一致

1
@Select("select id,...,dept_id as deptId,create_time as createTime...")
  • 方案二:通过@Results注解手动映射封装
1
2
3
4
5
6
7
@Results({
@Result(column = "dept_id",property = "deptId"),
@Result(column = "create_time",property = "createTime"),
@Result(column = "update_time",property = "updateTime")
})
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);
  • 方案三:开启MyBatis的驼峰命名自动映射开关
1
2
3
# resources.application.properties
#开启驼峰命名自动映射,即从数据库字段名 a_column 映射到Java属性名 aColumn。
mybatis.configuration.map-underscore-to-camel-case = true
查询(根据条件查询)
  • 根据输入的员工姓名、员工性别、入职时间搜素满足条件的员工信息。
  • 其中员工姓名,支持模糊匹配;性别进行精确查询;入职时间进行范田查询。
  • 支持分页查询。
  • 并对查询的结果,根据最后修改时间进行倒序排序。
1
2
3
4
5
6
7
8
9
// com.mapper.EmpMapper

@Mapper
public interface EmpMapper{

// 根据主键ID查询员工
@Select("select * from emp where name like concat('%',#{name},'%')) and gender = #{gender} and entrydate between #{begin} and #{end} order by update_time DESC")
public List<Emp> list(String name,Short gender,LocalDate begin,LocalDate end);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// test.java.com

@SpringBootTest
class SpringbootMybatisCrudApplicationTests {

@Autowired
private EmpMapper empMapper;

@Test
public void testList(){
empMapper.list("张",(short)1,LocalDate.of(2010,1,1),LocalDate.of(2020,1,1));
}
}

XML映射文件

规范

  • XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)。
    • 创建包结构new directory->com/dhu/mapper

src.main.java.com.mapper.EmpMapper(Mapper接口)

src.main.resources.com.mapper.EmpMapper.xml(XML映射文件)

  • XML映射文件的namespace属性为Mapper接口全限定名一致。
  • XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致。

copy reference

1
2
3
4
5
6
// src.main.java.com.dhu.mapper.EmpMapper

@Mapper
public interface EmpMapper{
public List<Emp> list(String name,Short gender,LocalDate begin,LocalDate end);
}
1
2
3
4
5
6
7
<!-- src.main.resources.com.dhu.mapper.EmpMapper.xml -->

<mapper namespace="com.dhu.mapper.EmpMapper">
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and entrydate between #{begin} and #{end} order by update_time desc
</select>
</mapper>

MybatisX

  • MybatisX是一款基于IDEA的快速开发Mybatis的插件,为效率而生。
    • Settings -> Plugins
    • 跳转到对应的接口/方法

MyBatis动态SQL

  • 动态SQL:随着用户的输入或外部条件的变化而变化的SQL语句,我们称为动态SQL。

    <if>

    • 动态条件查询<where>
    1
    2
    3
    4
    5
    6
    // src.main.java.com.dhu.mapper.EmpMapper

    @Mapper
    public interface EmpMapper{
    public List<Emp> list(String name,Short gender,LocalDate begin,LocalDate end); //动态条件查询
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <!-- src.main.resources.com.dhu.mapper.EmpMapper.xml -->

    <mapper namespace="com.dhu.mapper.EmpMapper">
    <select id="list" resultType="com.itheima.pojo.Emp">
    select *
    from emp
    <where> //动态生成where关键字、自动按条件添加and
    <if test = "name != null"> //使用test属性进行条件判断,若条件为true则拼接
    name like concat('%',#{name},'%')
    </if>
    <if test = "gender != null">
    and gender = #{gender}
    </if>
    <if test = "begin != null and end != null">
    and entrydate between #{begin} and #{end}
    </if>
    </where>
    order by update_time desc
    </select>
    </mapper>
    • 动态更新<set>
    1
    2
    3
    4
    5
    6
    // src.main.java.com.dhu.mapper.EmpMapper

    @Mapper
    public interface EmpMapper{
    public void Update(Emp emp); //动态更新员工信息
    }
    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
    <!-- src.main.resources.com.dhu.mapper.EmpMapper.xml -->

    <mapper namespace="com.dhu.mapper.EmpMapper">
    <update id="Update" resultType="com.itheima.pojo.Emp">
    update emp
    <set>
    <if test = "username != null">
    username = #{username},
    </if>
    <if test = "name != null">
    name = #{name},
    </if>
    <if test = "gender != null">
    gender = #{gender},
    </if>
    <if test = "image != null">
    image = #{image},
    </if>
    <if test = "job != null">
    job = #{job},
    </if>
    <if test = "entrydate != null">
    entrydate = #{entrydate},
    </if>
    <if test = "deptId != null">
    dept_id = #{deptId},
    </if>
    <if test = "updateTime != null">
    update_time = #{updateTime}
    </if>
    </set>
    where id = #{id}
    </update>
    </mapper> =

    <foreach>

    • 批量删除
    1
    delete from emp where id in (18,19,20);
    1
    2
    3
    4
    5
    6
    // src.main.java.com.dhu.mapper.EmpMapper

    @Mapper
    public interface EmpMapper{
    public void deleteByIds(List<Integer> ids); //批量删除员工信息
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- src.main.resources.com.dhu.mapper.EmpMapper.xml -->
    <!--
    collection:遍历的集合名称
    item:遍历出来的元素
    separator:分隔符
    open:遍历开始前拼接的SQL片段
    close:遍历结束后拼接的SQL片段
    -->

    <mapper namespace="com.dhu.mapper.EmpMapper">
    <delete id="deleteByIds" resultType="com.itheima.pojo.Emp">
    delete from emp where id in
    <foreach collection = "ids" item = "id" separator = "," open = "(" close = ")">
    #{id}
    </foreach>
    </delete>
    </mapper>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // test.java.com

    @SpringBootTest
    class SpringbootMybatisCrudApplicationTests {

    @Autowired
    private EmpMapper empMapper;

    @Test
    public void testDeleteByIds(){
    List<Integer> ids = Arrays.asList(13,14,15);
    empMapper.deleteById;
    }
    }

    <sql><include>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <!-- src.main.resources.com.dhu.mapper.EmpMapper.xml -->

    <mapper namespace="com.dhu.mapper.EmpMapper">

    <sql id = "commonSelect"> // 封装SQL语句并指定ID
    select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
    </sql>

    <select id="list" resultType="com.itheima.pojo.Emp">
    <include refid="commonSelect"/> // 复用SQL语句
    <where>
    <if test = "name != null">
    name like concat('%',#{name},'%')
    </if>
    <if test = "gender != null">
    and gender = #{gender}
    </if>
    <if test = "begin != null and end != null">
    and entrydate between #{begin} and #{end}
    </if>
    </where>
    order by update_time desc
    </select>
    </mapper>

MyBatis入门到实践
https://maplelea1f.github.io/2024/10/16/MyBatis入门到实践/
作者
Maple
发布于
2024年10月16日
许可协议