MyBatis查询数据库

1.MyBatis 是什么?

MyBatis(前身IBatis)定义:MyBatis 是一款优秀的持久层框架(ORM框架)

MyBatis支持的操作方式:

1.支持XML的操作方式(MyBatis 3.1 之前)【主流的操作方式】。
2.使用注解实现数据的操作(MyBatis 3.1 之后)

简单来说 MyBatis 是更简单完成程序和数据库交互的工具,也就是更简单的操作和读取数据库工具。

2.学习目标:

  1. 搭建一个MyBatis开发环境(建立在Spring Boot 和Spring MVC 的基础上)。
  2. 基于MyBatis的开发模式,操作数据库。

3.搭建MyBaits开发环境

3.1 创建数据库和测试数据

-- 创建数据库
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://www.baidu.com',1);

3.2在项目中添加Mybatis框架

3.2.1新项目添加Mybatis

s框架
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
耐心等待项目加载完成即可。
在这里插入图片描述

3.2.2原有项目添加Mybatis

前置条件:添加插件Editstarters:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
之后导入包即可。
注意事项:
经过以上操作,就创建了SSM(Spring Boot/Spring MVC/MyBatis)项目,但此时因为没有配置连接的数据库服务器地址,如果此时启动项目,就会报错

3.3 配置连接字符串和MyBatis的XML报错路径

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

3.3.1 配置连接字符串

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

#配置数据库的连接字符串
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1/mycnblog?characterEncoding=utf8mb4
    username: root
    password: 12345678
    driver-class-name: com.mysql.cj.jdbc.Driver

3.3.2 配置MyBatis中XML保存路径(使用XML方式操作MyBatis)

在这里插入图片描述

#配置MyBatis中XML保存路径
mybatis:
  mapper-locations: classpath:mybatis/**Mapper.xml

4.使用MyBatis模式实现增删改查(CRUD)操作

MyBatis模式就是两个文件:@Mapper文件(定义方法,没有方法实现)+XML会实现@Mapper的方法体

4.1 创建@Mapper接口文件(方法声明,没有方法实现)

import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    //查询方法定义完成
    public UserInfo getUserById(Integer id);
}

4.2 使用XML实现@Mapper接口的方法

<?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">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <!-- 查询单条数据(根据id) -->
    <select id="getUserById" resultType="com.example.demo.model.UserInfo">
        <!--        select * from userinfo where id=#{id}-->
        select * from userinfo where id=#{id}
    </select>
</mapper>

在这里插入图片描述

关于IDEA专业版@Autowired报错但不影响执行的问题原因分析:

@Autowired属于Spring注解,而@Mapper属于MyBatis的注解,Spring中的@Autowired不能识别MyBatis的@Mapper注解,所以会报错。

解决方案:
将Service里的Spring的@Autowireg更换为JDK的@Resource

4.3添加功能实现

方法实现:

    <!-- 用户添加(返回受影响的行数) -->
    <insert id="add">
        insert into userinfo(username,password)
        values(#{username},#{password})
    </insert>
    <!-- 用户添加(返回自增 ID) -->
    <insert id="add2" useGeneratedKeys="true" keyProperty="id">
        insert into userinfo(username,password) values(#{username},#{password})
    </insert>

在这里插入图片描述

方法声明:

    //添加用户(返回受影响行数)
    public int add(String username,String password);
    // 添加用户 2(返回的自增 ID)
    public void add2(UserInfo userInfo);

在这里插入图片描述

4.4修改功能实现

方法声明:

    //修改操作(根据id修改username,返回受影响的行数)
    public int update(@Param("id") int id, @Param("username") String username);

方法实现:

    <!-- 根据 id 修改 username(返回受影响的行数) -->
    <update id="update">
        update userinfo set username=#{username} where id=#{id}
    </update>

4.5 删除用户操作

方法声明:

    //删除方法(返回受影响的行数)
    public int del(@Param("id") int id);

方法实现:

    <!-- 根据 id 进行删除(返回受影响的行数) -->
    <delete id="del">
        delete from userinfo where id=#{id}
    </delete>

扩展功能:单元测试之后,将测试的数据进行回滚,也就是使用单元测试不污染数据库的数据

@Transactional 注解

    @Transactional // 单元测试的类或者方法上加上此注解,表示当前测试的数据不会影响数据库
    @Test
    void del() {
        int result = userMapper.del(3);
        Assertions.assertEquals(1, result);
    }
}

5.查询操作

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

#{}:预编译处理,不存在安全问题。
${}:字符直接替换,存在安全问题。

${}使用场景:当传递的是一个SQL语句时(而非一个某个参数的值),那么只能使用 ${}的形式,比如传递排序的 desc 或者是 asc 的时候,他是一个SQL语句,而非某一个参数的值。

    <!-- 根据时间排序查询所有的用户 -->
    <select id="getAllOrderByCreateTime" resultType="com.example.demo.model.UserInfo">
        select * from userinfo order by createtime ${order}
    </select>

在这里插入图片描述

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

${} 优点

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

SQL 注入问题

方法声明:

    //登录方法
    public UserInfo login(@Param("username") String username,
                          @Param("password") String password);

方法实现(使用${}):

    <!-- 登录方法 -->
    <select id="login" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where username='${username}' and password='${password}'
    </select>

在这里插入图片描述

结论:用于查询的字段,尽量使用 #{} 预查询的方式。

扩充知识:打印MyBatis最终执行的SQL

如图配置即可:
在这里插入图片描述

like 查询

1.like 使用 #{} 报错

    <!-- 根据用户名称进行模糊查询 -->
    <select id="getUserByLikeName" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where username like '%#{username}%'
    </select>

2.使用 ${}

    <!-- 根据用户名称进行模糊查询 -->
    <select id="getUserByLikeName" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where username like '%${username}%'
    </select>

可以实现功能,但是存在安全风险。
3.可以考虑使用 mysql 的内置函数 concat() 来处理,实现代码如下:

    <!-- 根据用户名称进行模糊查询 -->
    <select id="getUserByLikeName" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where username like concat('%',#{username},'%')
    </select>

5.2多表查询

对于 查询标签来说至少需要两个属性:

  1. id 属性:用于标识实现接口中的那个方法;
  2. 结果映射属性:结果映射有两种实现标签:resultMap和 resultType

5.2.1返回类型:resultType

绝大数查询场景可以使用 resultType 进行返回,它的优点是使用方便,直接定义到某个实体类即可。

5.2.2返回字典映射:resultMap

resultMap 使用场景:

  1. 字段名称和程序中的属性名不同的情况,可使用 resultMap 配置映射;
  2. 多表关联查询(一对一和一对多)关系可以使用 resultMap 映射并查询数据。

字段名和属性名不同的情况
在这里插入图片描述
程序中属性如下:
在这里插入图片描述
mapper.xml 代码如下:

    <select id="getAll" resultType="com.example.demo.model.ArticleInfo">
        select * from articleinfo
    </select>

查询出来name=NULL;

resultMap 的使用如下:
在这里插入图片描述
resultMap 基础使用:
在这里插入图片描述
在这里插入图片描述

5.2.3多表查询

5.2.3.1 一对一的表映射

一对一映射要使用 association标签,具体实现如下(一篇文章只对应一个作者):
ArticleMapper.xml实现代码:

    <resultMap id="BaseMap" type="com.example.demo.model.ArticleInfo">
        <id column="id" property="id"></id>
        <result column="title" property="name"></result>
        <result column="content" property="content"></result>
        <result column="createtime" property="createtime"></result>
        <result column="updatetime" property="updatetime"></result>
        <result column="uid" property="uid"></result>
        <result column="rcount" property="rcount"></result>
        <result column="state" property="state"></result>
        <!-- 一对一关系的实现 -->
        <association property="userInfo"
                     resultMap="com.example.demo.mapper.UserMapper.BaseMap"
                     columnPrefix="u_">
        </association>
    </resultMap>

    <select id="getAll2" resultMap="BaseMap">
        select a.*,u.id u_id,u.username u_username,u.password u_password from articleinfo a left join userinfo u on
        a.uid=u.id
    </select>

以上使用 association标签,表示一对一的结果映射:

  • property 属性:指定 Article 中对应的属性,即用户。
  • resultMap 属性:指定关联的结果集映射,将基于该映射配置来组织用户数据。
  • columnPrefix 属性:绑定一对一对象时,是通过columnPrefix+association.resultMap.column 来映射结果集字段。
    association.resultMap.column是指 标签中 resultMap属性,对应的结果集映射中,column字段。
    在这里插入图片描述
    userMapper.xml代码:
    <resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
        <result column="photo" property="photo"></result>
        <result column="createtime" property="createtime"></result>
        <result column="updatetime" property="updatetime"></result>
        <result column="state" property="state"></result>
        <collection property="alist"
                    resultMap="com.example.demo.mapper.ArticleMapper.BaseMap"
                    columnPrefix="a_">
        </collection>
    </resultMap>

实体类:
在这里插入图片描述

5.2.3.2一对多:一个用户多篇文章案例

一对多需要使用 collection 标签,用法和 association相同,如下所示:

    <resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
        <result column="photo" property="photo"></result>
        <result column="createtime" property="createtime"></result>
        <result column="updatetime" property="updatetime"></result>
        <result column="state" property="state"></result>
        <!-- 一对多关系的实现 -->
        <collection property="alist"
                    resultMap="com.example.demo.mapper.ArticleMapper.BaseMap"
                    columnPrefix="a_">
        </collection>
    </resultMap>

    <select id="getAll2" resultMap="BaseMap">
        select u.*,a.id a_id,a.title a_title,a.content a_content
        from userinfo u
        left join articleinfo a
        on u.id=a.uid
    </select>

6.动态SQL使用

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

6.1 < if> 标签

注册分为两种字段:必填字段和非必填字段,那如果在添加用户的时候有不确定的字段传入,程序应该如何实现呢?
这个时候就需要使用动态标签 < if> 来判断了,比如添加的时候性别 sex 为非必填字段,具体实现如下:

    <!-- 用户添加(使用动态 sql if -->
    <insert id="add3">
        insert into userinfo(username,
        <if test="photo!=null">
            photo,
        </if>
        password)
        values(#{username},
        <if test="photo!=null">
            #{photo},
        </if>
        #{password})
    </insert>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.2 < trim> 标签

之前的插入用户功能,只是有一个 sex 字段可能是选填项,如果有多个字段,一般考虑使用 < trim> 标签结合 < if> 标签,对多个字段都采取动态生成的方式。

trim 标签中有如下属性:

  • prefix:表示整个语句块,以prefix的值作为前缀
  • suffix:表示整个语句块,以suffix的值作为后缀
  • prefixOverrides:表示整个语句块要去除掉的前缀
  • suffixOverrides:表示整个语句块要去除掉的后缀
    <!-- 用户添加(使用动态 sql trim + if -->
    <insert id="add4">
        insert into userinfo
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username!=null">
                username,
            </if>
            <if test="password!=null">
                password,
            </if>
            <if test="photo!=null">
                photo
            </if>
        </trim>
        values
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username!=null">
                #{username},
            </if>
            <if test="password!=null">
                #{password},
            </if>
            <if test="photo!=null">
                #{photo}
            </if>
        </trim>
    </insert>

在这里插入图片描述
在以上 sql 动态解析时,会将第一个 trim 部分做如下处理:

  • 基于 prefix 配置,开始部分加上 (
  • 基于 suffix 配置,结束部分加上 )
  • 多个 组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于suffixOverrides 配置去掉最后一个 ,
  • 注意 <if test="username!=null> 中的 username是传入对象的属性

6.3 < where> 标签

    <!-- 根据名称或密码查询用户列表(使用动态 sql where) -->
    <select id="getListByNameOrPwd" resultMap="BaseMap">
        select * from userinfo
        <where>
            <if test="username!=null">
                username=#{username} and
            </if>
            <if test="password!=null">
                password=#{password}
            </if>
        </where>
    </select>

情景一:当不传递任何参数时,传递的SQL如下:
在这里插入图片描述
情景二:传递前面一个参数
在这里插入图片描述
使用以下代码可以解决这个问题:

    <!-- 根据用户名称进行模糊查询 -->
    <select id="getUserByLikeName" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where username like concat('%',#{username},'%')
    </select>

    <!-- 根据名称或密码查询用户列表(使用动态 sql where) -->
    <select id="getListByNameOrPwd" resultMap="BaseMap">
        select * from userinfo
        <where>
            <if test="username!=null">
                username=#{username}
            </if>
            <if test="password!=null">
                and password=#{password}
            </if>
        </where>
    </select>

在这里插入图片描述

情景三:传递后面一个参数

 <!-- 根据用户名称进行模糊查询 -->
    <select id="getUserByLikeName" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where username like concat('%',#{username},'%')
    </select>

    <!-- 根据名称或密码查询用户列表(使用动态 sql where) -->
    <select id="getListByNameOrPwd" resultMap="BaseMap">
        select * from userinfo
        <where>
            <if test="username!=null">
                username=#{username}
            </if>
            <if test="password!=null">
                and password=#{password}
            </if>
        </where>
    </select>

在这里插入图片描述
在这里插入图片描述
也可以使用< trim>标签进行该写代码:

<!-- 根据名称或密码查询用户列表(使用动态 sql where) -->
    <select id="getListByNameOrPwd" resultMap="BaseMap">
        select * from userinfo
        <trim prefix="where" prefixOverrides="and">
            <if test="username!=null">
                username=#{username}
            </if>
            <if test="password!=null">
                and password=#{password}
            </if>
        </trim>
    </select>

6.4< set>标签

根据传入的用户对象属性来更新用户数据,可以使用< set>标签来指定动态内容。

    <update id="updateById">
        update userinfo
        <set>
            <if test="username!=null">
                username=#{username},
            </if>
            <if test="password!=null">
                password=#{password},
            </if>
            <if test="photo!=null">
                photo=#{photo}
            </if>
        </set>
        where id=#{id}
    </update>

执行的SQL:
在这里插入图片描述

set标签已经自动将最后一个逗号删除掉
使用< trim>标签也可以实现< set>的功能:

    <update id="updateById">
        update userinfo
                <trim prefix="set" suffixOverrides=",">
                    <if test="username!=null">
                        username=#{username},
                    </if>
                    <if test="password!=null">
                        password=#{password},
                    </if>
                    <if test="photo!=null">
                        photo=#{photo}
                    </if>
                </trim>
        where id=#{id}
    </update>

6.5 < foreach>标签

对集合进行遍历时可以使用该标签。< foreach>标签有如下属性:

  • collection:绑定方法参数中的集合,如 List,Set,Map或数组对象
  • item:遍历时的每一个对象
  • open:语句块开头的字符串
  • close:语句块结束的字符串
  • separator:每次遍历之间间隔的字符串

接口方法:

public int delByIds(List<Integer> ids);

删除SQL:

    <delete id="delByIds">
        delete from userinfo where id in
        <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </delete>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值