MyBatis查询数据库

目录

1.MyBatis是什么?

2.为什么要学习 MyBatis?

3.怎么学MyBatis?

4.第⼀个MyBatis查询

4.1创建数据库表

4.2 添加MyBatis框架⽀持

4.3配置连接字符串和MyBatis

4.3.1配置连接字符串

4.3.2 配置 MyBatis 中的 XML 路径

4.4添加业务代码

4.4.1 添加实体类

4.4.2添加Mapper接口

4.4.3 添加 UserMapper.xml

4.4.4 添加 Service

4.4.5 添加 Controller

4.4.6 使⽤ postman 测试

4.4.7带有参数的查询

5.增、删、改操作

5.1增加操作

5.2 修改⽤户操作

5.3 删除⽤户操作

6.查询操作

6.1 单表查询

​编辑

​编辑

​编辑

6.1.1 参数占位符 #{} 和 ${}

6.1.2SQL注入问题

6.1.3${} 优点

6.1.4 like 查询

6.2多表查询

6.2.1 返回类型:resultType

6.2.2返回字典映射:resultMap

6.2.3 多表查询

6.2.3.1 ⼀对⼀的表映射

 7.复杂情况:动态SQL使⽤

7.1 标签

7.2 标签

7.3 标签

7.4 标签

 7.5 标签

​8.补充

1.MyBatis是什么?

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

2.为什么要学习 MyBatis?

对于后端开发来说,程序是由以下两个重要的部分组成的:
1. 后端程序
2. 数据库
⽽这两个重要的组成部分要通讯,就要依靠数据库连接⼯具,那数据库连接⼯具有哪些?⽐如之前我们学习的 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. 释放资源

3.怎么学MyBatis?

MyBatis 学习只分为两部分:
配置 MyBatis 开发环境;
使⽤ MyBatis 模式和语法操作数据库;

4.第⼀个MyBatis查询

开始搭建 MyBatis 之前,我们先来看⼀下 MyBatis 在整个框架中的定位,框架交互流程图:
MyBatis查询数据库_第1张图片
MyBatis 也是⼀个 ORM 框架, ORM(Object Relational Mapping),即对象关系映射。在⾯向
对象编程语⾔中,将关系型数据库中的数据与对象建⽴起映射关系,进⽽⾃动的完成数据与对象
的互相转换:
1. 将输⼊数据(即传⼊对象)+SQL 映射成原⽣ SQL
2. 将结果集映射为返回对象,即输出对象
ORM 把数据库映射为对象:
数据库表(table)--> 类(class)
记录(record,⾏数据)--> 对象(object)
字段(field) --> 对象的属性(attribute)
⼀般的 ORM 框架,会将数据库模型的每张表都映射为⼀个 Java 类。也就是说使⽤ MyBatis 可以像操作对象⼀样来操作数据库中的表,可以实现对象和数据库表之间的转换,接下来我们来看 MyBatis 的使⽤吧。

4.1创建数据库表

接下来我们要实现的功能是:使⽤ MyBatis 的⽅式来读取⽤户表中的所有⽤户,我们使⽤个⼈博
客的数据库和数据包,具体 SQL 如下。
-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;
-- 使⽤数据数据
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
) default charset 'utf8mb4';
-- 创建⽂章表
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
)default charset 'utf8mb4';
-- 创建视频表
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
)default charset 'utf8mb4';
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`,
`createtime`, `updatetime`, `state`) VALUES
(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1)
;
insert into articleinfo(title,content,uid)
 values('Java','Java正⽂',1);
insert into videoinfo(vid,title,url,uid) values(1,'java title','http://ww
w.baidu.com',1);
select * from userinfo;
select * from userinfo where username='admin' and password= '' or 1='1';
select * from userinfo ORDER BY id DESC;
select * from userinfo where username like CONCAT('%','m','%');

4.2 添加MyBatis框架⽀持

在创建新项目时,在添加Spring框架的基础上,加入MyBatis FrameWork和MySQL Driver框架支持。

4.3配置连接字符串和MyBatis

此步骤需要进⾏两项设置,数据库连接字符串设置和 MyBatis 的 XML ⽂件配置。

4.3.1配置连接字符串

application.yml文件中添加以下内容:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
    username: root
    password: 111111
    driver-class-name: com.mysql.cj.jdbc.Driver

4.3.2 配置 MyBatis 中的 XML 路径

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

#数据库配置
mybatis:
  mapper-locations: classpath:mapper/**Mapper.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

4.4添加业务代码

下⾯按照后端开发的⼯程思路,也就是下⾯的流程来实现 MyBatis 查询所有⽤户的功能:
MyBatis查询数据库_第2张图片

4.4.1 添加实体类

先添加用户的实体类,使用Data注解可以自己生成一些方法:

@Data
public class User {

    private Integer id;
    private String name;
    private String pwd;
    private String photo;
    private Date createtime;
    private Date updatetime;
    private Integer state;
}

4.4.2添加Mapper接口

@Mapper
public interface UserMapper {
    /**
     * 查询所有数据
     * @return
     */
    List queryAll();
}

4.4.3 添加 UserMapper.xml

数据持久成的实现,mybatis 的固定 xml 格式:






UserMapper.xml 查询所有⽤户的具体实现 SQL:





    


以下是对以上标签的说明:
标签:需要指定 namespace 属性,表示命名空间,值为 mapper 接⼝的全限定
名,包括全包名.类名。
select * from userinfo where id = ${uid}

把xml文件中的id改成uid,可以执行成功。

5.增、删、改操作

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

5.1增加操作

mapper接口:

Integer insert(User user);

xml实现:



        insert into userinfo (username,password,photo) values(#{username},#{password},#{photo})
    

使用Test方法实现结果:

如果设置了Param注解,就必须要使用Param注解的命名

Integer insert2(@Param("userinfo") User user);

        insert into userinfo (username,password,photo) values(#{userinfo.username},#{userinfo.password},#{userinfo.photo})
    

想要拿到自增id,可以在xml文件的insert标签里面添加以下内容:


        insert into userinfo (username,password,photo) values(#{userinfo.username},#{userinfo.password},#{userinfo.photo})
    
●useGeneratedKeys:这会令 MyBatis 使⽤ JDBC 的 getGeneratedKeys ⽅法来取出由数据
库内部⽣成的主键(⽐如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的⾃动
递增字段),默认值:false。
●keyColumn:设置⽣成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列
不是表中的第⼀列的时候,是必须设置的。如果⽣成列不⽌⼀个,可以⽤逗号分隔多个属性
名称。
●keyProperty:指定能够唯⼀识别对象的属性,MyBatis 会使⽤ getGeneratedKeys 的返回
值或 insert 语句的 selectKey ⼦元素设置它的值,默认值:未设置(unset)。如果⽣成列
不⽌⼀个,可以⽤逗号分隔多个属性名称。

5.2 修改⽤户操作

mapper接口:

void update(User user);
mapper.xml 实现代码:
    
        update userinfo set username=#{username},password=#{password} where id=#{id}
    

5.3 删除⽤户操作

mapper接口:

     /**
     * 删除数据
     */
    void delete(Integer id);

mapper.xml 实现代码:

    
        delete from userinfo where id=#{id}
    

6.查询操作

6.1 单表查询

上面我们已经实现了简单的单表查询操作,但我们的SQL语句属性都使用的是#,如果换成$会有什么结果呢?

根据id查询:

User queryById(@Param("uid") Integer id);
    

使用#查询结果:

MyBatis查询数据库_第4张图片

 使用$查询结果:

    

MyBatis查询数据库_第5张图片

根据name查询: 

User queryByName(String username);

使用#:

    

MyBatis查询数据库_第6张图片

使用$:

    

MyBatis查询数据库_第7张图片

为什么使用$就会出现异常呢?

6.1.1 参数占位符 #{} 和 ${}

●#{}:预编译处理。
●${}:字符直接替换。
预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使⽤ PreparedStatement
的 set ⽅法来赋值。直接替换:是MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。
预编译处理和字符串替换的区别故事(头等舱和经济舱乘机分离的故事):
在坐⻜机的时候头等舱和经济舱的区别是很⼤的,如下图所示:
⼀般航空公司乘机都是头等舱和经济舱分离的,头等舱的⼈先登机,登机完之后,封闭经济舱,然后再让经济舱的乘客登机,这样的好处是可以避免浑⽔摸⻥,经济舱的⼈混到头等舱的情况,这就相当于预处理,可以解决程序中不安全(越权处理)的问题。
MyBatis查询数据库_第8张图片
⽽直接替换的情况相当于,头等舱和经济舱不分离的情况,这样经济舱的乘客在通过安检之后可能越权摸到头等舱,如下图所示:
MyBatis查询数据库_第9张图片
这就相当于参数直接替换,它的问题是可能会带来越权查询和操作数据等问题,⽐如后⾯会讲的 SQL注⼊问题。

6.1.2SQL注入问题

User queryByNameAndPassword(@Param("username") String username,@Param("password") String password);
    
 @Test
    void queryByNameAndPassword() {
        String username="admin";
//        String password="admin";
        String password="' or 1='1";

        User user=userMapper.queryByNameAndPassword(username,password);
        log.info(user==null?null:user.toString());
    }

可以从上述的结果中发现,我们给密码赋值为“' or 1='1” ,即使不需要密码,也可以查出全部信息,这就会造成很严重的安全问题。

6.1.3${} 优点

当我们使用#{}进行排序操作时:

    
 
                   

 通过结果可以发现,当我们使用#操作时,会将传入排序的方法认为是想要查询的值,程序查不出来就会报错。

使用${}:

    


MyBatis查询数据库_第10张图片

 当我们使用${},程序就可以正常的查询出来。

所以排序时,只能使用${},为了防止SQL注入的问题,我们再前端让用户只能点击来排序,参数由后端来拼接,后端在查询之前,对参数进行校验,只能传入两个值:desc,asc。

6.1.4 like 查询

使用#{}:

    


MyBatis查询数据库_第11张图片

当使用#{}时,程序会报错;

使用${}:

    

使用${}不会报错,但${}存在SQL注入问题,该怎么解决呢?

使用mysql的内置函数concat:

    

MyBatis查询数据库_第12张图片

6.2多表查询

如果是增、删、改返回搜影响的⾏数,那么在 mapper.xml 中是可以不设置返回的类型的,如下图所示:
MyBatis查询数据库_第13张图片

然⽽即使是最简单查询⽤户的名称也要设置返回的类型,否则会出现如下错误:

MyBatis查询数据库_第14张图片

显示运⾏了⼀个查询但没有找到结果映射,也就是说对于 select * from userinfo where username like concat('%',#{name},'%')
它的优点是使⽤⽅便,直接定义到某个实体类即可。
数据库字段命名规则和Java的命名规则不一致
数据库命名:字母小写,以下划线分隔
Java属性命名:小驼峰,第一个英文单词首字母小写,其他英文单词首字母大写。

6.2.2返回字典映射:resultMap

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

一个xml文件中可以存在多个resultMap,只要id不同就可以。

resultMap 的使⽤如下:
MyBatis查询数据库_第15张图片

MyBatis查询数据库_第16张图片

如果程序中的属性和SQL字段名不一致,就会查不出来,但使用resultMap就可以解决这个问题。

6.2.3 多表查询

在多表查询时,如果使⽤ resultType 标签,在⼀个类中包含了另⼀个对象是查询不出来被包含的对象的,⽐如以下实体类:
@Data
public class ArticleInfo {
    private Integer id;
    private String title;
    private String content;
    private Date createtime;
    private Date updatetime;
    private Integer uid;
    private Integer rcount;
    private Integer state;
//    //作者相关信息
    private User user;
    private String username;
}
程序的执⾏结果如下图所示:

此时我们就需要使⽤特殊的⼿段来实现联表查询了。

6.2.3.1 ⼀对⼀的表映射

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

    
        
        
        
        
        
        

        
    

引入其他xml的resultMap,路径是:xml的namespace+resultMap的id

MyBatis查询数据库_第17张图片

常见使用写法:

   
    
        
        
        
        
        
        
        

    

 7.复杂情况:动态SQL使⽤

动态 sql 是Mybatis的强⼤特性之⼀,能够完成不同条件下不同的 sql 拼接。

7.1 标签

在注册⽤户的时候,可能会有这样⼀个问题,如下图所示:
MyBatis查询数据库_第18张图片
注册分为两种字段:必填字段和⾮必填字段,那如果在添加⽤户的时候有不确定的字段传⼊,程序应该如何实现呢?
这个时候就需要使⽤动态标签 来判断了。
    
        insert into articleinfo(
        
            title
        
        
            ,content
        
        
            ,uid
        
        
            ,state
        
        )
        values
        (
        
            #{title}
        
        
            ,#{content}
        
        
            ,#{uid}
        
        
            ,#{state}
        
        )
    

但当我们给第一个属性不赋值时,会出现错误:
MyBatis查询数据库_第19张图片

这时候就可以使用trim标签:

7.2 标签

如果所有字段都是⾮必填项,就考虑使⽤
标签结合标签,对多个字段都采取动态⽣成的⽅式。
标签中有如下属性:
prefix:表示整个语句块,以prefix的值作为前缀
suffix:表示整个语句块,以suffix的值作为后缀
prefixOverrides:表示整个语句块要去除掉的前缀
suffixOverrides:表示整个语句块要去除掉的后缀
调整 UserMapper.xml 的插⼊语句为:
    
        insert into articleinfo
        
            
                title
            
            
                ,content
            
            
                ,uid
            
            
                ,state
            

        
        values
        

            
                #{title}
            
            
                ,#{content}
            
            
                ,#{uid}
            
            
                ,#{state}
            

        
    

 

在以上 sql 动态解析时,会将第⼀个 部分做如下处理:
基于 prefix 配置,开始部分加上 (
基于 suffix 配置,结束部分加上 )
多个 组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于 suffixOverrides 配置去掉最后⼀个 ,
注意 中的 createTime 是传⼊对象的属性

7.3 标签

传⼊的⽤户对象,根据属性做 where 条件查询,⽤户对象中属性不为 null 的,都为查询条件。
Mapper 接⼝中新增条件查询⽅法:
ListqueryByCondition(@Param("uid") Integer uid,@Param("state") Integer state);

 

    

我们传入两个参数:

MyBatis查询数据库_第20张图片

但当我们只传入一个参数时:

 

报错了,where后面直接跟的and,第一个参数不传,结果where和and中间的数据没有了,造成sql语句报错,该怎么解决呢?

1.在where后面加1=1

    

MyBatis查询数据库_第21张图片

 2.使用where标签:

MyBatis查询数据库_第22张图片 

    

不传入第一条数据:
MyBatis查询数据库_第23张图片

一条数据都不传入:

MyBatis查询数据库_第24张图片

7.4 标签

根据传⼊的⽤户对象属性来更新⽤户数据,可以使⽤标签来指定动态内容。

MyBatis查询数据库_第25张图片 

当我们更新多个字段时,也会出现与上面一样的问题,我们就可以通过set标签来解决:

    
        update articleinfo
        
            
                state=#{state},
            

            
                uid=#{state};
            
        


    

 7.5 标签

对集合进⾏遍历时可以使⽤该标签。标签有如下属性:
collection:绑定⽅法参数中的集合,如 List,Set,Map或数组对象
item:遍历时的每⼀个对象
open:语句块开头的字符串
close:语句块结束的字符串
separator:每次遍历之间间隔的字符串
批量删除:
void batchDelete(@Param("ids") Listids);
    
        delete from articleinfo where id in
        
            #{deleteId}
        
    

8.补充

MyBatis的实现有两种方式:

1.xml方式,也就是我们上面所用到的方式

2.注解方式

@Mapper
public interface UserMapper2 {
    @Select("select * from userinfo")
    List queryAll();

    @Select("select * from userinfo where id= #{id}")
    List queryById(Integer id);
}

对于比较简单的SQL语句,注解的方式写起来很简单,但是负责一点的SQL语句写起来会非常复杂

MyBatis查询数据库_第26张图片

字符串的拼接很繁琐,而且没有错误提示,比较不适合新手,所以新手学习MyBatis框架,建议先使用xml方式再学习注解的方式。 

你可能感兴趣的:(mybatis)