一篇文章搞定MyBatis
MyBatis
一、概述
二、简介
2.1概念
MyBatis 是一款优秀的持久层框架,用于简化 JDBC 开发。MyBatis 本是 Apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis。2013 年 11 月迁移到 Github。官网:https://mybatis.org/mybatis-3/zh/index.html(持久层是负责将数据到保存到数据库的那一层代码;JavaEE 三层架构:表现层、业务层、持久层)
2.2MyBatis的改进点
三、快速入门
3.1整体步骤
查询 user 表中所有数据
- 创建 user 表,添加数据
- 创建模块,导入坐标
- 编写 MyBatis 核心配置文件 --> 替换连接信息 解决硬编码问题
- 编写 SQL 映射文件 --> 统一管理 sql 语句,解决硬编码问题
- 编码
- 定义 POJO 类
- 加载核心配置文件,获取 SqlSessionFactory 对象
- 获取 SqlSession 对象,执行 SQL 语句
- 释放资源
<!--连接池信息-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///db1?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
<select id="selectAll" resultType="com.itheima.pojo.User">
select * from tb_user;
</select>
下面主要是讲第5.2到5.4步。
3.2实验步骤讲解
1、核心配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.itheima.pojo"/>
</typeAliases>
<!--
environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment
-->
<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://localhost:3306/testmybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库连接信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/testmybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载sql映射文件-->
<mapper resource="com/itheima/mapper/UserMapper.xml"/>
</mappers>
</configuration>
注意这一段是加载映射文件的:
<mappers>
<!--加载sql映射文件-->
<mapper resource="com/itheima/mapper/UserMapper.xml"/>
</mappers>
2、加载核心配置文件,获取 SqlSessionFactory 对象;获取 SqlSession 对象,执行 SQL 语句;释放资源。
package com.itheima;
import com.itheima.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyBatisDemoM {
public static void main(String[] args) throws IOException {
// 获取SqlSessionFactory:这段代码直接从官网获取
// 下面加载的是核心配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 使用sqlsession来执行sql语句,传入映射文件的方法时,需要加入命名空间,以便后面有多个selectAll好区分
List<User> userList= sqlSession.selectList("com.itheima.mapper.UserMapper.selectAll");
System.out.println(userList);
// 释放资源
sqlSession.close();
}
}
结果:
四、代理开发
4.1整体步骤
定义与 SQL 映射文件同名的 Mapper 接口,并且将 Mapper 接口和 SQL 映射文件放置在同一目录下
设置 SQL 映射文件的namespace属性为 Mapper 接口全限定名
在 Mapper 接口中定义方法,方法名就是 SQL 映射文件中 sql 语句的id,并保持参数类型和返回值类型一致
编码
- 通过
SqlSession的getMapper方法获取 Mapper 接口的代理对象 - 调用对应方法完成 sql 的执行
细节:如果 Mapper 接口名称和 SQL 映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化 SQL 映射文件的加载
<mappers>
<!--加载sql的映射文件-->
<!--<mapper resource="com/itheima/mapper2/UserMapper.xml"/>-->
<package name="com.itheima.mapper"/>
</mappers>
4.2实验
1、定义与 SQL 映射文件同名的 Mapper 接口,并且将 Mapper 接口和 SQL 映射文件放置在同一目录下
注意在resources目录下要新建多级目录的时候使用的是“/”,而不是“.”,如果使用"com.itheima.mapper"系统会直接将"com.itheima.mapper"当成一个目录创建,而不是多级目录,正确的方式如下:
只有这样才能达到Mapper 接口和 SQL 映射文件放置在同一目录下的效果:
虽然 创建成功后显示的是这样的:
但是已经达到了Mapper 接口和 SQL 映射文件放置在同一目录下的效果。
2、设置 SQL 映射文件的namespace属性为 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.itheima.mapper.UserMapper">
<!--
namespace:命名空间
-->
<select id="selectAll" resultType="user">
select *
from tb_user;
</select>
</mapper>
3、在 Mapper 接口中定义方法,方法名就是 SQL 映射文件中 sql 语句的id,并保持参数类型和返回值类型一致:
package com.itheima.mapper;
import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public interface UserMapper {
List<User> selectAll();
}
值得注意的是xml映射文件中的selectAll的返回值是User,但是在Mapper中我们要依据语义来确定是使用List接收还是使用单个User接收,因为即使Mapper层的返回值类型是List,但是xml映射文件只与List的泛型,即User对齐即可。
3、编码:通过 SqlSession 的 getMapper方法获取 Mapper 接口的代理对象;调用对应方法完成 sql 的执行
package com.itheima.pojo;
import com.itheima.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyBatisDemoM2 {
public static void main(String[] args) throws IOException {
// 获取SqlSessionFactory:这段代码直接从官网获取
// 下面加载的是核心配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过sqlSession来获取Mapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 然后使用Mapper执行相关的方法即可
List<User> userList =userMapper.selectAll();
System.out.println(userList);
sqlSession.close();
}
}
4、使用包扫描的方式简化 SQL 映射文件的加载
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.itheima.pojo"/>
</typeAliases>
<!--
environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment
-->
<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://localhost:3306/testmybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库连接信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/testmybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<mappers>
<!--Mapper代理方式-->
<package name="com.itheima.mapper"/>
</mappers>
</configuration>
改的是这一段:
<mappers>
<!--Mapper代理方式-->
<package name="com.itheima.mapper"/>
</mappers>
五、Mybatis核心配置文件
上面是Mybatis核心配置文件可以配置的一些东西(从官网截取),需要注意的是配置时需要严格遵循上面的顺序,否则会报错,下面主要是抽取几个重要的来进行介绍,不再逐一介绍了。
1、环境
像环境的话是可以设置多个环境的,通过default属性切换不同的environment:
<!--
environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment
-->
<environments default="development">
<environment id="development">
<!-- 事务不需要mybatis来接管,将来Spring会接管,包括数据源-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库连接信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/testmybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="guangxun.kg"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库连接信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/testmybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="guangxun.kg"/>
</dataSource>
</environment>
</environments>
2、别名
通过来起别名,在引用时不区分实体类的大小写了,也不需要再写包名了。
<!--相当于给pojo这个包下面的所有实体类都取了别名,在引用时不区分实体类的大小写了,也不需要再写包名了-->
<typeAliases>
<package name="com.itheima.pojo"/>
</typeAliases>
起了别名后映射文件就可以这样写了:
<!--statement-->
<select id="selectAll" resultType="user">
select *
from tb_user;
</select>
六、增删改查功能
6.1实验准备——安装Mybatisx插件
Mybatisx插件可以将映射文件中的sql语句(statement)与mapper接口相对应,sql语句是红色的小鸟对应的,mapper接口是蓝色的小鸟对应的,点击小鸟会相互跳转。同时可以生成mapper对应的sql语句标签和返回值类型。(sql语句要自己写)
6.2实验
6.2.1查询所有
1、mapper:
List<Brand> selectAll();
2、xml映射文件:
<select id="selectAll" resultType="brand">
select *
from tb_brand;
</select>/
3、测试
package com.itheima.test;
import com.itheima.mapper.BrandMapper;
import com.itheima.pojo.Brand;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyBatisTest2 {
public static void main(String[] args) throws IOException {
// 获取sqlsessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取sqlsession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 获取mapper
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
// 使用执行相关方法
List<Brand>brandList = brandMapper.selectAll();
System.out.println(brandList);
sqlSession.close();
}
}
重要的是分析业务需要什么参数,需要返回什么结果。
但是我们可以看到有的字段没有封装上显示是空值,这是因为实体类属性的名称与数据库表的字段名称不一致:
解决方法如下:
1、方法一:我们可以使用as在映射文件中为数据库表的字段名取一个别名,让别名和实体类的属性名一样:
<select id="selectAll" resultType="brand">
select id, brand_name as brandName, company_name as companyName, ordered, description, status
from tb_brand;
</select>
缺点:每次查询都要定义一次别名。
2、方法二:定义sql片段,然后直接引用:
<sql id="brand_column">
id, brand_name as brandName, company_name as companyName, ordered, description, status
</sql>
<select id="selectAll" resultType="brand">
select
<include refid="brand_column"></include>
from tb_brand;
</select>
缺点:不灵活。
3、使用resultMap定义字段与实体类属性的映射:
- 定义标签2. 在标签中;
- 使用resultMap属性替换 resultType属性。
<!--
id:唯一标识
type:映射的类型,支持别名
-->
<resultMap id="brandResultMap" type="brand">
<!--
id:完成主键字段的映射
column:表的列名
property:实体类的属性名
result:完成一般字段的映射
column:表的列名
property:实体类的属性名
-->
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
<select id="selectAll" resultMap="brandResultMap">
select * from tb_brand;
</select>
实际上如果使用Mybatis做持久层框架的话,sql数据表字段是下划线命名,实体类属性是驼峰命名的话,可以在application.properties配置文件中开启驼峰命名自动映射,这样就可以不用再配置别名之类的了,只需要在数据表的字段中严格按照下划线命名,实体类中属性按照驼峰命名即可:
mybatis.configuration.map-underscore-to-camel-case=true
要是.yml配置文件的话可以这样:
mybatis:
configuration:
map-underscore-to-camel-case: true
6.2.2查看详情
1、分析思路:
2、实现:
mapper:
Brand selectById(int id);
映射文件:
<select id="selectById" resultMap="brandResultMap">
select *
from tb_brand where id = #{id};
</select>
3、说明:
a.#{id}中的参数名与mapper对应。
mybatis中参数占位符有两种:#{}和${}。
#{}:会将其替换为 ?,为了防止SQL注入
${}:拼sql。会存在SQL注入问题
使用时机:
- 参数传递的时候:#{}
- 表名或者列名不固定的情况下:${} 会存在SQL注入问题,表名或者列名不固定:
<select id="selectById" resultMap="brandResultMap">
select *
from ${tableNmae} where id = #{id};
</select>
b.参数类型:parameterType:可以省略,直接依据mapper中的即可,要写的话是这样:
<select id="selectById" parameterType="int" resultMap="brandResultMap">
select *
from tb_brand where id = #{id};
</select>
c.特殊字符处理(以"<"符号为例):
转义字符(字符少的时候用):
<select id="selectById" resultMap="brandResultMap">
-- select *
-- from tb_brand where id = #{id};
select *
from tb_brand where id < #{id};
</select>
CDATA区(输入CD会自动补齐,且在字符多的时候用):
select *
from tb_brand
where id
<![CDATA[
<
]]>
#{id};
6.2.3条件查询
1、分析:
像公司名称这些文字性的描述就使用就使用”like“模糊查询,像id,status状态码这些就使用”=查询“即可。
2、实现
a.情形一:散装参数:如果方法中有多个参数,需要使用@Param(“SQL参数占位符名称”)
mapper:
List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName);
散装参数:如果方法中有多个参数,需要使用@Param(“SQL参数占位符名称”),也就是说@Param中的参数名要与映射文件中的sql语句的#{}参数占位符一致。
映射文件:
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName};
</select>
测试(测试时就加上几个,参数且处理一下,其余不变):
//接收参数
int status = 1;
String companyName = "华为";
String brandName = "华为";
// 处理参数
companyName = "%"+companyName+"%";
brandName = "%"+brandName+"%";
b.情形二:对象参数:对象的属性名称要和参数占位符名称一致
mapper写好对应的方法:
List<Brand> selectByCondition(Brand brand);
映射文件不需要改动,只需要将原来散装的测试封装为对象传递即可:
@Test
public void testSelectByCondition() throws IOException {
//接收参数
int status = 1;
String companyName = "华为";
String brandName = "华为";
// 处理参数
companyName = "%"+companyName+"%";
brandName = "%"+brandName+"%";
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
List<Brand> brands = brandMapper.selectByCondition(brand);
System.out.println(brands);
//5. 释放资源
sqlSession.close();
}
c.情形三:使用map封装
使用maper封装,map的key值要与sql语句中的参数占位符保持一致,xml映射文件依然不用变:
mapper:
List<Brand> selectByCondition(Map map);
测试:
@Test
public void testSelectByCondition() throws IOException {
//接收参数
int status = 1;
String companyName = "华为";
String brandName = "华为";
// 处理参数
companyName = "%"+companyName+"%";
brandName = "%"+brandName+"%";
//封装map
Map map = new HashMap();
map.put("status" , status);
map.put("companyName", companyName);
map.put("brandName" , brandName);
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
List<Brand> brands = brandMapper.selectByCondition(map);
System.out.println(brands);
//5. 释放资源
sqlSession.close();
}
6.2.3多条件动态查询
1.
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName};
</select>
我们要是这样写的话会存在一定的弊端,比如说用户想要只输入一个条件,比如不输入companyName,来查询所有名为companyName的公司的信息,是无法实现的,因为其他值不输入,sql语句中的值就会赋值为空,而数据表中又没有这样的记录。
不输入brandName:
Map map = new HashMap();
map.put("status" , status);
map.put("companyName", companyName);
//map.put("brandName" , brandName);
所以这时候就需要进行动态判断,依据用户的输入或外部条件的变化而变化,if:用于判断参数是否有值,使用test属性进行条件判断:
<select id="selectByCondition" resultMap="brandResultMap">
select * from tb_brand
where
<if test="status != null">
status = #{status}
</if>
<if test="companyName != null and companyName != ''">
and company_name like #{companyName}
</if>
<if test="brandName != null and brandName != ''">
and brand_name like #{brandName}
</if>
</select>
注意第一个里面不需要加and,然后的test属性写的是判断条件,且引用的是实体类的属性值。这样就可以实现动态查询了:
Map map = new HashMap();
//map.put("status" , status);
//map.put("companyName", companyName);
map.put("brandName" , brandName);
我们可以有两种解决方式:
1、在where后面加入恒等式:
<select id="selectByCondition" resultMap="brandResultMap">
select * from tb_brand
where 1=1
<if test="status != null">
status = #{status}
</if>
<if test="companyName != null and companyName != ''">
and company_name like #{companyName}
</if>
<if test="brandName != null and brandName != ''">
and brand_name like #{brandName}
</if>
</select>
2、使用标签自动识别是否只有一个条件,自动去掉多余的and:
<select id="selectByCondition" resultMap="brandResultMap">
select * from tb_brand
<where>
<if test="status != null">
status = #{status}
</if>
<if test="companyName != null and companyName != ''">
and company_name like #{companyName}
</if>
<if test="brandName != null and brandName != ''">
and brand_name like #{brandName}
</if>
</where>
</select>
6.2.4单条件查询
mapper:
List<Brand> selectByConditionSingle(Brand brand);
映射文件:
<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-->
company_name like #{companyName}
</when>
<when test="brandName != null and brandName != ''"><!--相当于case-->
brand_name like #{brandName}
</when>
</choose>
</select>
这样就可以实现单条件查询了:
但是这种方法有bug,如果用户一个条件不输入就会报错:
解决方法:
1、在中加恒等式:
<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-->
company_name like #{companyName}
</when>
<when test="brandName != null and brandName != ''"><!--相当于case-->
brand_name like #{brandName}
</when>
<otherwise>
1 = 1
</otherwise>
</choose>
</select>
2、使用标签
<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-->
company_name like #{companyName}
</when>
<when test="brandName != null and brandName != ''"><!--相当于case-->
brand_name like #{brandName}
</when>
</choose>
</where>
</select>
6.2.5.1普通添加:
1、mapper:
void add(Brand brand);
2、xml映射文件
<insert id="add">
insert into tb_brand (brand_name, company_name, ordered, description, status)
values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
</insert>
3、测试
@Test
public void testAdd() throws IOException {
//接收参数
int status = 1;
String companyName = "波导手机";
String brandName = "波导";
String description = "手机中的战斗机";
int ordered = 100;
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setDescription(description);
brand.setOrdered(ordered);
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
brandMapper.add(brand);
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
注意使用mybatis进行插入需要 sqlSession.commit();提交事务,否则会插入不成功。openSession()默认为开启事务(不会自动提交),需要使用 sqlSession.commit();来提交事务。当然也可以使用SqlSession sqlSession = sqlSessionFactory.openSession(true);进行自动提交事务。
6.2.5.2主键返回
上面会存在一个问题,就是插入后不会返回id,要是我们的订单需要插入订单项时,得不到订单号的话,那么就无法插入订单项就会出问题:
@Test
public void testAdd2() throws IOException {
//接收参数
int status = 1;
String companyName = "hh手机";
String brandName = "hh";
String description = "手机中的战斗机";
int ordered = 100;
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setDescription(description);
brand.setOrdered(ordered);
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
// SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
brandMapper.add(brand);
Integer id = brand.getId();
System.out.println(id);
//提交事务
// sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
这时id返回的是null,要实现注解返回的话我们只需要在xml映射文件中加上两属性即可,useGeneratedKeys设置为true,keyProperty设置为我们要返回的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>
6.2.6.1修改手全部字段
1、mapper
int update(Brand brand);
2、xml映射文件
<update id="update">
update tb_brand
set brand_name = #{brandName},
company_name = #{companyName},
ordered = #{ordered},
description = #{description},
status = #{status}
where id = #{id};
</update>
3、测试
@Test
public void testUpdate() throws IOException {
//接收参数
int status = 0;
String companyName = "波导手机";
String brandName = "波导";
String description = "波导手机,手机中的战斗机";
int ordered = 200;
int id = 6;
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setDescription(description);
brand.setOrdered(ordered);
brand.setId(id);
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
int count = brandMapper.update(brand);
System.out.println(count);
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
但是这种方式存在一定问题,但用户只想修改一部分值,其他值不变,用户没有赋值时,字段就会赋空值,就不符合要求了:
所以就需要使用到动态修改了。
6.2.6.2修改动态字段
主要是使用到标签判空,然后使用就是在字段只有一个修改时且该字段不是最后一个字段时,标签会帮我们处理最后一个多出来的",",或者说没有一个字段是需要修改时,标签也会帮我们灵活处理。
<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>
</set>
where id = #{id};
</update>
6.2.7.1删除单个
1、mapper
void deleteById(int id);
2、xml映射文件
<delete id="deleteById">
delete from tb_brand where id = #{id};
</delete>
3、测试
@Test
public void testDeleteById() throws IOException {
//接收参数
int id = 6;
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
brandMapper.deleteById(id);
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
6.2.7.2批量删除(这里以数组的形式传递参数)
1、mapper
void deleteByIds(int[] ids);
2、xml映射文件
<delete id="deleteByIds">
delete from tb_brand where id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
mybatis会将数组参数,封装为一个Map集合。默认:key为array, value为我们的数组,所以在这里要指定参数名array。
或者也可以使用@Param注解在Mapper层中指定名称:
void deleteByIds(@Param("ids") int[] ids);
这样xml中就可以直接使用"ids"这个名称了:
<delete id="deleteByIds">
delete from tb_brand where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
collection是指定数组名称,item指定遍历出来的单个字段,separator是分隔符,open是左闭合,close是右闭合。
3、测试
@Test
public void testDeleteByIds() throws IOException {
//接收参数
int[] ids = {2,3};
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
brandMapper.deleteByIds(ids);
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
6.2.8参数传递
6.2.8.1多个参数传递
多个参数:封装为Map集合,默认的参数名如下:
map.put(“arg0”,参数值1)
map.put(“param1”,参数值1)
map.put(“param2”,参数值2)
map.put(“agr1”,参数值2)
当然可以使用@Param注解,替换Map集合中默认的arg键名:
mapper:
User select(@Param("username") String username,@Param("password") String password);
xml:
<select id="select" resultType="user">
select *
from tb_user
where
username = #{username}
and password = #{password}
</select>
也可以反过来验证,要是没有@Param:
User select(String username,String password);
也验证了什么参数的默认命名。也就是说下面这两种写法都可以:
<select id="select" resultType="user">
select *
from tb_user
where
username = #{arg0}
and password = #{arg1}
</select>
但是一般使用@Param来在mapper中指定名字,其逻辑是通过用指定的命名来替换对应的arg:
mapper:
User select(@Param("username") String username,String password);
xml:
<select id="select" resultType="user">
select *
from tb_user
where
username = #{arg0}
and password = #{param2}
</select>
可以看到arg0被username替换掉了。当然也可以debug来验证它们的默认命名:
6.2.8.2单个参数
1、结论:
- 单个参数:
1. POJO类型:直接使用,属性名 和 参数占位符名称 一致
2. Map集合:直接使用,键名 和 参数占位符名称 一致
3. Collection:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
map.put(“arg0”,collection集合);
map.put(“collection”,collection集合);
4. List:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
map.put(“arg0”,list集合);
map.put(“collection”,list集合);
map.put(“list”,list集合);
5. Array:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
map.put(“arg0”,数组);
map.put(“array”,数组);
6. 其他类型:直接使用
2、验证:
可以看到Collection是默认arg0、collection两个默认命名,List和Array则就可以通过代码看出。
虽然有很多默认命名,但是在开发中我们使用@Param来进行映射会使得代码更具有可读性。
七、注解开发
以select为例,其他的类似:
@Select("select * from tb_user where id = #{id}")
User selectById(int id);
直接在mapper层写就好了,简单的用注解,复杂的用xml映射文件。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)