什么是 MyBatis?
mybatis 是一款优秀的持久层框架,用于简化 JDBC 开发
MyBatis 本是 Apach 的一个开源项目 iBatis,2021 年这个项目由 apach software foundation 迁移到了 google code,并且改名为 MyBatis。2013 年 11 月迁移到 GitHub
官网:https://mybatis.org/mybatis-3/zh/index.html
持久层
1.负责将数据保存到数据库的那一层代码
2.JavaEE 三层架构:表现层、业务层、持久层
框架
1.框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
2.在框架的基础之上构建软件编写更加高效、规范、通用、可扩展
JDBC 缺点
JDBC 可参考:https://blog.csdn.net/weixin_46665411/article/details/122346809
1.硬编码
-
注册驱动,获取连接
-
SQL 语句
2.操作繁琐
-
手动设置参数
-
手动封装结果集
MyBatis 快速入门
查询 User 表中,所有数据
1.创建 user 表,添加数据
创建 tb_user
CREATE DATABASE mybatis;
USE mybatis;
DROP table if EXISTS tb_user;
CREATE TABLE tb_user(
id INT PRIMARY KEY auto_increment,
username VARCHAR(20),
`password` VARCHAR(20),
gender CHAR(1),
addr VARCHAR(30)
);
INSERT INTO tb_user VALUES(1,'zhangsan','123','男','北京');
INSERT INTO tb_user VALUES(2,'李四','234','女','天津');
INSERT INTO tb_user VALUES(3,'王五','11','男','西安');
2.创建模块,导入模块
3.编写 MyBatis 核心配置文件–>替换连接信息,解决硬编码问题
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库连接信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载SQL映射文件-->
<mapper resource="userMapper.xml"/>
</mappers>
</configuration>
4.编写 SQL 映射文件–>统一管理 sql 语句,解决硬编码问题
映射文件 userMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="test">
<select id="selectAll" resultType="com.mkyuan.pojo.User">
select * from tb_User;
</select>
</mapper>
5.编码
- 定义 POJO 类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String userName;
private String password;
private String gender;
private String addr;
}
- 加载核心配置文件,获取 SqlSessionFactory 对象
//1.加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream;
SqlSessionFactory sqlSessionFactory = null;
{
try {
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
- 获取 SqlSession 对象,执行 SQL 语句
//2.获取SqlSession对象,用来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.执行sql
List<User> users = sqlSession.selectList("test.selectAll");
- 释放资源
//4.释放资源
sqlSession.close();
Mapper 代理开发
1.定义与 SQL 映射文件同命的 Mapper 接口,并且将 Mapper 接口和 SQL 映射文件放置在同一目录下
2.设置 SQL 映射文件的 nameSpace 属性为 Mapper 接口全限定类名
3.在 Mapper 接口中定义方法,方法名就是 SQL 映射文件中 sql 语句中的 id,并保持参数类型和返回值类型一致
4.编码
-
通过 SqlSession 的 getMapper 方法获取 Mapper 接口的代理对象
-
调用对应方法完成 sql 的执行
//3.执行sql
//List<User> users = sqlSession.selectList("test.selectAll");
//3.1获取UserMapper接口的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectAll();
细节:如果 Mapper 接口名称和 SQL 映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化 SQL 映射文件的加载
<mappers>
<!--加载SQL映射文件-->
<!--mapper resource="com/mkyuan/mapper/userMapper.xml"/>-->
<!--Mapper代理方式-->
<package name="com.mkyuan.mapper"/>
</mappers>
MyBatis 核心配置文件
类型别名
<typeAliases >
<package name="com.mkyuan.pojo"/>
</typeAliases>
配置各个标签时,需要遵循前后顺序
配置文件完成增删改查
1.创建表 tb_brand
-- 删除tb_brand表
DROP TABLE
IF
EXISTS tb_brand;-- 创建tb_brand表
CREATE TABLE tb_brand (-- id主键
id INT PRIMARY KEY auto_increment,-- 品牌名称
brand_name VARCHAR ( 20 ),-- 企业名称
company_name VARCHAR ( 20 ),-- 排序字段
ordered INT,-- 排序字段
description VARCHAR ( 100 ),-- 描述信息
`status` INT -- 状态:0:禁用 1启用
);-- 添加数据
INSERT INTO tb_brand ( brand_name, company_name, ordered, description, `status` )
VALUES
( '三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0 ),
( '华为', '华为技术有限公司', 100, '华为致力于把数字世界带给每个人、每个家庭、每个组织、构建万物互联的智能世界', 1 ),
( '小米', '小米科技有限公司', 50, 'are you ok', 0 )
2.查询所有数据
① 编写接口方法:Mapper 接口
public interface BrandMapper {
/**
* 查询所有
* @return
*/
List<Brand> selectAll();
}
-
参数:无
-
结果:List
② 编写 SQL 语句:SQL 映射文件
<mapper namespace="com.mkyuan.mapper.BrandMapper">
<select id="selectAll" resultType="com.mkyuan.pojo.Brand">
select * from tb_brand;
</select>
</mapper>
③ 执行方法,测试
@Test
public void TestSelectAll() throws IOException {
//1.获取SqlSessionFactory
String resources = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取mapper接口的代理对象
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
List<Brand> brands = mapper.selectAll();
for (Brand brand : brands) {
System.out.println(brand);
}
//5.释放资源
sqlSession.close();
}
实体类属性名和数据库名不一致,不能自动封装数据
① 起别名:对不一样的列名起别名,让别名和实体类的属性名一致
- 缺点:每次查询都要定义一次别名
- sql 片段:不灵活
② resultMap
-
定义标签
-
在< select>标签中使用 resultMap 属性替换 resultType 属性
<mapper namespace="com.mkyuan.mapper.BrandMapper">
<!--数据库的字段名称和实体类的属性名称不一样,则不能自动封装数据
① 起别名:对不一样的列名起别名,让别名和实体类的属性名一致
* 缺点:每次查询都要定义一次别名
*sql片段:不灵活
② resultMap
(1).定义<resultMap>标签
(2).在<select>标签中使用resultMap属性替换resultType属性
-->
<resultMap id="brandResultMap" type="brand">
<!--
id:完成主键字段的映射
column:表的列名
property:实体类的属性名
result:完成一般字段的映射
column:表的列名
property:实体类的属性名
-->
<result column="brand_name" property="brandName"></result>
<result column="company_name" property="companyName"></result>
</resultMap>
<select id="selectAll" resultMap="brandResultMap">
select
*
from tb_brand;
</select>
<!--sql片段-->
<!-- <sql id="brand_column">
id,brand_name as brandName,company_name as companyName,ordered,description,status
</sql>
<select id="selectAll" resultType="com.mkyuan.pojo.Brand">
select
<include refid="brand_column"></include>
from tb_brand;
</select>
-->
</mapper>
3.查看详情
① 编写接口方法:Mapper 接口
Brand selectById(int id);
-
参数:id
-
结果:Brand
② 编写 SQL 语句:SQL 映射文件
<!-- 参数占位符
① #{}:会将其替换为?,为了防止SQL注入
② ${}:拼接sql,会存在SQL注入问题
③ 使用时机
- 参数传递的时候:#{}
- 表名或者列名不固定的情况下:${} 会存在SQL注入问题
参数类型: parameterType:可以省略
特殊字符处理:
① 转义字符:
② CDATA区
-->
<!-- <select id="selectById" resultMap="brandResultMap">
select * from tb_brand where id= #{id}
</select>-->
<select id="selectById" resultMap="brandResultMap">
select * from tb_brand where id
<![CDATA[
<
]]>
#{id}
</select>
③ 执行方法,测试
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
Brand brand = mapper.selectById(id);
参数占位符
① #{}:会将其替换为?,为了防止 SQL 注入
② ${}: 拼接 sql,会存在 SQL 注入问题
③ 使用时机
-
参数传递的时候:#{}
-
表名或者列名不固定的情况下:${} 会存在 SQL 注入问题
参数类型: parameterType:可以省略
特殊字符处理:
① 转义字符
② CDATA 区
4.条件查询
① 编写接口方法:Mapper 接口
/**
* 条件查询
* 参数类型
* 1.散装参数:如果方法中有多个参数,需要使用@Param("SQL参数占位符名称")
* 2.对象参数
* 3.map集合参数
* @param status
* @param companyName
* @param brandName
* @return
*/
List<Brand> selectByCondition(
@Param("status") int status,
@Param("companyName") String companyName,
@Param("brandName") String brandName
);
List<Brand> selectByCondition(Brand brand);
List<Brand> selectByCondition(Map map);
-
参数:所有查询条件
-
结果:List< Brand>
② 编写 SQL 语句:SQL 映射文件
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where status =#{status}
and company_name like #{companyName}
and brand_name like #{brandName}
</select>
③ 执行方法,测试
@Test
public void TestSelectByCondition() throws IOException {
int status = 1;
String companyName = "华为";
String brandName = "华为";
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
//1.获取SqlSessionFactory
// Brand brand1 = new Brand();
// brand1.setStatus(status);
// brand1.setBrandName(brandName);
// brand1.setCompanyName(companyName);
Map map = new HashMap();
map.put("status", status);
map.put("companyName", companyName);
map.put("brandName", brandName);
String resources = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取mapper接口的代理对象
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
//List<Brand> brands = mapper.selectByCondition(status,companyName,brandName);
//List<Brand> brand1 = mapper.selectByCondition(brand1);
List<Brand> brands = mapper.selectByCondition(map);
for (Brand brand : brands) {
System.out.println(brand);
}
//5.释放资源
sqlSession.close();
}
sql 语句设置多个参数有几种方式?
-
散装参数:需要使用@Param(“SQL 中的参数占位符名称”)
-
实体类封装参数:只需要保证 SQL 中的参数名和实体类属性名对应上,即可设置成功
-
map 集合:只需要保证 SQL 中参数名和 map 集合的键的名称对应上,即可设置成功
5.动态条件查询
SQL 语句会随着用户的输入或外部条件的变化而变化,我们称之为动态的 SQL
Mybatis 对动态 SQL 有很强大的支撑
if
<!--
if:用于判断参数是否有值,使用test进行条件判断
存在的问题:第一个条件不需要逻辑运输符
解决方案:
① 使用恒等式让所有条件格式都一样
② <where>标签替换where关键字
-->
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
<where>
<if test="status != null">
and status =#{status}
</if>
<if test="companyName != null">
and company_name like #{companyName}
</if>
<if test="brandName != null">
and brand_name like #{brandName}
</if>
</where>
</select>
choose(when,otherwise)
<select id="selectByConditionSingle" resultMap="brandResultMap">
select *
from
tb_brand
<where>
<choose><!--相当于switch-->
<when test="status != null"><!--相当于case-->
status =#{status}
</when>
<when test="companyName != null and companyName !=''"><!--相当于case-->
and company_name like #{companyName}
</when>
<when test="brandName != null and brandName !=''"><!--相当于case-->
and brand_name like #{brandName}
</when>
<otherwise><!--相当于default-->
1=1
</otherwise>
</choose>
</where>
</select>
6.添加
① 编写接口方法:Mapper 接口
/**
* 添加
* @param brand
*/
void add(Brand brand);
-
参数:除了 id 之外的所有数据
-
结果:void
② 编写 SQL 语句:SQL 映射文件
<insert id="add" parameterType="brand">
insert into tb_brand(brand_name,company_name,ordered,description,status)
values(#{brandName},#{companyName},#{ordered},#{description},#{status})
</insert>
③ 执行方法,测试
@Test
public void TestAdd() throws IOException {
int status = 1;
String companyName = "波导手机";
String brandName = "波导";
String description ="手机中的战斗机";
int ordered = 100;
//1.获取SqlSessionFactory
Brand brand1 = new Brand();
brand1.setStatus(status);
brand1.setBrandName(brandName);
brand1.setCompanyName(companyName);
brand1.setDescription(description);
brand1.setOrdered(ordered);
String resources = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取mapper接口的代理对象
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
mapper.add(brand1);
//提交事务
sqlSession.commit();
//5.释放资源
sqlSession.close();
}
mybatis 事务:
-
openSession():默认开启事务,进行增删改操作后需要使用 sqlSession.commit()手动提交事务
-
openSession(true):可以设置为自动提交事务(关闭事务)
7.添加-主键返回
在数据添加成功后需要获取插入数据库数据的主键的值
比如:添加订单和订单项
-
添加订单
-
添加订单项,订单中需要设置所属订单的 id
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into tb_brand(brand_name,company_name,ordered,description,status)
values(#{brandName},#{companyName},#{ordered},#{description},#{status})
</insert>
测试
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
mapper.add(brand1);
Integer id = brand1.getId();
System.out.println(id);
8.修改-修改全部字段
① 编写接口方法:Mapper 接口
/**
* 修改
* @return
*/
int update(Brand brand);
-
参数:所有数据
-
结果:void
② 编写 SQL 语句:SQL 映射文件
<update id="update">
update tb_brand
set
brand_name = #{brandName},
company_name=#{companyName},
ordered=#{ordered},
description=#{description},
status=#{status}
where id =#{id}
</update>
③ 执行方法,测试
@Test
public void TestUpdate() throws IOException {
int status = 1;
String companyName = "波导手机";
String brandName = "波导plus";
String description ="波导手机,手机中的战斗机";
int ordered = 100;
int id=4;
//封装对象
Brand brand1 = new Brand();
brand1.setStatus(status);
brand1.setBrandName(brandName);
brand1.setCompanyName(companyName);
brand1.setDescription(description);
brand1.setOrdered(ordered);
brand1.setId(id);
//1.获取SqlSessionFactory
String resources = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取mapper接口的代理对象
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
int count = mapper.update(brand1);
System.out.println(count);
//提交事务
sqlSession.commit();
//5.释放资源
sqlSession.close();
}
9.修改数据-修改动态字段
① 编写接口:Mapper 接口
-
参数:部分数据,封装到对象中
-
结果:void
② 编写 SQL 语句:SQL 映射文件
<update id="update">
update tb_brand
set
<if test="brandName!=null and brandName!=''">
brand_name = #{brandName},
</if>
<if test="companyName !=null and companyName !=''">
company_name=#{companyName},
</if>
<if test="ordered!=null">
ordered=#{ordered},
</if>
<if test="description!=null and description !=''">
description=#{description},
</if>
<if test="status!=null">
status=#{status}
</if>
where id =#{id}
</update>
③ 执行方法,测试
@Test
public void TestUpdate() throws IOException {
int status = 1;
String companyName = "波导手机";
String brandName = "波导plus2";
String description ="波导手机,手机中的战斗机";
int ordered = 103;
int id=4;
//封装对象
Brand brand1 = new Brand();
brand1.setStatus(status);
brand1.setBrandName(brandName);
// brand1.setCompanyName(companyName);
// brand1.setDescription(description);
brand1.setOrdered(ordered);
brand1.setId(id);
//1.获取SqlSessionFactory
String resources = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取mapper接口的代理对象
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
int count = mapper.update(brand1);
System.out.println(count);
//提交事务
sqlSession.commit();
//5.释放资源
sqlSession.close();
}
10.删除一个
① 编写接口方法,Mapper 接口
/**
* 删除一个
* @param id
*/
void deleteById(int id);
-
参数:id
-
结果:void
② 编写 SQL 语句,SQl 映射文件
<delete id="deleteById">
delete from tb_brand where id=#{id}
</delete>
③ 执行方法,测试
@Test
public void TestDeleteById() throws IOException {
int id = 5;
//1.获取SqlSessionFactory
String resources = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取mapper接口的代理对象
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
mapper.deleteById(id);
//提交事务
sqlSession.commit();
//5.释放资源
sqlSession.close();
}
11.批量删除
① 编写接口方法,Mapper 接口
/**
* 批量删除
*/
void deleteByIds(@Param("ids") int[] ids);
-
参数:id 数组
-
结果:void
② 编写 SQL 语句,SQL 映射文件
<!--
mybatis会将数组参数,封装为一个Map集合
①默认:array = 数组
②使用@Param注解改变map集合的默认key的名称
-->
<delete id="deleteById">
delete from tb_brand where id=#{id}
</delete>
<delete id="deleteByIds">
delete
from tb_brand where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
③ 执行方法,测试
@Test
public void TestDeleteByIds() throws IOException {
int ids[] = {9,10,11};
//1.获取SqlSessionFactory
String resources = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取mapper接口的代理对象
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
mapper.deleteByIds(ids);
//提交事务
sqlSession.commit();
//5.释放资源
sqlSession.close();
}