是一款优秀持久层框架(Dao层),用于简化jdbc开发
一、Mybatis架构
根据输入、输出参数可分类
- 简单类型:八大数据类型
- 复杂类型:pojo(标准类)、Map
二、Mybatis的ORM(对象关系映射)映射
则就是将sql的数据表内容,和java的对象建立一种映射关系,让他一一对应
三、使用
官方文档:MyBatis 3 | 入门 – mybatis
前期工作:需要两个配置文件
- 核心配置文件 -> 用来配置Mybatis -> mybatis-config.xml
- 映射配置文件 -> 用来建立表和对象关系的配置文件 -> userMapper.xml
按照顺序来就可以简单使用Mybatis,以下均结合官方文档:
场景如下:有一个user表,需要有一个select查出所有字段的方法
1.创建对应的数据表
2.创建Maven工程 ,然后导入所需的依赖
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--单元测试框架-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
3.也是最重要的其中一个部分,得明白,得配置Mybatis的核心文件,其次,就是映射配置文件,下面进行配置核心配置文件,而映射配置文件后面再说
现在resources下main创建 -> 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="导入Driver"/>
<property name="url" value="数据库地址"/>
<property name="username" value="用户名"/>
<property name="password" value="密码"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="映射配置文件地址"/>
</mappers>
</configuration>
其中,dataSource下的元素,都需要进行相应的配置
4.创建一个接口,里面定义一个方法,用来查询user表的所有内容
List<User> findAllUser ();
5.因为List中的泛型为 User,我们用User来封装我们sql查出的数据,所有接下来创建一个和数据表字段一致的数据表,而日期格式的类型使用:Date,并提供其对应的完整JavaBean
6.因为映射配置文件和接口需要在同一个目录下,而配置文件在resources下,接口在main/java下,所以接下来,需要在resources下创建一个和dao路径一致的文件夹,并在文件夹中创建映射配置文件 --------------> userMapper.xml
/**
* 注意,比如我在src的路径是 com.dongmianmao.dao.userMapper.xml
* 但是换到resources不能这样写,如果这样写,会导致目录不呈现树状,而是变成了一个文件夹
* 需要使用 com/dongmianmao/dao/userMapper.xml
* 要使用file目录的格式来创建文件
*/
<?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="在src/java下的接口路径,来绑定接口">
<select id="填写接口中绑定的方法名" resultType="查出数据存放的封装类的路径">
select * from user
</select>
</mapper>
7.有了以上的前置操作后,就可以开始正常使用了,创建一个测试类
@Test
public void test() throws IOException {
// 输入核心配置文件地址,以resources为根目录
String resource = "mybatis-config.xml";
// 通过核心配置文件地址创建一个字节输入流
InputStream is = Resources.getResourceAsStream(resource);
// 通过字节输入流读取配置文件创建工厂类
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
// 通过工厂类创建sqlsession类
SqlSession sqlSession = sqlSessionFactory.openSession();
// 使用sqlsession对象创建一个代理对象
UserDao userdao = sqlSession.getMapper(UserDao.class);
// 使用代理对象来调用方法
List<User> userList = userdao.findAllUser();
for (User user : userList) {
System.out.println(user);
}
SqlSession.close(); // 关闭资源
}
注意一个点:因为映射配置文件和接口需要在同一个目录下,而配置文件在resources下,接口在main/java下,他们最后能相同路径能够找到的原因就是因为编译后的字节码他们是在一个路径的
四、核心配置文件:properties标签
用来加载外部的资源配置文件,当需要解耦参数的时候,对核心配置文件来说,被抽离到properties的文件,就是外部资源配置文件
这个标签有一个属性:resource -> 指定需要引入的外部资源文件路径
单击根标签可以看到他的约束
第一位就为properties,当要添加标签的时候,先单机根标签查看约束
示例
<?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>
<!--加载外部配置资源文件-->
<properties resource="jdbc.properties"/>
<!--配置数据源(数据库连接)-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--配置和映射文件关联-->
<mappers>
<mapper resource="com/dongmianmao/dao/userMapper.xml"/>
</mappers>
</configuration>
- properties标签下无任何内容,让他自闭合
- resource找配置文件是以resources目录为当前目录的
- 引入后,获取值使用 ${key} 的方式来获取value
五、核心配置文件 settings
官方文档:配置_MyBatis中文网
settings是Mybatis中重要的调整设置,会改变Mybatis的运行行为
配置多,这里单讲可能会用到的
现在有以下场景:数据库字段名和映射的成员变量名不一致,导致无法自动封装结果,有两种方案
1.在对应的sql语句中,使用 AS 的方式来给查出来的值重新命名
private Strirng username;
create table tb1(
user_name vchar(20)
)
# 最后查询语句使用 as 重新命名来让mybatis找到映射字段
select user_name as username from tb1;
2.使用settings设置,开启驼峰映射
首先要先了解settings的标签规则,是settings 包裹着 setting
<!--在properties下-->
<settings>
<setting name = "" value = "" />
</settings>
在官方文档中驼峰映射的name为 mapUnderscoreToCamelCase 而value为布尔值,则
<!--开启驼峰映射 把数据库带有下划线的字段改为java驼峰命名规则的字段 user_name -> username/userName-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
别忘记了约束顺序,是排在properties下
六、核心配置文件:typeAliases(类型别名)
例如,当指定映射配置文件路径太长的时候,就需要指定别名变短,来看的可阅读性强
官方文档:配置_MyBatis中文网
父标签为:<typeAliases></typeAliases>
以下以pojo路径起别名进行演示,在typeAliases中有两个子标签
1.typeAlias,使用这个去指定路径和指定名字去简化他的路径,缺点:需要多个就要多个设置
<typeAlias type="com/dongmianmao/dao/userMapper.xml" alias="user"/>
2.package,在name指定扫文件夹后,他会扫描该路径的内容,并根据类名,引用格式为全小写/原命名
<package name="com.dongmianmao.pojo"/>
这个演示均是在核心配置文件中设置,之后在映射配置文件引用
在Mybatis中,内置了常见基本数据类型的别名,可以让我们引用类型的时候,可以直接引用类型而不用导包,例如:当查询出来是一个值的时候不需要封装成一个类,这个时候直接指定数据类型即可,其实本质上就是大写开头变成小写,大部分是这样
七、核心配置:typeHandlers(类处理器)-> 了解即可,底层已实现
Mybatis在设置预处理语句中的参数或从结果集取出一个值的时候,都会使用类处理器将获取到的值,以合适的方式==转换为java类型==
场景:在mysql中,varchar类型在java中对应String,而mybatis中,底层就是使用typeHandlers来让他
varchar => String
八、核心配置:environments -> 了解即可,底层已实现
使用这个配置,可以区分多个环境,如测试环境,开发环境等,不同的环境使用不同的配置,如同postman的环境配置一样,不同场景使用不同配置
在核心配置文件中,就已存在有对应的标签
其中:
1.主要的配置内容是在根标签的子标签<environment id=""></environment>
中包裹着,而使用id来区分是哪种环境
2.当需要切换环境的时候,则修改根标签<environments default=""></environments>
default的默认的id值
九、核心配置:事务管理器 -> 了解即可,底层已实现
事务管理器:使用jdbc的默认事务管理机制,学到spring就用spring的事务管理器
十、核心配置:数据源(数据库连接池) -> 了解即可,底层已实现
mybatis有自带的数据库连接池,在后续需要使用ssm整合才需要替换
十一、核心配置文件:mappers(映射器)
加载映射配置文件,前置要求如下:
- 在src/java/dao下的接口名,需要和resources下的映射配置文件名字相同
- 要存在在同一路径下,则采取分别在src/java和resources下创建相同目录,最后编译class在同一目录
父标签:<mappers></mappers>
同 typeAliases 一样,有两种方式可以去加载配置
1.<mapper resource="path"/>
,path指定指定的映射文件,然后映射文件再去找接口,缺点:当映射文件随着业务的增长越来越多,会导致每个都要去指定,很冗余,为了改善,就有了第二种 -> xml开发
2.<package name="path"/>
,使用该子标签,去指定接口的目录,通过接口来去找相同路径,相同名字的映射文件,直接根据整个目录去搜索,会比一个一个添加高效 -> 注解开发,推荐这个
注意
在写增删改查代码的时候,需要知道,在Mybatis中,事务是默认手动提交的,所以有了两种提交方式
- 使用 sqlSession.commit(); 来手动提交事务
- 在创建sqlSession的时候,SqlSession sqlSession = ssf.openSession(true); 在openSession()中设置true,为自动提交
十二、映射配置文件:select
用于编写select语句的代码
标签名:<select></select>
属性名 | 说明 | 是否必须 |
---|---|---|
id | 这条sql的唯一标识,和接口方法名一致 | 是 |
parameterType | 入参类型(根据类型别名写),一般也不会写,传的参数mybatis会推导 | 否 |
resultType/resultMap | 返回值类型 | 是 |
根据第二个属性,可以分为两种写法
1.没有入参,则是方法中没有形参,则就正常写,此时的接口方法为
/**
* 查询所有用户数据
* @return
*/
List<User> findAllUser();
然后在映射配置文件中则是
<select id="findAllUser" resultType="user">
select * from user;
</select>
<!--id 指定方法名,resultType指定返回类型-->
2.有入参的情况 -> 方法有形参
/**
* 根据指定id查找对应的user-+
* @param id
* @return
*/
User findUserId(Integer id);
此时的映射配置文件中 #{入参变量名}
<select id="findUserId" resultType="user">
select * from user where id = #{id};
</select>
<!--会不去指定入参类型,而是使用 #{入参变量名} 的方式来占位找值-->
十三、映射配置文件:insert
用于编写insert语句的代码
标签名:<insert></insert>
属性名 | 说明 | 是否必须 |
---|---|---|
id | 这条sql的唯一标识,和接口方法名一致 | 是 |
parameterType | 入参类型(根据类型别名写),一般也不会写,传的参数mybatis会推导 | 否 |
接口方法如下: |
/**
* 添加用户
* @param user 用户对象
* @return 受影响行数
*/
int addUser(User user);
可以看到,使用insert的时候,会返回一个受影响的行数
user类:
package com.dongmianmao.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.sql.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
定义了和数据表一致的字段
在映射配置文件中:
<!--插入数据-->
<insert id="addUser">
insert into user values(null,#{username},#{birthday},#{sex},#{address});
</insert>
只需要指定id以及插入语句使用 #{变量名} 的方式即可
十四、映射配置文件:update
用于编写update语句的代码
标签名:<update></update>
属性名 | 说明 | 是否必须 |
---|---|---|
id | 这条sql的唯一标识,和接口方法名一致 | 是 |
parameterType | 入参类型(根据类型别名写),一般也不会写,传的参数mybatis会推导 | 否 |
接口方法如下: |
/**
* 修改用户
* @param user 用户对象
* @return 受影响行数
*/
int updateUser(User user);
可以看到,使用insert的时候,会返回一个受影响的行数
user类:
package com.dongmianmao.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.sql.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
定义了和数据表一致的字段
在映射配置文件中:
<!--插入数据-->
<update id="updateUser">
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id};
</update>
只需要指定id以及插入语句使用 #{变量名} 的方式即可 ,实际使用的时候,可以结合select,先查出来指定id的对象,再set,再放进update中
十五、映射配置文件:delete
用于编写delete语句的代码
标签名:<delete></delete>
属性名 | 说明 | 是否必须 |
---|---|---|
id | 这条sql的唯一标识,和接口方法名一致 | 是 |
parameterType | 入参类型(根据类型别名写),一般也不会写,传的参数mybatis会推导 | 否 |
接口方法如下: |
/**
* 删除用户
* @param id 用户对象
* @return 受影响行数
*/
int deleteUser(Integer id);
可以看到,使用delete的时候,会返回一个受影响的行数
user类:
package com.dongmianmao.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.sql.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
定义了和数据表一致的字段
在映射配置文件中:
<!--删除数据-->
<delete id="deleteUser">
delete from user where id = #{id};
</delete>
十六、编写Mybatis工具类
通过下图,可以看到,无论Mybatis如何书写,获取SqlSession前的操作都是一样的,所以需要封装起来,来避免书写重复的东西,唯一会变的,只有SqlSession是否手动提交事务
为了不反复创建对象,消耗系统资源,采取:私有化构造器+构造方法的方式(会话工厂对象)
package com.dongmianmao.util;
import com.dongmianmao.dao.UserMapper;
import lombok.Getter;
import lombok.val;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
public class MybatisUtil {
private static SqlSessionFactory ssf = null;
private MybatisUtil() {}
// 随着类的加载只执行一次
static{
String resources = "mybatis-config.xml";
InputStream is = mybatisUtil.class.getClassLoader().getResourceAsStream(resources);
ssf = new SqlSessionFactoryBuilder().build(is);
}
/**
* * @return 工厂类创建的SqlSession
*/ public static SqlSession openSession(){
return ssf.openSession();
}
/**
* 是否手动提交事务
* @param flag 布尔值,决定返回的是否是自动提交事务
* @return
*/
public static SqlSession openSession(Boolean flag){
return ssf.openSession(flag);
}
/**
* 释放sqlSession资源的工具类
* @param sqlSession
*/
public static void closeSqlSession(SqlSession sqlSession){
if (sqlSession!=null) sqlSession.close();
}
}
十七、resultMap标签(结果集映射)
它和驼峰映射不同的是,驼峰映射只能处理简单的场景,而复杂的处理不了,这个resultMap就相当于==升级版的驼峰映射==
作用:解决查询结果字段名和实体类属性不一致不能自动封装结果,可作用在以下两个地方
1.作用在增删改查标签中的 resultMap 属性
首先创建父标签 : <resultMap></resultMap>
有以下属性
属性名 | 属性参数 | 属性作用 | 是否必须 |
---|---|---|---|
id | 用于被引用的名字 | 指定这个标签的名字 | 是 |
type | Java实体类地址 | 指定Java中的实体类的路径 | 是 |
写出来是这样的 |
<resultMap id = "名字" type = "Java实体类地址"></resultMap>
在这个标签下有两个重要的子标签,来指定Mysql中的字段
<id></id>
-> 用来指定数据表的主键字段<result></result>
-> 用来指定数据表的除了主键以外的字段
这两个标签都有共同的属性
属性名 | 属性参数 | 属性作用 | 是否必须 |
---|---|---|---|
column | Mysql中的字段名 | 用来指定Mysql中的字段名 | 是 |
property | Java中映射的变量名 | 用来指定Java中实体类中的字段的映射变量名 | 是 |
因为这个标签的意义就是为了将Mysql的字段和Java中实体类的映射名对应,而当一致不需要特殊处理的情况下,可以在父标签添加其属性来让他自动添加剩余映射字段 |
属性名 | 属性参数 | 属性作用 | 是否必须 |
---|---|---|---|
autoMapping | true/false | 将剩下没有指定不需要特殊处理的字段给自动映射 | 否 |
使用:
在使用增删改查标签的时候,在标签使用属性 resultMap 参数为 resultMap标签中所指定的id属性值
十八、Mybatis中的占位符区别
在Mybatis中存在以下两种占位符:
- #{key} -> 在映射文件中作为sql的占位符参数使用
- ${key} -> 在核心配置文件中获取外部配置文件中的数据
在映射文件中做为sql的占位符参数使用(注意细节:需要使用@Param(“参数”)声明参数名)相当于给参数设置了名字
public Brand findBrandById(@Param("id") Integer id); // 这样给参数设置名字后, 才可以${key}
两种占位符的区别,为什么 ${key} 既可以在核心配置,也可以在映射文件使用,还要区分这两个占位符?
#{}:底层使用的是ProparedStatement对象(jdbc中的pstmt),对sql进行预编译,使用占位符,来防止sql注入
KaTeX parse error: Expected 'EOF', got '#' at position 75: …行==参数传递==,使用 ==#̲{} ==防止sql注入 如果…{} ==来进行拼接
十九、多条件查询
场景:例如在浏览器中,查询东西,有多个条件搜索框,如什么什么公司,什么什么时间段之前,这个时候就需要使用多条件查询
当形参有两个以上的时候,需要使用 @Param() 来声明参数,不然Mybatis找不到参数,一般后面叫什么,前面就叫什么
方法1:把对应的查询条件放入形参中
List<Brand> findTheBrand(
@Param("brandName") String brandName,
@Param("companyName") String companyName,
@Param("status") int status
);
对应的查询语句为:
<select id="findTheBrand" resultMap="rr">
select * from tb_brand
where brand_name = #{brandName} and company_name = #{companyName} and status = #{status};
</select>
进行以上配置后,在测试的时候就可以看到,结果符合预期
总结:使用麻烦,需要给给参数加上==@Param==
方法二:在形参传递映射对象,在映射配置文件直接读映射变量
List<Brand> findTheBrand2(Brand brand);
对应查询语句同方法一一样
<select id="findTheBrand" resultMap="rr">
select * from tb_brand
where brand_name = #{brandName} and company_name = #{companyName} and status = #{status};
</select>
在测试类中只需要把需要的参数赋值给成员变量即可
@Test
public void testFindTheBrand2(){
SqlSession sqlSession = MybatisUtil.openSession();
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
Brand brand = new Brand();
brand.setBrandName("华为");
brand.setCompanyName("华为技术有限公司");
brand.setStatus(1);
List<Brand> list = mapper.findTheBrand2(brand);
for (Brand brand1 : list) {
System.out.println(brand1);
}
}
总结:只需要保证sql语句中的参数名和实体类的映射名对应上即可
方法三、在形参处传递Map,在映射配置文件中使用 #{key} 的方法拿map中的key
List<Brand> findTheBrand3(Map brandMap);
对应查询语句同方法一一样
<select id="findTheBrand" resultMap="rr">
select * from tb_brand
where brand_name = #{brandName} and company_name = #{companyName} and status = #{status};
</select>
在测试类中只需要把需要的参数以key:value的形式赋值给map即可
@Test
public void testFindTheBrand3(){
SqlSession sqlSession = MybatisUtil.openSession();
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
Map map = new HashMap();
map.put("brandName","华为");
map.put("companyName","华为技术有限公司");
map.put("status",1);
List<Brand> list = mapper.findTheBrand3(map);
for (Brand brand : list) {
System.out.println(brand);
}
}
总结:保证sql语句中的参数名和map集合的键名对应上即可
二十、动态SQL
在上面的多条件查询中,每个条件参数都是写死的,而实际情况,是不确定用户会输入哪个参数,理应是输入单个条件也能查询出来,这个时候就徐娅使用动态SQL,来把可能遇到的情况给预设好
动态SQL:会随着用户的输入或外部条件的变化而变化,称为动态SQL
1.<if>
标签,针对条件判断
根据条件来判断是否添加对应的sql语句
属性名 :
属性名 | 属性参数 | 属性作用 | 是否必须 |
---|---|---|---|
test | 条件 | if标签内容的添加条件 | 是 |
根据上面的场景,我们并不清楚用户到底传入什么参数,所以我们得保证,每一条 if 中的sql语句都需要 and ,和 like 进行模糊查询
<select id="findTheBrand3" resultMap="rr">
select * from tb_brand
where 1 = 1
<if test="brandName!=null">
and brand_name like #{brandName}
</if>
<if test="companyName!=null">
and company_name like #{companyName}
</if>
<if test="status!=null">
and status like #{status};
</if>
</select>
可以通过在where 后 , 使用 1 = 1 ,来让无参数的时候是搜索出全部
而test中的参数,则是对传入进来的参数,进行判断,不为空就添加sql语句
当三个参数全都存在的时候,可以看到sql语句是这样的
select * from tb_brand where 1 = 1 and brand_name like ? and company_name like ? and status like ?;
2.<where>
标签,替代where语句
在上面 if 标签的例子 中的 1 = 1 会很冗余,这个时候就有了 where 标签的出现
<select id="findTheBrand3" resultMap="rr">
select * from tb_brand
<where>
<if test="brandName!=null">
and brand_name like #{brandName}
</if>
<if test="companyName!=null">
and company_name like #{companyName}
</if>
<if test="status!=null">
and status like #{status};
</if>
</where>
</select>
可以看到,使用<where></where>
来替代 where 语句,标签下的 if 标签中的语句,都有and,接下来看运行结果
select * from tb_brand WHERE brand_name like ? and company_name like ? and status like ?;
可以看到,第一条 if 中的 and 消失了,可以得出,<where></where>
标签,会帮把多余的and给自动去除
3.<choose></choose>
标签,针对多条件,同switch
类似于 switch ,用于多条件选择一个,满足一个则不继续往下执行
标签:
标签名 | 标签作用 | 属性名 | 属性作用 |
---|---|---|---|
choose | 同java中的switch | / | / |
when | 同java中的case | test | 条件 |
需要包裹在<where></where> 中,当都不成立的时候,from 表名 后就什么都没有了 |
4.set标签,针对update操作中的set
可以自动去除多余的逗号,配合 if标签 使用
直接看xml中update的配置
<update id="updateOrder">
update tb_order
<set>
<if test="payment!=null">
payment = #{payment},
</if>
<if test="paymentType!=null">
payment_type = #{paymentType},
</if>
<if test="status!=null">
status = #{status}
</if>
</set>
where
id = #{id};
</update>
这样设置就可以根据传参来设置sql语句
测试类
@Test
public void testUpdateOrder(){
Order order = new Order();
order.setId(1);
order.setPayment(10000.0);
order.setStatus(1);
int i = mapper.updateOrder(order);
if(i > 0){
sqlSession.commit();
System.out.println("修改成功");
}else System.out.println("修改失败");
}
5、delete-批量删除<foreach></foreach>
标签
在sql中,删除批量元素,都是使用 in 关键字来指定多个 i=d 进行删除,如:
delete * from 表名 where id in(1,4,5); -- 表示删除1,4,5字段的内容
在mybatis中,mybatis给我们预设好了这一场景,当我们需要进行多指定行数删除记录的时候,可以使用<foreach>
标签
<foreach>
标签是用于迭代容器数据项的,需要用迭代的元素项拼接起来进行多次删除
属性名 | 属性参数 | 属性作用 | 是否必须 |
---|---|---|---|
collection | 迭代容器名 | 指定迭代容器类型(小写) | 是 |
item | 迭代项名 | 指定迭代项的名字,用于后面引用 | 是 |
open | 拼接开始字符 | 指定拼接的开始字符 | 是 |
separator | 参数间隔字符 | 指定迭代项之间的间隔字符 | 是 |
close | 拼接结束字符 | 指定拼接的结束字符 | 是 |
注意:本质上还是使用 in 的方式来一起删除,迭代的意义是根据指定格式来拼接起来sql语句 |
演示:删除tb_order表中的1,4,5数据
首先在指定的映射配置文件中配置好delete标签
<delete id="deleteOrder">
delete from tb_order where id in
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
- collection -> 指定的是传入的类型为 list
- item -> 指定迭代项的变量名为 id
- open -> 指定拼接的开始字符为 (
- separator -> 指定迭代项之间的间隔字符为 ,
- close -> 指定拼接的结束字符为 )
然后在测试类中
@Test
public void testDeleteIdOrder(){
List<Integer> list = new ArrayList<>();
Collections.addAll(list,1,4,5);
int i = mapper.deleteOrder(list);
if (i>0){
sqlSession.commit();
System.out.println("删除成功");
}
}
按演示传入的参数传递进来包含有 1,4,5 的参数的时候,sql语句是这样的
delete from tb_order where id in(1,4,5);
仔细看就可以看出,和最开始所说的sql中编写的批量删除一致,而mybatis底层则是帮我们更便捷的编写了 sql 语句
二十一、insert - 主键回填
主键回填:在向表中插入一行记录后,会自动把当前行的主键列下的数据值获取
场景:现如今有一个订单表,还有一个订单项表,当插入订单的时候,需要拿到这个自动生成的主键-订单id,来给订单项表进行插入数据的情况
需要用到 insert 中的以下两个属性搭配使用:
属性名 | 属性参数 | 属性作用 | 是否必须 |
---|---|---|---|
useGeneratedKeys | boolean | 是否开启主键回填 | 是 |
keyProperty | 主键名 | 用于获取指定主键列下的数据 | 是 |
演示:接下来将模拟以上场景
测试sql
drop table if exists tb_order ;
drop table if exists tb_order_item ;
create table tb_order
(
id int primary key auto_increment,
payment double , -- 订单总金额
payment_type int , -- 订单支付方式:1.微信支付 2.支付宝支付
status int -- 订单状态:1. 未支付 2. 已支付
);
create table tb_order_item
(
id int primary key auto_increment,
goods_name varchar(20) , -- 商品名称
goods_price double, -- 商品单价
count varchar(32), -- 购买数量
order_id int -- 订单id
);
insert into tb_order (payment, payment_type, status)
values (4887,1,2),(1975,1,1);
insert into tb_order_item (goods_name, goods_price, count, order_id)
values ('华为手机',3888,1,1),('华为pad',999,1,1)
,('小米手机',888,2,2),('小米手环',199,1,2);
mapper接口类
package com.dongmianmao.dao;
import com.dongmianmao.pojo.Order;
import com.dongmianmao.pojo.OrderItem;
public interface OrderMapper {
int addOrder(Order order);
int addOrderItem(OrderItem orderItem);
}
Order类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private Integer id;//主键
private Double payment;
private Integer paymentType;
private Integer status;
}
OrderItem类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderItem {
private Integer id;
private String goodsName;
private double goodsPrice;
private String count;
private Integer orderId;//订单id(外键)
}
OrderMaaper.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">
<!--利用namespace属性,绑定当前映射文件和Mapper接口映射关联-->
<mapper namespace="com.dongmianmao.dao.OrderMapper">
<insert id="addOrder" useGeneratedKeys="true" keyProperty="id"> <!--指定主键回填-->
insert into tb_order (payment, payment_type, status)
values (#{payment},#{paymentType},#{status});
</insert>
<insert id="addOrderItem">
insert into tb_order_item (goods_name, goods_price, count, order_id)
values (#{goodsName},#{goodsPrice},#{count},#{orderId});
</insert>
</mapper>
最后是测试类
import com.dongmianmao.dao.OrderMapper;
import com.dongmianmao.pojo.Order;
import com.dongmianmao.pojo.OrderItem;
import com.dongmianmao.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class OrderMapperTest {
@Test
public void testInsertOrder(){
// 插入订单
SqlSession sqlSession = MybatisUtil.openSession();
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
Order order = new Order();
order.setPayment(1000.0);
order.setStatus(1);
order.setPaymentType(1);
int i = mapper.addOrder(order); // 受影响行数, 主键回填:插入行的主键值填充到OrderItem中
// 插入订单项
OrderItem orderItem = new OrderItem();
System.out.println(order.getId());
orderItem.setOrderId(order.getId()); // 从Order对象中获取id
orderItem.setGoodsName("小米");
orderItem.setGoodsPrice(1000);
orderItem.setCount("10");
int i1 = mapper.addOrderItem(orderItem);
if (i > 0 && i1 > 0){
sqlSession.commit();
System.out.println("订单创建成功");
}
sqlSession.close();
}
}
效果:在测试类中,实现了两个操作,一个是添加订单表,一个是添加完订单表后拿到订单id去添加订单项
在这里面中的 OrderId 字段,是通过 order.getId() 获得的,接下来看以下加没加主键回填的getId效果
// 未添加主键回填
System.out.println(order.getId()); // null
// 添加主键回填
System.out.println(order.getId()); // 对应数据id行数
由此可以知道主键回填的真正体现,是在第一条插入操作完成后,mybatis将主键id值赋值给了对象id上,这样才可以在第二次操作添加数据项的时候拿到对应的id