MyBatis 操作数据库

前言

之前我们 Dao层 的数据是 Mock的 (即虚拟的), 真实的数据应该从数据库中读取.
我们学习 MySQL 数据库时,已经学习了JDBC来操作数据库, 但是JDBC操作太 复杂 了.

JDBC 操作⽰例回顾

我们先来回顾⼀下 JDBC 的操作流程:
  1.  创建数据库连接池 DataSource
  2. 通过 DataSource 获取数据库连接 Connection
  3. 编写要执⾏带 ? 占位符的 SQL 语句
  4. 通过 Connection 及 SQL 创建操作命令对象 Statement
  5. 替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值
  6. 使⽤ Statement 执⾏ SQL 语句
  7. 查询操作:返回结果集 ResultSet,更新操作:返回更新的数量
  8. 处理结果集
  9. 释放资源
代码实例 
下⾯的⼀个完整案例,展⽰了通过 JDBC 的 API 向数据库中添加⼀条记录,修改⼀条记录,查询⼀条记录的操作。
-- 创建数据库
create database if not exists library default character set utf8mb4;
-- 使⽤数据库
use library;
-- 创建表
create table if not exists soft_bookrack (
 book_name varchar(32) NOT NULL,
 book_author varchar(32) NOT NULL,
 book_isbn varchar(32) NOT NULL primary key
);

以下是 JDBC 操作的具体实现代码:
package com.example.demo.mapper;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class SimpleJdbcOperation {
 private final DataSource dataSource;
 public SimpleJdbcOperation(DataSource dataSource) {
 this.dataSource = dataSource;
 }
 /**
 * 添加⼀本书
 */
 public void addBook() {
 Connection connection = null;
 PreparedStatement stmt = null;
 try {
 //获取数据库连接
 connection = dataSource.getConnection();
 //创建语句
 stmt = connection.prepareStatement(
 "insert into soft_bookrack (book_name, book_author, 
book_isbn) values (?,?,?);"
 );
 //参数绑定
 stmt.setString(1, "Spring in Action");
 stmt.setString(2, "Craig Walls");
 stmt.setString(3, "9787115417305");
 //执⾏语句
 stmt.execute();
} catch (SQLException e) {
 //处理异常信息
 } finally {
 //清理资源
 try {
 if (stmt != null) {
 stmt.close();
 }
 if (connection != null) {
 connection.close();
 }
 } catch (SQLException e) {
 //
 }
 }
 }


 /**
 * 更新⼀本书
 */

 public void updateBook() {
 Connection connection = null;
 PreparedStatement stmt = null;
 try {
 //获取数据库连接
 connection = dataSource.getConnection();
 //创建语句
 stmt = connection.prepareStatement(
 "update soft_bookrack set book_author=? where book_isbn=?;"
 );
 //参数绑定
 stmt.setString(1, "张卫滨");
 stmt.setString(2, "9787115417305");
 //执⾏语句
 stmt.execute();
 } catch (SQLException e) {
 //处理异常信息
 } finally {
 //清理资源
 try {
 if (stmt != null) {
 stmt.close();
 }
 if (connection != null) {
 connection.close();
 }
 } catch (SQLException e) {
//
 }
 }
 }
 /**
 * 查询⼀本书
 */
 public void queryBook() {
 Connection connection = null;
 PreparedStatement stmt = null;
 ResultSet rs = null;
 Book book = null;
 try {
 //获取数据库连接
 connection = dataSource.getConnection();
 //创建语句
 stmt = connection.prepareStatement(
 "select book_name, book_author, book_isbn from 
soft_bookrack where book_isbn =?"
 );
 //参数绑定
 stmt.setString(1, "9787115417305");
 //执⾏语句
 rs = stmt.executeQuery();
 if (rs.next()) {
 book = new Book();
 book.setName(rs.getString("book_name"));
 book.setAuthor(rs.getString("book_author"));
 book.setIsbn(rs.getString("book_isbn"));
 }
 System.out.println(book);
 } catch (SQLException e) {
 //处理异常信息
 } finally {
 //清理资源
 try {
 if (rs != null) {
 rs.close();
 }
 if (stmt != null) {
 stmt.close();
 }
 if (connection != null) {
 connection.close();
 }
 } catch (SQLException e) {
//
 }
 }
 }
 public static class Book {
 private String name;
 private String author;
 private String isbn;
 //省略 setter getter ⽅法
 }
}
从上述代码和操作流程可以看出,对于 JDBC 来说,整个操作⾮常的繁琐,我们不但要拼接每⼀个参数,⽽且还要按照模板代码的⽅式,⼀步步的操作数据库,并且在每次操作完,还要⼿动关闭连接 等,⽽所有的这些操作步骤都需要在每个⽅法中重复书写. 那有没有⼀种⽅法,可以更简单、更⽅便的操作数据库呢?
答案是肯定的,这就是我们要学习 MyBatis 的真正原因,它可以帮助我们更⽅便、更快速的操作数据库.

正文

1.什么是MyBatis?

  • MyBatis是⼀款优秀的 持久层 框架,⽤于简化JDBC的开发。
  • MyBatis本是 Apache的⼀个开源项⽬iBatis,2010年这个项⽬由apache迁移到了google code,并且改名为MyBatis 。2013年11⽉迁移到Github。
  • 官⽹:mybatis ‒ MyBatis 3 | 简介

持久层:指的就是持久化操作的层, 通常指数据访问层(dao), 是⽤来操作数据库的.

2.MyBatis使用

Mybatis操作数据库的步骤:
  1. 准备⼯作(创建springboot⼯程、数据库表准备、实体类)
  2. 引⼊Mybatis的相关依赖,配置Mybatis(数据库连接信息)
  3. 编写SQL语句(这里我只演示注解,XML下一篇博客演示)
  4. 测试

2.1 准备⼯作

 2.1.1 创建⼯程
创建springboot⼯程,并导⼊ mybatis的起步依赖、mysql的驱动包
2.1.2 数据准备
创建⽤⼾表, 并创建对应的实体类User
数据库中
-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
-- 使⽤数据数据
USE mybatis_test;
-- 创建表[⽤⼾表]
DROP TABLE IF EXISTS userinfo;
CREATE TABLE `userinfo` (
 `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
 `username` VARCHAR ( 127 ) NOT NULL,
 `password` VARCHAR ( 127 ) NOT NULL,
 `age` TINYINT ( 4 ) NOT NULL,
 `gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-⼥ 0-默认',
 `phone` VARCHAR ( 15 ) DEFAULT NULL,
 `delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
 `create_time` DATETIME DEFAULT now(),
 `update_time` DATETIME DEFAULT now(),
 PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4; 
-- 添加⽤⼾信息
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );

项目中 

实体类的属性名与表中的字段名⼀⼀对应

import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
 private Integer id;
 private String username;
 private String password;
 private Integer age;
 private Integer gender;
 private String phone;
 private Integer deleteFlag;
 private Date createTime;
 private Date updateTime;
}

2.2 配置数据库连接字符串


Mybatis中要连接数据库,需要数据库相关参数配置
  • MySQL驱动类
  • 登录名
  • 密码
  • 数据库连接字符串

如果是application.yml⽂件, 配置内容如下: 

# 数据库连接配置
spring:
 datasource:
 url: jdbc:mysql://127.0.0.1:3306/mybatis_test?
characterEncoding=utf8&useSSL=false
 username: root
 password: root
 driver-class-name: com.mysql.cj.jdbc.Driver

//用户名和密码改成你自己的,一般用户名没有改就都是root

2.3 写持久层代码  

在项⽬中, 创建持久层接⼝UserInfoMapper

@Mapper
public interface UserInfoMapper {
 //查询所有⽤⼾
 @Select("select username, `password`, age, gender, phone from userinfo")
 public List<UserInfo> queryAllUser();
}
Mybatis的持久层接⼝规范⼀般都叫 XxxMapper
@Mapper注解:表⽰是MyBatis中的Mapper接⼝
  • 程序运⾏时, 框架会⾃动⽣成接⼝的实现类对象(代理对象),并给交Spring的IOC容器管理
  • @Select注解:代表的就是select查询,也就是注解对应⽅法的具体实现内容

2.4 单元测试 

在创建出来的SpringBoot⼯程中,在src下的test⽬录下,已经⾃动帮我们创建好了测试类 ,我们可以直接使⽤这个测试类来进⾏测试.
使⽤Idea ⾃动⽣成测试类
除此之外, 也可以使⽤Idea⾃动⽣成测试类
1. 在需要测试的Mapper接⼝中, 右键 -> Generate -> Test
2. 选择要测试的⽅法, 点击 OK
3. 书写测试代码
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class UserInfoMapperTest {
 @Autowired
 private UserInfoMapper userInfoMapper;
 @Test
 void queryAllUser() {
 List<UserInfo> userInfoList = userInfoMapper.queryAllUser();
 System.out.println(userInfoList);
 }
}
测试类上添加了注解 @SpringBootTest,该测试类在运⾏时,就会⾃动加载Spring的运⾏环境.

我们通过@Autowired这个注解, 注⼊我们要测试的类, 就可以开始进⾏测试了

运⾏结果:
3. MyBatis的基础操作
上⾯我们学习了Mybatis的查询操作, 接下来我们学习MyBatis的增, 删, 改操作
在学习这些操作之前, 我们先来学习MyBatis⽇志打印
3.1 打印⽇志
在Mybatis当中我们可以借助⽇志, 查看到sql语句的执⾏、执⾏传递的参数以及执⾏结果
在配置⽂件中进⾏配置即可

3. MyBatis的基础操作

3.1 打印⽇志
在Mybatis当中我们可以借助⽇志, 查看到sql语句的执⾏、执⾏传递的参数以及执⾏结果
在配置⽂件中进⾏配置即可

mybatis:
 configuration: # 配置打印 MyBatis⽇志
 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
重新运⾏程序, 可以看到SQL执⾏内容, 以及传递参数和执⾏结果
  • ①: 查询语句
  • ②: 传递参数及类型
  • ③: SQL执⾏结果
3.2 参数传递
在queryById⽅法中添加⼀个参数(id),将⽅法中的参数,传给SQL语句使⽤ #{} 的⽅式获取⽅法中的参数.

@Select("select username, `password`, age, gender, phone from userinfo where 
id= #{id} ")
UserInfo queryById(Integer id);
如果mapper接⼝⽅法形参只有⼀个普通类型的参数,#{…} ⾥⾯的属性名可以随便写,如:#{id}、#
{value}。建议和参数名保持⼀致

添加测试⽤例

@Test
void queryById() {
 UserInfo userInfo = userInfoMapper.queryById(4);
 System.out.println(userInfo);
}

 运⾏结果:

也可以通过 @Param , 设置参数的别名, 如果使⽤ @Param 设置别名, #{...}⾥⾯的属性名必须和
@Param 设置的⼀样
@Select("select username, `password`, age, gender, phone from userinfo where 
id= #{userid} ")
UserInfo queryById(@Param("userid") Integer id);
3.3 增(Insert)

SQL 语句:

@Insert("insert into userinfo (username, `password`, age, gender, phone) 
values (#{username},#{password},#{age},#{gender},#{phone})")
Integer insert(UserInfo userInfo);
直接使⽤UserInfo对象的属性名来获取参数
测试代码:
@Test
void insert() {
 UserInfo userInfo = new UserInfo();
 userInfo.setUsername("zhaoliu");
 userInfo.setPassword("zhaoliu");
 userInfo.setGender(2);
 userInfo.setAge(21);
 userInfo.setPhone("18612340005");
 userInfoMapper.insert(userInfo);
}
如果设置了 @Param 属性, #{...} 需要使⽤ 参数.属性 来获取
@Insert("insert into userinfo (username, `password`, age, gender, phone) 
values (#{userinfo.username},#{userinfo.password},#{userinfo.age},#
{userinfo.gender},#{userinfo.phone})")
Integer insert(@Param("userinfo") UserInfo userInfo);
返回主键
Insert 语句默认返回的是 受影响的⾏数
但有些情况下, 数据插⼊之后, 还需要有后续的关联操作, 需要获取到新插⼊数据的id
⽐如订单系统
当我们下完订单之后, 需要通知物流系统, 库存系统, 结算系统等, 这时候就需要拿到订单ID
如果想要拿到⾃增id, 需要在Mapper接⼝的⽅法上添加⼀个Options的注解
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into userinfo (username, age, gender, phone) values (#
{userinfo.username},#{userinfo.age},#{userinfo.gender},#{userinfo.phone})")
Integer insert(@Param("userinfo") UserInfo userInfo);
  • useGeneratedKeys:这会令 MyBatis 使⽤ JDBC 的 getGeneratedKeys ⽅法来取出由数据库内部⽣成的主键(⽐如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的⾃动递增字段),默认值:false.
  • keyProperty:指定能够唯⼀识别对象的属性,MyBatis 会使⽤ getGeneratedKeys 的返回值或 insert 语句的 selectKey ⼦元素设置它的值,默认值:未设置(unset)
测试数据:
@Test
void insert() {
 UserInfo userInfo = new UserInfo();
 userInfo.setUsername("zhaoliu");
 userInfo.setPassword("zhaoliu");
 userInfo.setGender(2);
 userInfo.setAge(21);
 userInfo.setPhone("18612340005");
 Integer count = userInfoMapper.insert(userInfo);
 System.out.println("添加数据条数:" +count +", 数据ID:" + userInfo.getId());
}
运⾏结果:
注意: 设置 useGeneratedKeys=true 之后, ⽅法返回值依然是受影响的⾏数, ⾃增id 会设置在上
述 keyProperty 指定的属性中.
3.4 删(Delete)
 @Delete("delete from userinfo where id = #{id}")
void delete(Integer id);
3.5 改(Update)
@Update("update userinfo set username=#{username} where id=#{id}")
void update(UserInfo userInfo);
3.6 查(Select)  
接下来我们多查询⼀些数据
@Select("select id, username, `password`, age, gender, phone, delete_flag, 
create_time, update_time from userinfo")
List<UserInfo> queryAllUser();
查询结果:
从运⾏结果上可以看到, 我们SQL语句中, 查询了delete_flag, create_time, update_time, 但是这⼏个属性却没有赋值.
MyBatis 会根据⽅法的返回结果进⾏赋值.
⽅法⽤对象 UserInfo接收返回结果, MySQL 查询出来数据为⼀个, 就会⾃动赋值给对象.
⽅法⽤List<UserInfo>接收返回结果, MySQL 查询出来数据为⼀个或多个时, 也会⾃动赋值给List.
但如果MySQL 查询返回多个, 但是⽅法使⽤UserInfo接收, MyBatis执⾏就会报错.
原因分析:
当⾃动映射查询结果时,MyBatis 会获取结果中返回的列名并在 Java 类中查找相同名字的属性(忽略⼤⼩写)。 这意味着如果发现了 ID 列和 id 属性,MyBatis 会将列 ID 的值赋给 id 属性
解决办法:
1. 起别名
 @Select("select id, username, `password`, age, gender, phone, delete_flag as 
deleteFlag, " +
2 "create_time as createTime, update_time as updateTime from userinfo")
3 public List<UserInfo> queryAllUser();

2. 结果映射
@Select("select id, username, `password`, age, gender, phone, delete_flag, 
create_time, update_time from userinfo")
@Results({
 @Result(column = "delete_flag",property = "deleteFlag"),
 @Result(column = "create_time",property = "createTime"),
 @Result(column = "update_time",property = "updateTime")
})
List<UserInfo> queryAllUser();
如果其他SQL, 也希望可以复⽤这个映射关系, 可以给这个Results定义⼀个名称
@Select("select id, username, `password`, age, gender, phone, delete_flag, 
create_time, update_time from userinfo")
@Results(id = "resultMap",value = {
 @Result(column = "delete_flag",property = "deleteFlag"),
 @Result(column = "create_time",property = "createTime"),
 @Result(column = "update_time",property = "updateTime")
})
List<UserInfo> queryAllUser();
@Select("select id, username, `password`, age, gender, phone, delete_flag, 
create_time, update_time " +
 "from userinfo where id= #{userid} ")
@ResultMap(value = "resultMap")
UserInfo queryById(@Param("userid") Integer id);
使⽤ id 属性给该 Results 定义别名, 使⽤ @ResultMap 注解来复⽤其他定义的 ResultMap
3. 开启驼峰命名
通常数据库列使⽤蛇形命名法进⾏命名(下划线分割各个单词), ⽽ Java 属性⼀般遵循驼峰命名法约定.
为了在这两种命名⽅式之间启⽤⾃动映射,需要将 mapUnderscoreToCamelCase 设置为 true
mybatis:
 configuration:
 map-underscore-to-camel-case: true #配置驼峰⾃动转换

驼峰命名规则: abc_xyz => abcXyz
  • 表中字段名:abc_xyz
  • 类中属性名:abcXyz

Java 代码不做任何处理

@Select("select id, username, `password`, age, gender, phone, delete_flag as 
deleteFlag, " +
 "create_time as createTime, update_time as updateTime from userinfo")
public List<UserInfo> queryAllUser();

添加上述配置, 运⾏代码:

 字段全部进⾏正确赋值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值