MyBatis查询数据库

1.MyBatis 是什么?
MyBatis 是⼀款优秀的持久层框架,它⽀持⾃定义 SQL、存储过程以及⾼级映射。MyBatis 去除了⼏乎所有的 JDBC 代码以及设置参数和获取结果集的⼯作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接⼝和 Java POJO(Plain Old Java Objects,普通⽼式 Java 对象)为数据库中的记录。
简单来说 MyBatis 是更简单完成程序和数据库交互的⼯具,也就是更简单的操作和读取数据库⼯具
 

2.为什么学习MyBatis? 

对于后端开发来说,程序是由以下两个重要的部分组成的:

  • 后端程序
  • 数据库
     

⽽这两个重要的组成部分要通讯,就要依靠数据库连接⼯具,那数据库连接⼯具有哪些? JDBC, MyBatis,那已经有了 JDBC 了,为什么还要学习 MyBatis?这是因为 JDBC 的操作太繁琐了,我们回顾⼀下 JDBC 的操作流程:

  1. 创建数据库连接池 DataSource
  2. 通过 DataSource 获取数据库连接 Connection
  3. 编写要执⾏带 ? 占位符的 SQL 语句
  4. 通过 Connection 及 SQL 创建操作命令对象 Statement
  5. 替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值
  6. 使⽤ Statement 执⾏ SQL 语句
  7.  查询操作:返回结果集 ResultSet,更新操作:返回更新的数量
  8. 处理结果集
  9. 释放资源

 下⾯的⼀个完整案例,展示了通过 JDBC 的 API 向数据库中添加⼀条记录,修改⼀条记录,查询⼀条记录的操作。

  以下是 JDBC 操作的具体实现代码:

package com.example._20230103.javase;


import com.example._20230103.model.Book;

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 statement = null;
        try {
            //获取数据库连接
            connection = dataSource.getConnection();
            //创建语句
            statement = connection.prepareStatement("insert into soft_bookrack(book_name,book_author,book_isbn) values (?,?,?);");
            //参数绑定
            statement.setString(1, "Spring in Action");
            statement.setString(2, "Craig Walls");
            statement.setString(3, "9787115417305");
            //执行语句
            statement.execute();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (statement != null) {
                    statement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    //更行一本书
    public void updateBook() {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            //获取数据库连接
            connection = dataSource.getConnection();
            //创建语句
            statement = connection.prepareStatement("update soft_bookrack set book_author=? where book_isbn=?");
            //参数绑定
            statement.setString(1, "张卫滨");
            statement.setString(2, "975646515645615");
            statement.execute();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (statement != null) {
                    statement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    //查询一本书
    public void queryBook() {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        Book book = null;
        try {
            //获取数据库连接
            connection = dataSource.getConnection();
            //创建语句
            statement = connection.prepareStatement("select book_name,book_author,book_isbn from soft_bookrack where book_isbn = ?");
            //参数绑定
            statement.setString(1, "975646515645615");
            //执行语句
            resultSet = statement.executeQuery();
            if (resultSet.next()) {
                book = new Book();
                book.setName(resultSet.getString("book_name"));
                book.setAuthor(resultSet.getString("book_author"));
                book.setIsbn(resultSet.getString("book_isbn"));
            }
            System.out.println(book);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (statement != null) {
                    statement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}


 

引入mysql依赖

从上述代码和操作流程可以看出,对于 JDBC 来说,整个操作⾮常的繁琐,我们不但要拼接每⼀个参数,⽽且还要按照模板代码的⽅式,⼀步步的操作数据库,并且在每次操作完,还要⼿动关闭连接等,⽽所有的这些操作步骤都需要在每个⽅法中重复书写。于是我们就想,那有没有⼀种⽅法,可以更简单、更⽅便的操作数据库呢?
答案是肯定的,这就是我们要学习 MyBatis 的真正原因,它可以帮助我们更⽅便、更快速的操作数据库。
 

 3.怎么学MyBatis?

MyBatis 学习只分为两部分:

  • 配置 MyBatis 开发环境;
  • 使⽤ MyBatis 模式和语法操作数据库。
     

 4.第⼀个MyBatis查询

开始搭建 MyBatis 之前,我们先来看⼀下 MyBatis 在整个框架中的定位,框架交互流程图:
 

MyBatis 也是⼀个 ORM 框架,ORM(Object Relational Mapping),即对象关系映射。在⾯向对
象编程语⾔中,将关系型数据库中的数据与对象建⽴起映射关系,进⽽⾃动的完成数据与对象的
互相转换:

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

ORM 把数据库映射为对象:

  • 数据库表(table)--> 类(class)
  • 记录(record,⾏数据)--> 对象(object)
  • 字段(field) --> 对象的属性(attribute)

⼀般的 ORM 框架,会将数据库模型的每张表都映射为⼀个 Java 类。
也就是说使⽤ MyBatis 可以像操作对象⼀样来操作数据库中的表,可以实现对象和数据库表之间
的转换,接下来我们来看 MyBatis 的使⽤吧。

4.创建数据库和表

接下来我们要实现的功能是:使⽤ MyBatis 的⽅式来读取⽤户表中的所有⽤户,我们使⽤个⼈博
客的数据库和数据包,具体 SQL 如下。
 

-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8;
-- 使⽤数据库
use mycnblog;
-- 创建用户表
drop table if exists userinfo;
create table userinfo
(
    id         int primary key auto_increment,
    username   varchar(100) not null,
    password   varchar(32)  not null,
    photo      varchar(500) default '',
    createtime datetime     default now(),
    updatetime datetime     default now(),
    `state`    int          default 1
);

-- 创建⽂章表
drop table if exists articleinfo;
create table articleinfo
(
    id         int primary key auto_increment,
    title      varchar(100) not null,
    content    text         not null,
    createtime datetime              default now(),
    updatetime datetime              default now(),
    uid        int          not null,
    rcount     int          not null default 1,
    `state`    int                   default 1
);

-- 创建视频表
drop table if exists videoinfo;
create table videoinfo
(
    vid        int primary key,
    `title`    varchar(250),
    `url`      varchar(1000),
    createtime datetime default now(),
    updatetime datetime default now(),
    uid        int
);

-- 添加⼀个⽤户信息
INSERT INTO `userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`)
VALUES (1, 'admin', 'admin', '', '2023-01-05 17:10:48', '2023-01-05 19:10:48', 1);

-- ⽂章添加测试数据
insert into articleinfo(title, content, uid)
values ('Java', 'Java正⽂', 1);

-- 添加视频
insert into videoinfo(vid, title, url, uid)
values (1, 'javatitle', 'http://www.baidu.com', 1);

5.添加MyBatis框架⽀持

这个插件用来管理依赖。

 6.配置连接字符串

如果是 application.yml 添加如下内容:


如果使⽤ MySQL 是 5.x 之前的使⽤的是“com.mysql.jdbc.Driver”,如果是⼤于 5.x 使⽤的
是“com.mysql.cj.jdbc.Driver”。

7.配置 MyBatis 中的 XML 路径


MyBatis 的 XML 中保存是查询数据库的具体操作 SQL,配置如下:

<?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 = 你要实现的接口的路径-->
<mapper namespace="com.example._20230103.mapper.UserMapper">
    
</mapper>

8.添加业务代码

下⾯按照后端开发的⼯程思路,也就是下⾯的流程来实现 MyBatis 查询所有⽤户的功能:



 

9.添加实体类

10.添加 mapper 接⼝
 

11.添加 UserMapper.xml
 

以下是对以上标签的说明:
<mapper>标签:需要指定 namespace 属性,表示命名空间,值为 mapper 接⼝的全限定
名,包括全包名.类名。


<select>查询标签:是⽤来执⾏数据库的查询操作的:id:是和 Interface(接⼝)中定义的⽅法名称⼀样的,表示对接⼝的具体实现⽅法。resultType:是返回的数据类型,也就是开头我们定义的实体类

12.添加 Service
 

13.添加 Controller


 

14.使⽤ postman 测试


15.增、删、改操作


接下来,我们来实现⼀下⽤户的增加、删除和修改的操作,对应使⽤ MyBatis 的标签如下:

  • <insert>标签:插⼊语句
  • <update>标签:修改语句
  • <delete>标签:删除语句
     

 1.插入功能的实现:

 运行结果:

 2.修改功能的实现:

运行结果:

3.删除功能的实现:

 

运行结果:

16.参数占位符 #{} 和 ${}

  • #{}:预编译处理.
  • ${}:字符直接替换.
     

预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使⽤ PreparedStatement 的set ⽅法来赋值。直接替换:是MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。

17.${} 优点

 

 

 

 

运行结果:

 

使⽤ ${sort} 可以实现排序查询,⽽使⽤ #{sort} 就不能实现排序查询了,因为当使⽤ #{sort} 查询时,如果传递的值为 String 则会加单引号,就会导致 sql 错误。
 

 18.SQL 注⼊问题

 

 

 

 

运行结果:

 

 黑客能通过sql注入,能获得表中所有数据,还可以删库。

select * from userinfo where username= 'admin' and password='admin'
select * from userinfo where username= 'admin' and password='' or 1='1';

 使用${}存在安全隐患(SQL注入问题),而#{}不存在SQL注入问题。我们在满足特定场景下使用${}.

19.like查询

 

 

 

 运行结果:

 

 20.多表查询

如果是增、删、改返回搜影响的⾏数,那么在 mapper.xml 中是可以不设置返回的类型的,如下图所示:

 

 对于 <select> 查询标签来说⾄少需要两个属性:

  • id 属性:⽤于标识实现接⼝中的那个⽅法;
  • 结果映射属性:结果映射有两种实现标签:<resultMap> 和 <resultType>。
     

21.返回类型:resultType

绝⼤数查询场景可以使⽤ resultType 进⾏返回,如下代码所示:

<select id="getUserById" resultType="com.example._20230103.model.UserInfo">
        select * from userinfo where id=#{id}
</select>

它的优点是使⽤⽅便,直接定义到某个实体类即可.

22.返回字典映射:resultMap

resultMap 使⽤场景:

  • 字段名称和程序中的属性名不同的情况,可使⽤ resultMap 配置映射;
  • ⼀对⼀和⼀对多关系可以使⽤ resultMap 映射并查询数据

字段名和属性名不同的情况
 



 

 

 

运行结果:

 

可以明显看到查询不到titlename这个属性。这个时候就可以使⽤ resultMap 了,resultMap 的使⽤规则如下.

 

 

 

 

 运行结果:

 

23.⼀对⼀的表映射


 ⼀对⼀映射要使⽤ <association> 标签,具体实现如下(⼀篇⽂章只对应⼀个作者):

 

 

 

 

 

 

 

 

单元测试运行结果:

 

补充:把数据库文件发一下:

-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8;
-- 使⽤数据库
use mycnblog;
-- 创建用户表
drop table if exists userinfo;
create table userinfo
(
    id         int primary key auto_increment,
    username   varchar(100) not null,
    password   varchar(32)  not null,
    photo      varchar(500) default '',
    createtime datetime     default now(),
    updatetime datetime     default now(),
    `state`    int          default 1
);

-- 创建⽂章表
drop table if exists articleinfo;
create table articleinfo
(
    id         int primary key auto_increment,
    title      varchar(100) not null,
    content    text         not null,
    createtime datetime              default now(),
    updatetime datetime              default now(),
    uid        int          not null,
    rcount     int          not null default 1,
    `state`    int                   default 1
);

-- 创建视频表
drop table if exists videoinfo;
create table videoinfo
(
    vid        int primary key,
    `title`    varchar(250),
    `url`      varchar(1000),
    createtime datetime default now(),
    updatetime datetime default now(),
    uid        int
);

-- 添加⼀个⽤户信息
INSERT INTO `userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`)
VALUES (1, 'admin', 'admin', '', '2023-01-05 17:10:48', '2023-01-05 19:10:48', 1);

-- ⽂章添加测试数据
insert into articleinfo(title, content, uid)
values ('Java', 'Java正⽂', 1);

-- 添加视频
insert into videoinfo(vid, title, url, uid)
values (1, 'javatitle', 'http://www.baidu.com', 1);

 

columnPrefix 如果省略,并且恰好两个表中如果有相同的字段,那么就会导致查询出错,示例如下:

这是原来的样子:

 删除columnPrefix:

测试运行结果:

这是因为userinfo 表中有 id 字段,articleinfo 中也有⼀个 id 字段,会导致查询出错,正确的做法是添加 columnPrefix.

24.⼀对多:⼀个⽤户多篇⽂章案例


 

 

 

 

 

 

单元测试junit5运行结果:

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿瞒有我良计15

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值