写点什么

MyBatis 操作数据库

作者:JAVA活菩萨
  • 2022 年 8 月 02 日
  • 本文字数:6057 字

    阅读完需:约 20 分钟

MyBatis操作数据库


目录

(1)xml 文件中添加 MyBatis 依赖

(2)准备 springboot 启动配置

(3)准备数据库对应的 mybatis 文件

2、插入操作->获取自增的主键

3、多表查询(关系映射)

一、MyBatis

MyBayis 支持自定义 SQL、存储过程以及高级映射,几乎去除了所有的 JDBC 代码以及设置参数和获取结果集的工作。可以通过简单的 xml 或注释来配置和映射原始类型、接口为数据库中的记录。简单来说 MyBatis 是更简单完成程序和数据库交互的工具,更简单的进行操作和读取数据库中的数据。

回顾 JDBC 操作:

  1. 创建数据库连接池 DataSource;

  2. 通过 DataSource 获取数据库连接 Connection;

  3. 编写要执行的带占位符的 SQL 语句;

  4. 通过 Connection 及 SQL 创建操作命令的对象 Statement;

  5. 替换占位符:指定要替换的数据库字段类型,占位索引及要替换的值;

  6. 使用 Statement 执行 SQL 语句;

  7. 查询操作:返回结果集 ResultSet;更新操作:返回一个 int 值;

  8. 处理结果集;

  9. 释放资源。

存在以下几个问题:

(1)DataSource 和 Connection 两者的关系?

  • DataSource 提供连接池的支持。连接池在初始化是创建一定数量的数据库连接,这些连接可以复用,每次使用完数据库连接,释放资源调用 connection.close()都是将 Connection 对象回收。

  • DataSource 创建时,设置了 url,账号,密码,不是连接数据库的;通过 DataSource.getConnection()获取连接对象时,才通过 Connection 表示出建立的数据库连接这个概念。

(2)创建操作命令的对象 Statement 是,如何防止 sql 注入?

使用 PreparedStatement(预编译的操作命令对象)。拼接字符串=>替换占位符。

(3)防止 sql 注入的原理是什么?

PreparedStatement 防止会进行预编译,把替换的字符·中,单引号加上“/”zhuanyi

对于 crud 操作:

  • 输入都是一个对象(根据属性来及逆行过滤/插入/修改),真实开发时,方法参数可以设计为 int、String,其实也可以作为对象的属性;

  • 输出:修改/插入/删除返回 int;查询操作返回 List/某个对象。

二、一个 MyBatis 查询

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

  • 将输入数据(传入的对象)+SQL 映射成原生的 SQL(输入替换占位符)

  • 将结果集映射为返回对象(输出对象)



1、环境准备

(1)xml 文件中添加 MyBatis 依赖

在 Spring 环境的基础上,需要添加 MyBatis 和数据库相关的依赖包(添加完成后刷新 maven 即可):

<!--添加MyBatis依赖包-->        <dependency>            <groupId>org.mybatis.spring.boot</groupId>            <artifactId>mybatis-spring-boot-starter</artifactId>            <version>2.1.3</version>        </dependency>        <!--使用第三方的数据库连接池方案(不使用mysql驱动包中内置的连接池)-->        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>druid-spring-boot-starter</artifactId>            <version>1.2.3</version>        </dependency>        <!--添加数据库驱动包-->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <!--可以不添加,表示我们编译时不能使用这个依赖包中的API,运行时才会依赖-->            <scope>runtime</scope>        </dependency>
复制代码

(2)准备 springboot 启动配置

配置 application.properties 文件:

配置 mybatis:数据库相关的信息:

#配置mybatis:数据库相关的信息(注意修改数据库名等信息)spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=falsespring.datasource.username=rootspring.datasource.password=123456
复制代码

配置 mybatis 的 xml 文件路径

#mybatis的xml文件路径(classpath:是spring框架中的写法,类加载的路径)#表示类加载根路径下,mapper文件夹下,**Mapper.xmlmybatis.mapper-locations=classpath:mapper/**Mapper.xml
复制代码

(3)准备数据库对应的 mybatis 文件

实体类、mapper 文件、xml 文件(一张表,对应一套)。

以用户表 user 为例:

【1】添加实体类

【2】添加 mapper 接口

【3】添加 UserMapper.xml 文件

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

<?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的全限定名--><mapper namespace="com.example.demo.mapper.UserMapper"></mapper>
复制代码

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

<?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的全限定名--><mapper namespace="org.example.mapper.UserMapper"><!--select/update等标签是sql语句,id和接口方法名一致--><!--resultType指定结果集转换的类型:如果是一行数据,设置为具体的一个类型,如果是多行数据,设置为List包裹的泛型数据-->    <select id="selectAll" resultType="org.example.model.User">        select * from  user    </select></mapper>
复制代码

上面的代码中:

  • select/update/insert/delete 标签是 sql 语句,id 和接口方法名一致;

  • resultType 指定结果集转换对象的类型:如果是一行数据,设置为一个具体的类型,如果是多行数据,设置为 List 包裹的泛型类型。

2、插入操作->获取自增的主键

再很多场景下,都需要返回自增主键的值,如:



新增一个用户后:

(1)如果刷新页面=>新增的自增主键可以不返回,会重新请求用户列表的数据;

(2)如果不刷新页面=>必须返回自增主键的值。不刷新页面,列表多出来的一行用户,没有主键 id,无法进行正常的删除、修改操作。

如何获取自增主键呢?

<!-- 返回自增id --><!--xml文件的sql语句中,需要添加两个参数,参能获取到自增的主键--><insert id="add2" useGeneratedKeys="true" keyProperty="id">    insert into userinfo(username,password,photo,state)    values(#{username},#{password},#{photo},1)</insert>
复制代码
  • useGeneratedKeys:MyBatis 使用 JDBC 的 getGeneratedKeys 方法去除由数据库生成的主键(MySQL 关系型数据库管理系统的自增字段),默认值:false;

  • keyColumn:设置生成键值再表中的列名,在某些数据库中,当主键列不是表中的第一列时,必须进行设置。如果生成列不止一个,可以用逗号分隔开;

  • keyProperty:值定能够为一识别对象的属性。如果生成列不止一个,可以用逗号分割多个属性名称。

3、mybatis 处理结果集

类似于 jdbc 处理结果集的方式:

  • 遍历:移动到下一行;

  • 每一条数据对应一个对象:把每个字段设置到对象的属性。

结果集字段名=对象的属性名,设置该字段的值为对象的属性;如果结果集字段名带_下划线,对象属性也需要一样(可以在 application.properties 中配置为下划线自动转驼峰式)

如果查询的结果记得字段和对象的属性名不一致,就不能使用结果集类型(resultType),需要使用结果集映射(resultMap)

<!--结果集映射resultMap,指定Java对象的属性和接轨国际字段的映射关系-->    <resultMap id="BaseResultMap" type="org.example.model.User"><!--id标签配置主键字段的映射,result标签配置非主键字段的映射--><!--每个标签配置两个属性,property指定Java对象属性,column指定对应的sql字段-->        <id property="id" column="id" />        <result property="username" column="username"/>        <result property="password" column="password"/>        <result property="nameDesc" column="name_desc"/>    </resultMap><!--查询字段的name_desc和User中nameDesc属性不一致,需要配置resultMap-->    <select id="selectAll" resultMap="BaseResultMap">        select * from  user    </select>
复制代码

三、MyBatis 表查询

1、MyBatis 占位符

#{变量名}:

  • 原理:先替换为 jdbc 占位符?,再执行 jdbc 的替换

  • 作用:防止 sql 注入

${变量名}:

  • 原理:拼接字符串;

  • 可能存在 sql 注入

  • 应用:再进行 orderby 排序时,占位符需要使用 ${},不能使用 #{}(会由多余的单引号)

2、like 查询

like 查询不能使用 #{}(会报错),也不能直接使用 ${}(存在 sql 注入),可以考虑使用 mysql 的聂志函数 concat()来处理。

<select id="findUserByName3" resultType="com.example.demo.model.User">     select * from userinfo where username like concat('%',#{username},'%');</select>
复制代码

3、多表查询(关系映射)

(1)一对一表映射

先查看文章和用户的一对一关系(ArticleMapper.xml 文件编写如下):

<resultMap id="BaseResultMap" type="org.example.model.Article">    <!--        文章表字段和对象属性的关联-->        <id column="id" property="id" />        <result column="title" property="title" />        <result column="content" property="content" />        <result column="create_time" property="createTime" />        <result column="user_id" property="userId" />        <!-- 文章属性user设置为1对1映射 -->        <!--用户信息的映射,再另一个类中有,直接拿来用就行-->        <!--找到u_开头的结果集字段,去除前缀,映射到UserMapper.xml中的结果集映射中-->        <association property="user" columnPrefix="u_"            resultMap="org.example.mapper.UserMapper.BaseResultMap">        </association>    </resultMap>
<!-- 文章表关联用户表,返回文章表和用户表1对1映射--> <select id="selectOneToOne" resultMap="BaseResultMap"> select u.id u_id, u.username u_username, u.password u_password, u.nickname u_nickname, u.head u_head, u.github u_github, a.id, a.title, a.content, a.create_time, a.user_id from user u,article a where u.id=a.user_id </select>
复制代码

(2)一对多表映射

对于一对多映射,查询结果为一个集合。

查看用户和文章的一对多关系(需要修改 Usermapper.xml 文件):

<!-- 结果集映射resultMap,指定Java对象的属性,和结果集字段的映射关系 -->    <resultMap id="BaseResultMap" type="org.example.model.User">        <!-- id标签是配置主键字段的映射,result标签配置非主键字段的映射 -->        <!-- id/result标签,都要配置两个属性:property指定java对象属性,column指定结果集字段名 -->        <id property="id" column="id" />        <result property="username" column="username" />        <result property="password" column="password" />        <result property="nickname" column="nickname" />        <result property="head" column="head" />        <result property="github" column="github" />        <result property="nameDesc" column="name_desc" />        <!-- 一个用户关联多篇文章:1对多映射 -->        <collection property="articles"                    columnPrefix="a_"                    resultMap="org.example.mapper.ArticleMapper.BaseResultMap"        />    </resultMap>    <select id="selectOneToMany" resultMap="BaseResultMap">        select            u.id,            u.username,            u.password,            u.nickname,            u.head,            u.github,            a.id a_id,            a.title a_title,            a.content a_content,            a.create_time a_create_time,            a.user_id a_user_id        from user u,article a            where u.id=a.user_id    </select>
复制代码

四、动态 SQL

1、<if>标签

sql 中,分为必填字段和非必填字段,对于非必填的字段,需要根据用户传进来的数据判断是否传入该字段,使用<if>标签。

<!--对象中性别字段不为空时,才传入--><!--test中的sex,时对象中的属性,不是数据库中的字段-->insert into user(    username,    password,    nickname,    <if test="sex != null">        sex,    </if>) values (    #{username},    #{password},    #{nickname},    <if test="sex != null">        #{sex},    </if>)
复制代码

2、<trim>标签

之前的 if 标签,只有一个字段是选填项,如果有多个字段选填,使用 if 标签的时候可能会存在“,”处理的问题,可以使用 trim 标签,对多个字段都采取动态生成的方式,标签中有如下属性:

  • prefix:值作为整个语句块的前缀;

  • suffix:值作为整个语句块的后缀;

  • prefixOverride:表示整个语句块要去掉的前缀;

  • suffixOverride:表示整个语句块要去掉的后缀;

insert into user<!--拼接前缀、后缀小括号,同时去掉最后多的一个逗号--><trim prefix="(" suffix=")" suffixOverrides=",">    <if test="username != null">        username,    </if>    <if test="password != null">        password,    </if></trim><trim prefix="values (" suffix=")" suffixOverrides=",">    <if test="username != null">    #{username},    </if>    <if test="password != null">        #{password},    </if></trim></insert>
复制代码

3、<where>标签

查询操作:对于 if 标签和 trim 标签,如果进行 where 查询,此时不传入字段时会报错,所以查询的时候需要使用 where 标签。

select * from user<where>    <if test="username != null">        and username=#{username}    </if>    <if test="password != null">        and password=#{password}    </if></where>
复制代码

4、<set>标签

修改操作,修改的字段和值是可选的场景使用。

update user<set>    <if test="username != null">        and username=#{username}    </if>    <if test="password != null">        and password=#{password}    </if></set>
复制代码

5、<foreach>标签

对集合进行遍历,有以下属性:

  • collection:绑定方法参数中的集合;

  • item:遍历时的每一个对象;

  • open:语句块的前缀;

  • close:语句块的后缀;

  • separactor:每次遍历之间间隔的字符串。

使用场景:

(1)根据 id 进行批量删除:

<delete id="deleteByIds">    delete from article        where id in        <foreach collection="list" item="item" open="(" close=")" separator=",">            #{item}        </foreach></delete>
复制代码

(2)批量插入:

insert into user(username,password) values<foreach collection="list" item="item" separactor=",">    (#{item.username},#{item.password})</foreach>
复制代码


用户头像

JAVA活菩萨

关注

还未添加个人签名 2022.07.25 加入

还未添加个人简介

评论

发布
暂无评论
MyBatis操作数据库_Java_JAVA活菩萨_InfoQ写作社区