MyBatis查询数据库

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

日升时奋斗,日落时自省 

目录

1、MyBatis

1.1、Mybatis配置

1.2、添加MyBatis框架支持

1.3、配置连接字符串和MyBatis

1.3.1、配置连接字符串

1.3.2、配置MyBatis中XML路径

2、业务代码

2.1、添加实体类

2.2、添加mapper接口

2.3、添加.xml文件

3、单元测试

3.1、单元测试优势

3.2、Spring Boot单元测试使用

4、MyBatis操作数据库

4.1、查询操作(单表)

4.1.1、有参查询

4.1.2、${}诱发SQL注入(传对象举例)

4.1.3、#{}解决${}带来的SQL注入(传对象举例)

4.1.4、like模糊查询

4.1.5、#{}和${}的区别

4.2、数据修改(单表)

4.3、数据删除(单表)

4.4、数据插入(单表)

4.4.1、插入操作(普通插入)

4.4.2、插入操作(设置主键)

4.5、多表查询

5、动态SQL使用

5.1、if标签

5.2、trim标签

5.3、where标签

5.4、set标签

5.5、foreach标签

如果是一路平缓的学习,那一定了解过JDBC,以及JDBC如何连接数据库,但是那时学习的数据库连接操作代码冗余太多,操作也比较复杂。

注:本篇内容有点长(选取自己需要的即可)

1、MyBatis

此处MyBatis查询数据库与Spring系列连起来,前面学习内容都不能进行保存,除了日志记录是保存文件中的,其他数据存储都是保存子内存中的,当下就通过MyBatis来连接数据库

MyBatis是一个优秀的基于Java的持久层框架,它支持定制化SQL、存储过程以及高级映射。 Mybatis通过XML或注解方式将要执行的各种statement(statement、preparedStatment、callableStatment)配置起来,并通过java对象和statement中的sql进行映射生成最终的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回

<1>自定义SQL:指开发者可以手动编写SQL语句并通过MyBatis框架执行的方式进行数据操作

<2>存储过程:预先保存在数据库中的一段程序,可以被多次调用,通常用于执行特定的复杂业务逻辑或数据操作

<3>高级映射:在SQL查询语句中加入关联查询的语句,并通过ResultMap将查询结果映射成Java对象

Mybatis去除了JDBC繁琐的代码(获取参数结果集等),Mybatis通过简单的XML文件或者注解来配置和映射原始类型,接口和java对象

1.1、Mybatis配置

配置Mybatis开发环境

使用Mybatis模式和语法操作数据库

MyBatis也是ORM框架,ORM(Object Relational Mapping)即位对象关系映射,在面向对象语言的编程中,将关系来型数据与对象建立起映射关系,进而自动的完成数据域对象的互相转换:

<1>将输入数据(即传入对象)+SQL映射成原生SQL

<2>将结果集映射为返回对象,即输出对象

ORM把数据库映射为对象(规定性映射):

数据库表(table)--->类(class)

记录(record,行数据)--->对象(object)

字段(field)--->对象的属性(attribute)

1.2、添加MyBatis框架支持

添加MyBatis框架支持分为两种情况:

<1>一种情况是对自己之前的Spring项目进行升级

<2>另一种情况是创建一个全新的MyBatis和Spring Boot的项目

下面开始创建一个项目并且开始附带上Mybatis需要的依赖:

创建Spring Boot项目 这里只展示引入依赖部分

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

后面的操作部分和Spring Boot项目创建操作相同

1.3、配置连接字符串和MyBatis

1.3.1、配置连接字符串

Spring Boot配置文件相关

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

这里给友友们提供数据库和其中表(但是没有数据,可以自己加两条)

我们这里使用.yml配置文件进行配置设置

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

注:这里的数据库连接自己的数据库,因为我这里的数据库名称叫做 mycnblog ;

友友们填写的自己的数据库就可以了

 driver-class-name配置项:设置驱动程序的名称(是固定的,直接复制就行我们这里使用的是mysql的)

注:mysql的版本是有一定规定的,mysql5.x版本之前的使用“com.mysql.jdbc.Driver”这个配置
如果是高于5.x版本的使用“com.mysql.cj.jdbc.Driver”

1.3.2、配置MyBatis中XML路径

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

这里还会设置到 日志的打印级别,因为日志级别的Mybatis打印的日志级别是Debug级别的,但是Spring Boot 默认的打印级别是INFO,所以这里也会将日志级别设置较低

mybatis:
  mapper-locations: classpath:mybatis/*Mapper.xml
  # 设置 Mybatis 的 xml 保存路径
  configuration: # 配置打印 MyBatis 执行的 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 配置打印 MyBatis 执行的 SQL
  logging:
    level:
      com:
        example:
          demo: debug

 注意几个点:

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

2、业务代码

添加业务代码,有这么一系列流程来实现MyBatis查询用户功能,当前先走第一步写一个实体类

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

 2.1、添加实体类

这个实体类也不是随便加的,因为我们想用MyBatis,所以要遵循规定,MyBatis的映射关系

以下是我想要映射的数据库表单,所以java对象的实体类要与之一样

 这里创建entity层,该层存放实体类,

@Data
public class UserEntity {
    private Integer id;
    private String username;
    private Integer password;
    private String photo;
    private LocalDateTime createtime;
    private LocalDateTime updatetime;
    private Integer state;
}

2.2、添加mapper接口

这里我们定义一个mapper层,用来写mapper接口(一定注意这里创建的是一个接口)

@Mapper
public interface UserMapper {
    List getAll();
}

getAll()就是我们写的方法用来操控数据库的,但是暂时还没有写sql代码

注:mapper接口,上一定要写@Mapper注解

@Mapper:MyBatis会自动扫描所有的接口,然后根据接口定义创建相应的代理对象。这样我们就可以直接使用该接口来操作数据库,而无需手动编写SQL语句的实现

2.3、添加.xml文件

数据持久层现,MyBatis的固定xml格式:





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

 此处就要开始写sql语句了,在mapper标签内写sql操作语句

    

id:写的是映射java接口路径下对应的方法名

resultType:指定查询结果集中每行数据的映射类型的属性,这里我们操作是UserEntity实体类,就写该类的路径就行

select * from userinfo where id=${uid}

代码都写好了,现在开始单元测试:(还是原来的操作,除了选择的方法不同以外其他没有什么区别)

在我们写操作数据库方法的Java接口处 单击右键  generate ->Test

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

 还是在刚刚创建测试类中 (一起都沿用之前有的,对象注入也适用刚刚写过的):

    @Test
    void getUserId() {
        UserEntity user=userMapper.getUserId(1);
        System.out.println(user);
    }

 运行结果:

 注:红色框框中显示的是我们写的sql语句,蓝色框框就是传参过程中,我们拿到值

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

 4.1.2、${}诱发SQL注入(传对象举例)

使用${}是很不安全的,容易叫人进行恶意拿取数据

SQL注入:是一种攻击技术,攻击者利用输入验证不严格的网站或应用程序中的漏洞将恶意SQL代码插入到查询中,从而绕过身份验证和控制访问数据的机制

这里给友友们演示一下:${}带来的问题

还是写一个login操作 登录需要涉及用户名和密码(还是在原来的UserMapper接口中写):

    UserEntity login(UserEntity user);

注:此时传递的是一个对象

对象映射到.xml文件中,属性怎么设置,Spring boot 已经给我们封装好了,直接使用属性就传参就行了

.xml文件 sql 语句 :

    

同样的操作,创建单元测试对应的方法来测试(这里就不在演示添加过程,有参查询中有详细过程):

注意:这里传参都是字符串(问题就在这里)

    @Test
    void login() {
        String username="admin";
        String password="admin";
        UserEntity user=new UserEntity();
        user.setUsername(username);
        user.setPassword(password);
        UserEntity user1=userMapper.login(user);
        System.out.println(user1);
    }

 运行结果:

这里不能看出字符串少了引号,所以sql语句错了,那们就给给他添加上引号

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

 给sql语句添加上 单引号:

 这次就不会报错了:

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

 正是因为这样,字符串的拼接带来了SQL注入的问题,当前是没有拿到信息的,total是:0,这才是正确的,数据不对不应该拿到信息

以下是我数据库中的用户和密码:

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

 这里就开始展现SQL注入如何获取信息(只修改了密码输入)

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

 运行结果:

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

4.1.3、#{}解决${}带来的SQL注入(传对象举例)

解决${}的方法就是使用#{} ,一旦使用${}就需要格外注意SQL注入

把原来的$换成# 直接运行单元测试:

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

 另一种方式就需要在接收数据时,需要处理部分特殊情况,防止诱发SQL注入

当然了${} 也不是没有什么用处,#{}传递的是属性,不能传递sql中的关键字,${}能(凡是存在必有道理),这里试问:什么情况会涉及到传sql关键字呀,感觉基本没有什么情况是吧

上事例:

如果需要设置升降序

java接口内操作sql的方法:

    //sql 关键字 传递
    List getUserByUserOrder(@Param("ord") String ord);

 .xml文件sql语句(这里只能使用${} ,#{}会报错):

    

添加单元测试 进行测试(传参直接传 sql 关键字 desc 倒序方便看到效果):

    @Test
    void getUserByUserName() {
        List lists=userMapper.getUserByUserOrder("desc");
        for (UserEntity list:lists) {
            System.out.println(list);
        }
    }

单元测试结果(是可以做到,但是做到是做到了,但是还是要注意SQL注入,可以使用传一个值来判定升降即可,尽可能避免掉SQL注入):

注:后面的操作都会使用#{} 来接收属性值

4.1.4、like模糊查询

Java接口内操作sql方法:

模糊查询直接 需要 %字符串% 这个确实使用#{}是输入不出来的,虽然${}是可以进行字符串拼接操作的,但是还是不得不提防SQL注入:

所以这里就给友友们推荐一个方法 concat 

concat:有多少参数都可以进行拼接 ,下面开始操作一下:

Java接口内操作sql的方法:

    List getListByName(@Param("username")String username);

.xml文件中的sql语句(以下是${}版的,不安全):

    

 .xml文件中的sql语句(以下是concat方法版的,比${}安全):

    

 单元测试(因为这里是模糊查找所以只需要输入一部分即可):

    @Test
    void getListByName() {
        String username="无";
        List list=userMapper.getListByName(username);
        //list.stream().forEach(System.out::println);  //stream是正则表达式的封装,可以进行直接打印
        //如果没有了解过stream 的 直接使用for循环即可
        for(int i=0;i

 运行结果:

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

4.1.5、#{}和${}的区别

<1>功能不同:${} 是直接替换,而 #{}是占位符;

<2>使用场景不同:普通参数使用 #{},如果传递的是 SQL 命令或 SQL 关键字,需要使用 ${},但在使用前一定要做好安全验证;

<3>安全性:使用 ${} 存在安全问题,如 SQL 注入,而 #{} 则不存在安全问题。

4.2、数据修改(单表)

修改数据就没有那么那么复杂了,但是这里会新认识一个相关单元测试的注解@Transactional

Java接口内 sql 方法代码:

    //修改密码
    int updatePassword(@Param("uid") Integer id,
                       @Param("password") String password,
                       @Param("newpassword") String newpassword);

.xml文件sql语句 :

    
        update userinfo set password=#{newpassword}
        where id=#{uid} and password=#{password}
    

单元测试:

    @Test
    void updatePassword() {
        int result=userMapper.updatePassword(1,"123456","admin");
        System.out.println("修改行数:"+result);
    }

运行结果:

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

 此时要想不污染数据库需要添加一个注解@Transactional,才能进行回滚不影响数据库数据

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

 4.3、数据删除(单表)

因为修改和删除简单,所以先举例,容易接收,有了前面的地基,其实操作也很类似

这里就直接写一个删除的例子友友们就基本了解了:

Java接口内 代码:

    //删除用户信息
    int deleteById(@Param("uid") Integer id);

.xml文件sql 语句:

    
        delete from userinfo where id=#{uid}
    

 单元测试:

    @Transactional   //该注解 会让操作的数据 进行回滚
    @Test
    void deleteById() {
        int result=userMapper.deleteById(6);
        System.out.println("删除影响的行数:"+result);
    }

运行结果(前面已经介绍了 回滚的注解,介绍就用起来):

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

 4.4、数据插入(单表)

普通的插入操作和前面的 删除和修改操作 很类似

4.4.1、插入操作(普通插入)

Java接口内操作sql的方法:

    //添加用户
    int addUser(UserEntity user);

.xml文件sql语句:

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

 单元测试(此处使用注解 进行回滚操作了):

    @Transactional
    @Test
    void addUser() {
        String username="黑无常";
        String password="123456";
        UserEntity input=new UserEntity();
        input.setUsername(username);
        input.setPassword(password);
        int result=userMapper.addUser(input);
        System.out.println("添加影响的行数:"+result);
    }

 运行结果:MyBatis查询数据库_第26张图片

4.4.2、插入操作(设置主键)

数据插入就与前面有所不同了,设置自动生成主键等操作

看代码来理解  

Java接口内写一个添加操作的方法:

    //插入操作 主键设置
    int addUserGetId(UserEntity user);

.xml文件中写插入的sql语句:



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

注:涉及到两个新的参数一个是:useGeneratedKeys,另一个是keyProperty

useGeneratedKeys:是一个可选的配置属性,可以用于指示 MyBatis 是否应该使用数据库自动生成的主键。当 useGeneratedKeys 设置为 true 时,MyBatis 将使用 JDBC 的 getGeneratedKeys 方法来获取自动生成的主键(前提是你数据库创建表已经设置里主键),并将其设置回实体对象中

keyProperty:对应数据库主键字段

单元测试:

    @Test
    void addUserGetId() {
        String username="白无常";
        String password="123456";
        UserEntity input=new UserEntity();
        //设置新的 用户名和 密码
        input.setUsername(username);
        input.setPassword(password);
        //传递对象直接传,  Spring boot中的映射会进行获取其中属性 对应到xml文件中写sql语句
        int result=userMapper.addUserGetId(input);
        System.out.println("添加行数:"+result);
    }

这里就先不加@Transactional注解了,为了方便友友观察到

单元测试启动:然后看一下数据库,是否有新增 :

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

4.5、多表查询

这里我们就尝试操作多个表,这里创建一个文章表,但是我们还想知道这个文章对应的是哪个用户的

扩展的实体类:用户的名字字段

文章表的实体类:文章表字段信息

文章实体类:

@Data
public class ArticleInfo {
    private Integer id;
    private String title;
    private String content;
    private String createtime;
    private String updatetime;
    private Integer uid;
    private Integer rcount;
    private Integer state;
}

扩展表的需要的名称:

@Data
public class AritcleInfoVo extends ArticleInfo {
    //Data注解来自于 Lombok是不能直接 提供父类的toString
    //所以这里就需要 自己进行generate 快捷一个toString
    @Override
    public String toString() {
        return "AritcleInfoVo{" +
                "username='" + username + '\'' +
                "} " + super.toString();
    }
    private String username;
}

注:类虽然有继承父类,但是Lombok提供的注解不行,所以我们需要自己来写已给toString

这里toString怎么来的,给友友们简要操作一下:

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

 这里创建操作和原来的无异(接下来就以操作文章表进行):

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

 注:蓝鸟写 Java接口内操作sql的方法 ; 红鸟:.xml文件写sql语句

前置操作已经做完了,下面开始多表查询:

Java接口内 操作sql 的方法(通过id获取多表查询信息):

@Mapper
public interface AritcleMapper {
    List getAll(@Param("uid")Integer uid);
}

.xml文件中的sql语句:

    

单元测试(获得uid = 1,也就是用户编号为1的文章 ):

@SpringBootTest
class AritcleMapperTest {
    @Autowired
    private AritcleMapper aritcleMapper;
    //测试多表查询
    @Test
    void getAll() {
        int uid=1;
        List lists=aritcleMapper.getAll(uid);
        for(int i=0;i

运行结果:

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

注:为什么这里只说到多表的查询,是因为其他的多表操作的基本不太敢用,多表删除一条数据回影响很多条数据,修改也是类似;

5、动态SQL使用

5.1、if标签

为啥有个动态SQL呢,我们不是所有选择都是一定的

举个例子:就像我们去填写信息的时候(注册csdn账号),注册只填写部分必要信息,还有一些是可写可不写的,这部分怎么写入数据库,个数少了还好说,没种情况我都写一个方法来操作数据存入,个数多了咋办,像填写简历的信息的时候,那么多信息可选可不选,所有情况都要写吗??

答案:不需要

这里使用标签来解决这个可选问题

我们这里使用userinfo用户表,用户和密码总是要传的嘛,头像可以不传

Java接口内 sql 操作方法:

    //if 标签
    int addUser2(UserEntity user);

.xml文件sql语句:

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

标签就是数据库某个字段必成可传可不传的

使用特点:标签使用时一定需要设置test参数,否则会报红,test参数里面就写限制条件,photo就是我数据库中的图片字段,意为:如果图片为空的话,标签就不满足,那就不执行标签内的字段

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

 单元测试(添加了@Transactional注解,不会污染数据库):

    @Transactional
    @Test
    void addUser2() {
        String username="白无常";
        String password="123456";
        UserEntity input=new UserEntity();
        input.setUsername(username);
        input.setPassword(password);
        int result=userMapper.addUser2(input);
        System.out.println("插入:"+result+"条");
    }

运行结果:

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

5.2、trim标签

这里我们更换以下顺序,为了突显效果像这样传参 username,password,photo

trim标签又是干什么的,if标签是让sql成为了动态的,但是不代表if标签一个人就能完成无错操作,

注意细节的友友们,会看到写if标签中除了password字段后面还跟了一个“逗号”,如果这个逗号

举个例子:

如果 username,password,photo 就以这个顺序为例,假如这三个属性都是可选可不选的,那要是photo没有传的话,就根据上面if标签来写

产生结果: usernam,password,    这样的sql能运行吗???我相信肯定是不能的

trim标签能去掉没有用的后缀或者前缀,所以这时在if标签外面写一个trim标签来控制后缀多余的“逗号”就可以解决问题(不要觉得这个问题是特殊问题,很常见)

trim标签有四个参数:

<1>prefix:表示整个语句块,以prefix的值作为前缀
<2>suffix:表示整个语句块,以suffix的值作为后缀
<3>prefixOverrides:表示整个语句块要去除掉的前缀
<4>suffixOverrides:表示整个语句块要去除掉的后缀

java接口内操作sql的方法(添加方法):

    //trim 标签
    int addUser3(UserEntity user);

.xml文件sql语句:

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

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

单元测试(我们这里只传递username和photo,最后一个参数的位置我们空下来):

    @Transactional
    @Test
    void addUser3() {
        String username="白无常";
        String photo="bind.jpg";
        UserEntity input=new UserEntity();
        input.setUsername(username);
        input.setPhoto(photo);
        int result=userMapper.addUser3(input);
        System.out.println("插入:"+result+"条");
    }

注:生成的sql应该是 insert into userinfo(username,photo,) values(?,?);

经过trim标签的处理:看以下运行结果:

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

 5.3、where标签

where标签又是来干什么的呢 主要避免什么都不输入的情况,你像你输入查询情况,他就默认全部给你看,查询再过滤掉你不看的

举个例子:

这里以文章表来说:  如果我想查询某几个信息

select * from articleinfo where id=#{id} and title like concat('%',#{title},'%');

如果此时没有输入id 和 title的话我们在.xml文件写的sql就会变成:

select * from articleinfo where 这就啥也不是了(下面开始演示where标签)

Java接口内操作sql的方法:

    List getListByIdOrTitle(@Param("id") Integer id
            ,@Param("title")String title);

.xml文件的sql语句(where 还会清除if标签中前置and ,这是规定,遵守就行,所以我们尽量将and将在if标签内 要写的sql语句的前面):

    

单元测试(来看一下sql语句,以下测试三个结果):

    @Test
    void getListByIdOrTitle() {
        List lists=aritcleMapper.getListByIdOrTitle(1,"Java");
        System.out.println(lists.size());
    }

运行结果:

 5.4、set标签

set 关键字 针对sql语句来想起来的一定是修改,这就是给修改使用的 有了前面的三个标签,这个标签的使用方法也是一致的(这里就写一个修改的sql语句操作)

Java接口内操作sql的方法(修改内容直接传一个对象,如果实体类中属性进行添加了也不会有什么影响):

    //set 标签
    int updateById(UserEntity user);

.xml文件的SQL语句:

    
        update userinfo
        
            
                username=#{username},
            
            
                password=#{password},
            
        
        where id=#{id}
    

set标签有一个特点,就是if标签内后置的“逗号”set是会根据mysql 语法进行去除的,就像where标签可以去掉if标签内前置的and一样(都是一个道理,当然这些都是规定)

单元测试(观察password后面的“逗号”去除):

这里我们传的是对象,给对象设置id 就是where后面指定要修改的id,其他设置都是修改内容

    @Transactional
    @Test
    void updateById() {
        String username="小刘";
        String password="admin";
        UserEntity input=new UserEntity();
        input.setId(3);
        input.setUsername(username);
        input.setPassword(password);
        int result=aritcleMapper.updateById(input);
        System.out.println("修改数:"+result);
    }

运行结果:

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

 注:这里只是告诉了友友们的使用特点,也不是说就只有这一种情况,这些标签可以混合使用

5.5、foreach标签

foreach可以设置五个参数:

<1>collection:绑定⽅法参数中的集合,如 List,Set,Map或数组对象
<2>item:遍历时的每⼀个对象  (就相当于创建临时变量来接收一样)
<3>open:语句块开头的字符串
<4>close:语句块结束的字符串
<5>separator:每次遍历之间间隔的字符串

针对单表的多条数据删除也是比较常见,foreach也就是为了这样的操作(下面直接开始演示案例)

Java接口内操作sql方法:

    // 根据文章id集合批量删除文章 foreach 标签
    int delByIdList(List idList);

.xml文件sql语句:

    
        delete from articleinfo
        where id in

        
            #{aid}
        
    

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

单元测试(我这里刚好有3条数据所以只添加了3个id作为演示,可以回滚):

    @Transactional
    @Test
    void delByIdList() {
        List lists=new ArrayList<>();
        lists.add(1);
        lists.add(2);
        lists.add(3);
        int result=aritcleMapper.delByIdList(lists);
        System.out.println("删除行数:"+result);
    }

运行结果:

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

你可能感兴趣的:(mybatis,java,mysql)