Author:Dawn_T17🥥
目录
MyBatis介绍
MyBatis入门
入门程序
JDBC
一个小插件——lombok
数据库连接池
MyBatis基础操作 ——通过注解配置
前置
占位符
日志
预编译SQL 和SQL注入展示
1.删除操作
2.添加操作
主键返回
3.更新操作
4.查询操作
数据封装
条件查询
MyBatis基础操作 ——通过XML配置
***四个一致
MybatisX插件
动态SQL
MyBatis介绍
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。 ---MyBatis官网
持久层(Persistence Layer)即DAO层
持久层是软件架构中的一个重要概念,主要负责数据的持久化存储和管理。
MyBatis入门
入门程序
1.创建工程 (三步工作)
2.配置MyBatis
MyBatis项目下的resources里有一个配置文件
用来配置数据库连接信息
配置数据库连接信息-四要素
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=1234
3.编写SQL语句
@Mapper 注解(映射器)
在运行时候,会自动生成该接口实现类对象(代理对象),并且将该对象交给IOC容器管理
@Mapper
通常是在 MyBatis 框架中使用的注解。
当一个接口被标注了 @Mapper
注解后,MyBatis 框架会自动为这个接口创建代理对象,并将其纳入 MyBatis 的映射器体系中,使得在其他代码中可以通过这个接口来执行数据库的操作,而无需手动创建实现类。
@Select注解
是在 MyBatis 框架中使用的注解。
它用于在 Mapper 接口的方法上,指定要执行的 SQL 查询语句。通过 @Select
注解,可以清晰地定义方法与特定的数据库查询操作的关联。
@Selete注解用来传递查询数据相关的SQL语句
package com.dawn_t.mapper;import com.dawn_t.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper //在运行时候,会自动生成该接口实现类对象(代理对象),并且将该对象交给IOC容器管理
public interface UserMapper {//查询全部用户信息@Select("select * from user")public List<User> list();
}
4.测试
package com.dawn_t;import com.dawn_t.mapper.UserMapper;
import com.dawn_t.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTest
class SpringbootMybatisQuickstartApplicationTests {@Autowiredprivate UserMapper userMapper;@Testpublic void testListUser(){List<User> userList =userMapper.list();userList.stream().forEach(user -> {System.out.println(user);});}}
JDBC
见其他文章
因为JDBC代码繁杂,数据库连接释放次数频繁,浪费资源。
现在业务开发主要运用SpringBoot 联合Mybatis
一个小插件——lombok
通过Maven导入lombok相关依赖(不用版本号)
lombok会在编译时候,自动生成相关代码
这个过程需要lombok插件
这个插件是IDEA自带的插件,下载IDEA时候已经下载好了。
数据库连接池
数据库连接池是一种用于优化数据库操作性能的技术。
它的主要作用是减少创建和关闭数据库连接所带来的开销。在没有连接池的情况下,每次与数据库进行交互都需要创建新的连接,完成操作后再关闭连接。这个过程涉及到一系列的系统资源分配和释放,非常耗时。
例如,在一个高并发的 Web 应用中,如果每个用户请求都单独创建和关闭数据库连接,服务器的性能会受到极大影响,可能导致响应延迟甚至系统崩溃。
连接池会预先创建一定数量的数据库连接,并将这些连接保存在池中。当应用程序需要访问数据库时,它可以从池中获取一个可用的连接,使用完毕后将连接放回池中,而不是关闭连接。
在实际应用中,合理配置连接池的参数非常重要,比如最大连接数、最小连接数、等待超时时间等。如果最大连接数设置过小,可能无法满足高并发的需求;如果设置过大,又可能会浪费系统资源。
MyBatis基础操作 ——通过注解配置
前置
占位符
***Mybatis中的SQL语句动态获取参数
用占位符 #{ }
#{a}
日志
日志是在软件开发和系统运行过程中,对关键事件和操作的记录。
日志很杂,很多,不用记
未添加日志输出前,我们看不到具体的实行过程
这里在application.properties添加一个Mybatis日志输出指令
#配置MyBatis的日志信息,指定输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
就会在运行和测试的时候在控制台上输出 预编译的SQL
预编译SQL 和SQL注入展示
预编译 SQL 是一种提高数据库操作安全性和性能的技术。
它的工作原理是将 SQL 语句的结构(模板)提前发送给数据库进行解析和优化,而实际执行时只需要将具体的参数值传递进去。
优势:
-
安全性增强
有效防止 SQL 注入攻击。因为参数值是作为数据而不是可执行的代码处理,恶意输入无法改变 SQL 语句的结构和逻辑。 -
性能提升
数据库可以对预编译的语句进行缓存和复用,对于相同结构但参数不同的多次执行,节省了重复解析和优化的开销。 -
代码可读性和可维护性
使代码更加清晰和易于理解,将 SQL 语句的结构和参数分离,减少了复杂的字符串拼接操作。
关于性能
非预编译和预编译对比
关于安全
SQL注入展示
SQL 注入是一种常见的网络攻击手段,攻击者通过在输入中插入恶意的 SQL 代码来改变原本 SQL 语句的逻辑,从而获取未经授权的数据或者执行非法操作。
如果没有使用预编译,直接拼接用户输入来构建 SQL 语句:
String username = request.getParameter("username");
String password = request.getParameter("password");String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
如果攻击者输入用户名 ' OR '1'='1
和任意密码,最终的 SQL 语句就会变成:
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = 'any_password'
这样就绕过了正常的认证逻辑,能够获取所有用户的数据。
如下图,密码行输入一些SQl语句,改变SQL查询条件,非法登录!
用预编译处理后
数据库会将预编译的 SQL 语句结构进行解析和优化,并将传入的参数视为数据而不是可执行的代码,从而避免了 SQL 注入的风险。
1.删除操作
@Delete注解用来传递删除数据相关的SQL语句
接口方法
@Mapper
public interface EmpMapper {@Delete("delete from emp where id=#{id}")public int delete(Integer id);
}
测试类
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {@Autowiredprivate EmpMapper empMapper;@Testpublic void testDelete(){empMapper.delete(17);}//可以获取返回值,这个返回值表示影响的数据数量
// 将empMapper的返回值设为int
// public void testDelete(){
// int delete=empMapper.delete(16);
// System.out.println(delete);
// }
}
2.添加操作
@Insert注解用来传递添加数据相关的SQL语句
pojo对象准备
//lombok
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {//数据库中用下划线_表示的数据名称,pojo对象里我换成了驼峰命法表示private Integer id;private String username;private String password;private String name;private Short gender;private String image;private Short job;private LocalDate entrydate;private Integer deptId;private LocalDateTime createTime;private LocalDateTime updateTime;
}
接口方法
@Mapper
public interface EmpMapper {
//直接写死,不推荐
// @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) " +
// "values('T','Tang',1,'1.jpg',1,'2005-06-07',1,now(),now())")
// public void insert();//占位符动态赋值@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) " +"values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")public void insert(Emp emp);
}
//values中是Emp对象中的变量名称
当插入的数据有多个值的时候,可以封装进一个对象中,一起动态赋值传递具体参数
测试类
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {@Autowiredprivate EmpMapper empMapper;@Testpublic void testInsert(){Emp emp= new Emp();emp.setUsername("T");emp.setName("Tang");emp.setImage("1.jpg");emp.setGender((short) 1);emp.setJob((short) 1);emp.setEntrydate(LocalDate.of(2005,6,17));emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());emp.setDeptId(1);empMapper.insert(emp);}
}
主键返回
现在用get()方法获得id,会获得NULL
我们用主键返回,为主键属性赋值
主键返回运用场景:
在插入一条主表数据后,获取到主键值,然后将其用于插入关联表的数据。
例如,先插入订单主表数据,获取订单 ID 后,再将其用于插入订单详情表中,以建立关联。
@Options(useGeneratedKeys = true,keyProperty = "id")@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) " +"values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")public void insert(Emp emp);
@Options(useGeneratedKeys = true,keyProperty = "id")
useGeneratedKeys = true
表示使用数据库生成的主键,keyProperty = "id"
指定将主键值赋给@Options注解注解的实体类 Emp中的 id
属性。
3.更新操作
接口方法
@Mapper
public interface EmpMapper {@Update("update emp set username=#{username},name=#{name},gender=#{gender},image=#{image}," +"job=#{job},entrydate=#{entrydate},dept_id=#{deptId},update_time=#{updateTime} where id=#{id}")public void update(Emp emp);
}
测试类
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {@Autowiredprivate EmpMapper empMapper;@Testpublic void testUpdate(){Emp emp= new Emp();emp.setId(15);emp.setUsername("X");emp.setName("Xu");emp.setImage("1.jpg");emp.setGender((short) 1);emp.setJob((short) 1);emp.setEntrydate(LocalDate.of(2005,6,17));emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());emp.setDeptId(1);empMapper.update(emp);}
}
4.查询操作
接口方法
@Mapper
public interface EmpMapper {@Select("select * from emp where id=#{id}")public Emp getById(Integer id);
}
测试类
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {@Autowiredprivate EmpMapper empMapper;@Testpublic void testGetById(){Emp emp= empMapper.getById(18);System.out.println(emp);}
}
打开控制台会发现,查询出的结果,未和数据库属性名统一的接受不到数据
数据封装

改进措施
1.
// 方案一:给字段起别名@Select("select id, username, password, name, gender, image, job, " +"entrydate, dept_id deptId, create_time createTime, update_time updateTime from emp where id=#{id}")public Emp getById(Integer id);
2.
//方案二:通过@Results和@Result注解手动映射封装@Results({@Result(column ="dept_id" ,property = "deptId"),@Result(column ="create_time" ,property = "createTime"),@Result(column ="update_time" ,property = "updateTime")})@Select("select * from emp where id=#{id}")public Emp getById(Integer id);
@Results
和 @Result
注解用于手动定义数据库表字段与 Java 对象属性之间的映射关系。
column
:指定数据库表中的列名。它指明了从数据库中获取数据时所依据的列。
property
:指定要将对应列的值映射到的 Java 对象的属性名。
通过 @Result
注解中对 column
和 property
的设置,实现了数据库表列与 Java 对象属性之间的精确映射,确保从数据库获取的数据能够正确地赋值给对应的对象属性。
3.
//方案三:开启mybaties的驼峰命名自动映射开关@Select("select * from emp where id=#{id}")public Emp getById(Integer id);
前提,在application.properties里添加按驼峰命名自动映射的开关配置
(打 camel 就能弹出来)
#开启mybaties的驼峰命名自动映射开关
mybatis.configuration.map-underscore-to-camel-case=true
条件查询
TIP
引号内不可以使用 #{},因为是预编译,编译后会变成 ?,SQL语法中引号内不得有占位符?
改成${}拼接占位符可以
也可以用SQL中的concat函数
concat函数
在 MySQL 中,CONCAT
函数用于将多个字符串连接在一起。
其语法为:CONCAT(str1, str2, str3,...)
,它可以接受多个参数,并将这些参数依次连接成一个字符串。
@param注解
在 MyBatis 的映射接口方法中,当一个方法有多个参数时,使用 @Param
注解可以为参数指定一个明确的名称,方便在 SQL 语句中通过指定的名称来引用这些参数。
使用原因:Java编译后,变量名是没有的,会被转化,例如 var1,var2等。所以当有多个参数时候 ,会无法互相识别匹配,用@param注解使得参数名和数据属性名称相关联。
@Select("SELECT * FROM emp WHERE name = #{name}")
Emp getEmpByName(@Param("name") String name);
@Select("select * from emp where name like concat('%',#{name},'%') and gender=#{gender} and " +"entrydate between #{begin} and #{end} order by update_time desc ")public List<Emp> list(@Param("name") String name,@Param("gender") Short gender,@Param("begin") LocalDate begin,@Param("end")LocalDate end);
@Testpublic void testList(){List<Emp> empList=empMapper.list("张", (short) 1,LocalDate.of(2010,1,1),LocalDate.of(2020,1,1));System.out.println(empList);}
MyBatis基础操作 ——通过XML配置
XML配置文件,又叫XML映射文件
在 MyBatis 中,XML 映射文件(通常以 .xml
为扩展名)用于定义 SQL 语句、结果映射以及其他与数据库操作相关的配置。
-
命名空间规范:
- 在
<mapper>
标签中,namespace
属性的值应与对应的接口全限定名一致,以确保 MyBatis 能够正确关联接口方法和 XML 中的操作定义
- 在
-
SQL 语句规范:
- 清晰准确地编写
select
、insert
、update
和delete
等操作的 SQL 语句。 - 正确使用参数占位符
#{}
来接收传入的参数,确保参数的类型与实际传递的类型匹配。
- 清晰准确地编写
-
结果映射规范:
- 对于查询操作,通过
resultType
或resultMap
来定义结果的映射方式。 - 如果使用
resultMap
,则需要在 XML 中单独定义结果映射的规则。
- 对于查询操作,通过
***四个一致
1.映射文件的名称和接口名称一致
2.namespace属性和Mapper接口全限定名一致
3.映射文件中sql语句id和Mapper接口方法名一致
4.查询语句,保证返回类型一致(resultType)
配置 XML过程:
文件名和接口名一致(同包同名),包的结构也要一致
XML文件所属包结构和接口所处包结构一致
文件名和接口名称一致
XML文件需要约束,直接去MyBtis中文网上找 ,复制粘贴
namespace属性和Mapper接口全限定名一致
关于快速获得Mapper接口全限定名
找到mapper接口,右击接口名称,复制引用
案例:
XML配置条件查询语句
XML配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dawn.springbootmybatiscrud.mapper.EmpMapper">
<!-- resultType:单条记录所封装的类型-->
<select id="list" resultType="com.dawn.springbootmybatiscrud.pojo.Emp">select * from emp where name like concat('%',#{name},'%') and gender=#{gender} andentrydate between #{begin} and #{end} order by update_time desc
</select>
</mapper>
接口(通过XML中SQL语句的id属性和此方法绑定)
public List<Emp> list(@Param("name") String name,@Param("gender") Short gender,@Param("begin") LocalDate begin,@Param("end")LocalDate end);
MybatisX插件
MybatisX 是一款针对 IDEA 或相关开发工具的插件,它为 MyBatis 的开发提供了一系列的便利功能。
-
快速导航:能够方便地在 Mapper 接口、XML 映射文件和相关的实体类之间进行快速跳转。
-
代码生成:可以根据数据库表结构自动生成 MyBatis 的 Mapper 接口、XML 映射文件等代码。
-
SQL 语句预览:在编写 Mapper 接口中的方法时,实时预览对应的 SQL 语句。
-
代码提示和补全:提供丰富的代码提示和自动补全功能,提高开发效率。
-
结果集映射预览:帮助开发者直观地了解查询结果与实体类之间的映射关系。
MybatisX 插件能够大大提升 MyBatis 开发的效率和便捷性。
安装后,可以快速导航,点击接口中的小蓝鸟即可快速导航到配置SQL的XML文件中 的小红鸟
动态SQL
在 MyBatis 中,动态 SQL 是一种强大的特性,它允许您根据不同的条件构建不同的 SQL 语句。
动态 SQL 主要通过 <if>
、<choose>
、<when>
、<otherwise>
、<foreach>
等标签来实现。
<if>
<where>
<mapper namespace="com.dawn.springbootmybatiscrud.mapper.EmpMapper">
<!-- resultType:单条记录所封装的类型-->
<select id="list" resultType="com.dawn.springbootmybatiscrud.pojo.Emp">select * from emp<where><if test="name !=null"> name like concat('%',#{name},'%') </if><if test="gender != null">and gender=#{gender} </if><if test="begin!=null and end != null">and entrydate between #{begin} and #{end} </if></where>order by update_time desc
</select>
</mapper>
<set>
<update id="updateUser">UPDATE users<set><if test="username!= null">username = #{username}, </if><if test="password!= null">password = #{password}, </if></set>WHERE id = #{id}
</update>
<foreach>
在 MyBatis 中,<foreach>
标签用于遍历集合来构建动态的 SQL 语句,常用于批量操作,如批量插入、批量更新等。
<foreach item="item" index="index" collection="collection" open="(" separator="," close=")"><!-- 要重复执行的 SQL 片段 -->
</foreach>
其中:
item
:表示每次迭代的元素。(遍历出来的元素)index
:表示当前迭代的索引。collection
:要遍历的集合属性。open
、separator
、close
:分别指定开始符号、元素之间的分隔符和结束符号。
案例:
<insert id="batchInsertUsers">INSERT INTO users (username, password)VALUES<foreach item="user" collection="userList" open="(" separator="," close=")">(#{user.username}, #{user.password})</foreach>
</insert>
<sql>
<include>
二者配合使用
<sql>
标签用于定义可复用的 SQL 片段:
<sql id="userColumns">id, username, password
</sql>
<include>
标签用于在其他 SQL 语句中引用上述定义的片段:
<select id="getUsers">SELECT <include refid="userColumns"/> FROM users
</select>
通过这种方式,可以将常用的 SQL 片段提取出来,避免重复编写,并且当需要修改这些共用部分时,只需在一处修改即可。