MyBatis

MyBatis 是一款优秀的持久层框架,用于简化 JDBC 开发
官网:https://mybatis.org/mybatis-3/zh/index.html
持久层
  负责将数据到保存到数据库的那一层代码
  JavaEE三层架构:表现层、业务层、持久层
框架
  框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
  在框架的基础之上构建软件编写更加高效、规范、通用、可扩展

MyBatis 快速入门

查询user表中所有数据

1.创建user表,添加数据
2.创建模块,导入坐标
3.编写 MyBatis 核心配置文件 -- > 替换连接信息 解决硬编码问题
4.编写 SQL 映射文件 --> 统一管理sql语句,解决硬编码问题
5.编码
  1.定义POJO类
  2.加载核心配置文件,获取 SqlSessionFactory 对象
  3.获取 SqlSession 对象,执行 SQL 语句
  4.释放资源

 2.创建模块,导入坐标

要使用 MyBatis,只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。
如果使用 Maven 来构建项目,则需将依赖代码置于 pom.xml 文件中。
pom.xml:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6 
 7     <groupId>org.example</groupId>
 8     <artifactId>mybatis-demo</artifactId>
 9     <version>1.0-SNAPSHOT</version>
10 
11     <properties>
12         <maven.compiler.source>8</maven.compiler.source>
13         <maven.compiler.target>8</maven.compiler.target>
14     </properties>
15 
16     <dependencies>
17         <!--mybatis 依赖-->
18         <dependency>
19             <groupId>org.mybatis</groupId>
20             <artifactId>mybatis</artifactId>
21             <version>3.5.5</version>
22         </dependency>
23         <!--mysql 驱动-->
24         <dependency>
25             <groupId>mysql</groupId>
26             <artifactId>mysql-connector-java</artifactId>
27             <version>5.1.46</version>
28         </dependency>
29         <!--junit 单元测试-->
30         <dependency>
31             <groupId>junit</groupId>
32             <artifactId>junit</artifactId>
33             <version>4.13</version>
34             <scope>test</scope>
35         </dependency>
36         <!-- 添加slf4j日志api -->
37         <dependency>
38             <groupId>org.slf4j</groupId>
39             <artifactId>slf4j-api</artifactId>
40             <version>1.7.20</version>
41         </dependency>
42         <!-- 添加logback-classic依赖 -->
43         <dependency>
44             <groupId>ch.qos.logback</groupId>
45             <artifactId>logback-classic</artifactId>
46             <version>1.2.3</version>
47         </dependency>
48         <!-- 添加logback-core依赖 -->
49         <dependency>
50             <groupId>ch.qos.logback</groupId>
51             <artifactId>logback-core</artifactId>
52             <version>1.2.3</version>
53         </dependency>
54     </dependencies>
55 </project>

3.编写 MyBatis 核心配置文件 -- > 替换连接信息 解决硬编码问题

mybatis-config.xml:

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE configuration
 3         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 4         "http://mybatis.org/dtd/mybatis-3-config.dtd">
 5 <configuration>
 6 
 7     <typeAliases>
 8         <package name="com.test.pojo"/>
 9     </typeAliases>
10     
11     <!--
12     environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment
13     -->
14     <environments default="development">
15         <environment id="development">
16             <transactionManager type="JDBC"/>
17             <dataSource type="POOLED">
18                 <!--数据库连接信息-->
19                 <property name="driver" value="com.mysql.jdbc.Driver"/>
20                 <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
21                 <property name="username" value="root"/>
22                 <property name="password" value="1234"/>
23             </dataSource>
24         </environment>
25     </environments>
26     <mappers>
27         <!--加载sql映射文件-->
28        <mapper resource="UserMapper.xml"/>
29     </mappers>
30 </configuration>

4.编写 SQL 映射文件 --> 统一管理sql语句,解决硬编码问题

UserMapper.xml:

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE mapper
 3         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5 
 6 <!--
 7     namespace:名称空间
 8 -->
 9 
10 <mapper namespace="test">
11     <!--statement-->
12     <select id="selectAll" resultType="com.test.pojo.User">
13         select * from tb_user;
14     </select>
15 </mapper>

5.编码

1.定义POJO类
  User.java:

 1 public class User {
 2 
 3     private Integer id;
 4     private String username;
 5     private String password;
 6     private String gender;
 7     private String addr;
 8 
 9     public Integer getId() {
10         return id;
11     }
12     public void setId(Integer id) {
13         this.id = id;
14     }
15     ...
16     @Override
17     public String toString() {
18         ...
19     }
20 }

2.加载核心配置文件,获取 SqlSessionFactory 对象
3.获取 SqlSession 对象,执行 SQL 语句
4.释放资源
  MyBatisDemo.java:

 1 public static void main(String[] args) throws IOException {
 2     //1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
 3     String resource = "mybatis-config.xml";
 4     InputStream inputStream = Resources.getResourceAsStream(resource);
 5     SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 6 
 7     //2. 获取SqlSession对象,用它来执行sql
 8     SqlSession sqlSession = sqlSessionFactory.openSession();
 9     //3. 执行sql
10     List<User> users = sqlSession.selectList("test.selectAll");
11     System.out.println(users);
12     //4. 释放资源
13     sqlSession.close();
14 }

解决SQL映射文件的警告提示

UserMapper.xml存在警告信息:

  

产生原因:Idea和数据库没有建立连接,不识别表信息
解决方式:在Idea中配置MySQL数据库连接

Mapper 代理开发

目的:
  解决原生方式中的硬编码
  简化后期执行SQL

使用 Mapper 代理方式完成入门案例

1、定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下
  创建mybatis-demo/src/main/java/com/test/mapper/UserMapper.java
  创建mybatis-demo/src/main/resources/com/test/mapper,将UserMapper.xml放到该文件夹下。(注:resources文件夹下需要使用【com/test/mapper】方式来定义文件夹,不能使用【com.test.mapper】来定义文件夹)

2、设置SQL映射文件的namespace属性为Mapper接口全限定名
修改UserMapper.xml:

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE mapper
 3         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5 
 6 <!--
 7     namespace:名称空间
 8 -->
 9 
10 <!--<mapper namespace="test">-->
11 <mapper namespace="com.test.mapper.UserMapper">
12     <!--statement-->
13     <select id="selectAll" resultType="com.test.pojo.User">
14         select * from tb_user;
15     </select>
16 </mapper>

3、在 Mapper 接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致
UserMapper.java:

1 public interface UserMapper {
2     List<User> selectAll();
3 }

注:
1、UserMapper.xml位置变更,MyBatis核心配置文件mybatis-config.xml中加载sql映射文件路径也需修改;
2、如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化SQL映射文件的加载
  mybatis-config.xml:

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE configuration
 3         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 4         "http://mybatis.org/dtd/mybatis-3-config.dtd">
 5 <configuration>
 6 
 7     <typeAliases>
 8         <package name="com.test.pojo"/>
 9     </typeAliases>
10     
11     <!--
12     environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment
13     -->
14     <environments default="development">
15         <environment id="development">
16             <transactionManager type="JDBC"/>
17             <dataSource type="POOLED">
18                 <!--数据库连接信息-->
19                 <property name="driver" value="com.mysql.jdbc.Driver"/>
20                 <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
21                 <property name="username" value="root"/>
22                 <property name="password" value="1234"/>
23             </dataSource>
24         </environment>
25     </environments>
26     <mappers>
27         <!--加载sql映射文件-->
28         <!--<mapper resource="UserMapper.xml"/>-->
29         <!--修改1-->
30         <!--<mapper resource="com/test/mapper/UserMapper.xml"/>-->
31         <!--修改2-->
32         <!--Mapper代理方式-->
33         <package name="com.test.mapper"/>
34     </mappers>
35 </configuration>

4、编码
通过 SqlSession 的 getMapper方法获取 Mapper接口的代理对象
调用对应方法完成sql的执行
  MyBatisDemo.java:

 1 public static void main(String[] args) throws IOException {
 2     //1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
 3     String resource = "mybatis-config.xml";
 4     InputStream inputStream = Resources.getResourceAsStream(resource);
 5     SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 6 
 7     //2. 获取SqlSession对象,用它来执行sql
 8     SqlSession sqlSession = sqlSessionFactory.openSession();
 9     //3. 执行sql
10     //List<User> users = sqlSession.selectList("test.selectAll");
11     //3.1 获取UserMapper接口的代理对象
12     UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13     List<User> users = userMapper.selectAll();
14     System.out.println(users);
15     //4. 释放资源
16     sqlSession.close();
17 }

MyBatis 核心配置文件详解

MyBatis 核心配置文件的顶层结构如下:(注:配置各个标签时,需要遵守前后顺序)

类型别名(typeAliases)
在mybatis-config.xml中添加类型别名(typeAliases)

1 <typeAliases >
2     <package name="com.test.pojo"/>
3 </typeAliases>

在UserMapper.xml中写返回类型时可直接写类名(不区分大小写)
  UserMapper.xml:

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE mapper
 3         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5 
 6 <!--
 7     namespace:名称空间
 8 -->
 9 <mapper namespace="com.test.mapper.UserMapper">
10     <!--<select id="selectAll" resultType="com.test.pojo.User">-->
11     <select id="selectAll" resultType="user">
12         select * from tb_user;
13     </select>
14 </mapper>

Mybatis缓存

缓存就是内存中的数据,常常来自对数据库查询结果的保存,使用缓存可以避免频繁与数据库进行交互,从而提高查询响应速度。
MyBatis 提供了对缓存的支持,分为一级缓存和二级缓存。
  一级缓存: SqlSession级别的缓存,缓存的数据只在SqlSession内有效。
  二级缓存: mapper级别的缓存,同一个namespace公用这一个缓存,所以对SqlSession是共享的,二级缓存需要我们手动开启。

Mybatis一级缓存

1.为什么需要Mybatis一级缓存
  当我们使用Mybatis进行数据库的操作时候,会创建一个SqlSession来进行一次数据库的会话,会话结束则关闭SqlSession对象。如果我们很有可能多次查询完全相同的sql语句,每一次查询都查询一次数据库,那查询数据库代价是比较大的,这会导致系统的资源浪费。
  为了解决这个问题,Mybatis对每一次会话都添加了缓存操作,不需要相同的SQL每次都查询数据库,这就是Mybatis一级缓存的作用。
2.Mybatis一级缓存的实现
  我们知道对SqlSession的操作,mybatis内部都是通过Executor来执行的,Executor的生命周期和SqlSession是一致的,Mybatis在Executor中创建了本地缓存(一级缓存)

  如果sqlSession执行close、commit、插入、更新、删除操作,清空sqlSession中的一级缓存,保证缓存中始终保存的是最新的信息,避免脏读。(当对SqlSession执行更新操作(update、delete、insert)后并执行commit时,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果))。
注:在mybatis与spring进行整合开发时,事务控制在service中进行,重复调用两次servcie将不会走一级缓存,因为在第二次调用时session方法结束,SqlSession就关闭了。
3.Mybatis一级缓存配置
  mybatis一级缓存的范围有SESSION和STATEMENT两种,默认是SESSION。
  如果不想使用一级缓存,可以把一级缓存的范围指定为STATEMENT,这样每次执行完一个Mapper中的语句后都会将一级缓存清除.如果需要更改一级缓存的范围,可以在Mybatis的配置文件中,在下通过localCacheScope指定:

<setting name="localCacheScope" value="STATEMENT"/>

Mybatis二级缓存

1.为什么需要Mybatis二级缓存?
  MyBatis 一级缓存最大的共享范围就是一个SqlSession内部,那么如果多个 SqlSession 需要共享缓存,则需要开启二级缓存.
2.Mybatis二级缓存的实现
  开启二级缓存后,会使用 CachingExecutor 装饰 Executor,进入一级缓存的查询流程前,先在 CachingExecutor 进行二级缓存的查询。

  二级缓存开启后,一个 namespace 下的所有操作语句,都影响着同一个 Cache,即二级缓存被多 SqlSession 共享,是一个全局的变量。
  当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库
  MyBatis 是默认关闭二级缓存的,因为对于增删改操作频繁的话,那么二级缓存形同虚设,每次都会被清空缓存。

Mybatis一级缓存与二级缓存的区别

1.Mybatis的一级缓存的作用域是SQLSession,Mybatis默认开启一级缓存。
  在同一个SqlSession中,执行相同的SQL查询时,第一次会去查询数据库,并写在缓存中,第二次会直接从缓存中取。当执行SQL时候两次查询中间发生了增删改的操作commit后,SQLSession的缓存会被清空。
  每次查询会先去缓存中找,如果找不到,再去数据库查询,然后把结果写到缓存中。Mybatis的内部缓存使用一个HashMap,key为hashcode+statementld+sql语句。Value为查询出来的结果集映时成的java对象。
2.Mbatis二级缓存是默认不开启的,作用于一个Applicaton,是Mapper级别的,多个SqlSession使用同一个Mapper的sql能够使用二级缓存。

配置文件完成增删改查

MyBatisX 插件

MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
主要功能:
  XML 和 接口方法 相互跳转
  根据接口方法生成 statement
安装:点击file->settings

 实体类属性名和数据库表列名不一致导致封装数据失败问题

1、给字段起别名

1、在写sql语句时给这两个字段起别名,将别名定义成与属性名一致:

<select id="selectAll" resultType="brand">
    select id, brand_name as brandName, company_name as companyName, ordered, description, status from tb_brand;
</select>

上面的SQL语句中的字段列表书写麻烦,Mybatis提供了sql片段可以提高sql的复用性。
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" />
    from tb_brand;
</select>

2、使用resultMap定义字段和属性的映射关系

BrandMapper.xml:

<resultMap id="brandResultMap" type="brand">  <!--id:唯一标识;type:需要映射的类型 -->
    <result column="brand_name" property="brandName"/>
    <result column="company_name" property="companyName"/>
</resultMap>

<select id="selectAll" resultMap="brandResultMap">
    select * from tb_brand;
</select>

id:完成主键字段的映射
  column:表的列名
  property:实体类的属性名
    例:<id column="id" property="id1" />
result:完成一般字段的映射
  column:表的列名
  property:实体类的属性名
注意:只需定义字段名和属性名不一样的映射,一样的不需要定义。

用注解实现resultMap

BrandMapper.xml:

<resultMap id="brandResultMap" type="brand">
    <result column="brand_name" property="brandName"></result>
    <result column="company_name" property="companyName"></result>
</resultMap>

BrandMapper.java:

1 public interface BrandMapper {
2     @Select("select * from tb_brand")
3     @ResultMap("brandResultMap")
4     List<Brand> selectAll();
5 }

参数占位符

<select id="selectById"  resultMap="brandResultMap">
    select * from tb_brand where id = #{id};
</select>

两种参数占位符
  #{} :执行SQL时,会将#{}占位符替换为?,为了防止SQL注入。底层使用的是 PreparedStatement。
  ${} :拼接SQL。底层使用的是Statement,会存在SQL注入问题。
使用时机:
  参数传递的时候:#{}
  表名或者列名不固定的情况下:${}。但会存在SQL注入问题。

parameterType使用 

对于有参数的mapper接口方法,我们在映射配置文件中应该配置ParameterType来指定参数类型。只不过该属性都可以省略。

<select id="selectById" parameterType="int" resultMap="brandResultMap">
    select * from tb_brand where id = ${id};
</select>

SQL语句中特殊字段处理

映射配置文件是xml类型的问题,sql中 > < 等这些字符在xml中有特殊含义,所以需要将这些符号进行转义,可以使用以下两种方式进行转义

<select id="selectById"  resultMap="brandResultMap">
    select * from tb_brand where id < #{id};
</select>

1. 转义字符:
  &lt;  就是<的转义字符

<select id="selectById" resultMap="brandResultMap">
    select * from tb_brand where id &lt; #{id};
</select>

2. CDATA区:
  <![CDATA[内容]]>

<select id="selectById" resultMap="brandResultMap">
    select * from tb_brand where id
     <![CDATA[
        <
     ]]>
     #{id};
</select>

多条件查询

编写接口方法

Mybatis针对多参数有三种实现:
1、散装参数(使用麻烦)
使用 @Param("参数名称") 标记每一个参数,在映射配置文件中就需要使用 #{参数名称} 进行占位

List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName,@Param("brandName") String brandName);

2、实体类封装参数
将多个参数封装成一个实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和实体类属性名保持一致。

List<Brand> selectByCondition(Brand brand);

3、map集合
将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和map集合中键的名称一致。

List<Brand> selectByCondition(Map map);

编写SQL语句

BrandMapper.xml:

<select id="selectByCondition" resultMap="brandResultMap">
    select * from tb_brand
        where status = #{status}
        and company_name like #{companyName}
        and brand_name like #{brandName}
</select>

编写测试方法

MybatisTest.java:

 1 public void testSelectByCondition() throws IOException {
 2     //接收参数
 3     int status = 1;
 4     String companyName = "华为";
 5     String brandName = "华为";
 6 
 7     // 处理参数
 8     companyName = "%" + companyName + "%";
 9     brandName = "%" + brandName + "%";
10 
11     //1. 获取SqlSessionFactory
12     String resource = "mybatis-config.xml";
13     InputStream inputStream = Resources.getResourceAsStream(resource);
14     SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
15     //2. 获取SqlSession对象
16     SqlSession sqlSession = sqlSessionFactory.openSession();
17     //3. 获取Mapper接口的代理对象
18     BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
19 
20     //4. 执行方法
21     //方式一 :接口方法参数使用 @Param 方式调用的方法
22     //List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName);
23     //方式二 :接口方法参数是 实体类对象 方式调用的方法
24      //封装对象
25     /* Brand brand = new Brand();
26         brand.setStatus(status);
27         brand.setCompanyName(companyName);
28         brand.setBrandName(brandName);*/    
29     //List<Brand> brands = brandMapper.selectByCondition(brand);
30     
31     //方式三 :接口方法参数是 map集合对象 方式调用的方法
32     Map map = new HashMap();
33     map.put("status" , status);
34     map.put("companyName", companyName);
35     map.put("brandName" , brandName);
36     List<Brand> brands = brandMapper.selectByCondition(map);
37     System.out.println(brands);
38 
39     //5. 释放资源
40     sqlSession.close();
41 }

动态SQL

SQL语句会随着用户的输入或外部条件的变化而变化
MyBatis 对动态SQL有很强大的支撑:
  if
  choose (when, otherwise)
  trim (where, set)
  foreach

if 标签

用于判断参数是否有值,使用test属性进行条件判断(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>

存在的问题:第一个参数不输入,sql语句会变为 select * from tb_brand where and company_name like ? and brand_name like ?
解决方案:
1、使用恒等式让所有条件格式都一样

<select id="selectByCondition" resultMap="brandResultMap">
    select * from tb_brand where 1 = 1
        <if test="status != null">
            and 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>

2、<where> 标签
作用:
  替换where关键字
  会动态的去掉第一个条件前的 and
  如果所有的参数没有值则不加where关键字

<select id="selectByCondition" resultMap="brandResultMap">
    select * from tb_brand
    <where>
        <if test="status != null">
            and 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>

单条件查询

从多个条件中选择一个
choose (when, otherwise):选择,类似于Java中的switch语句

<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><!--相当于default-->
            1 = 1
        </otherwise>
    </choose>
</select>

或结合<where>标签

<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>

添加

编写接口方法

void add(Brand brand);

编写SQL语句

BrandMapper.xml:

<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>

编写测试方法

MyBatis事务:
openSession():默认开启事务,进行增删改操作后需要使用 sqlSession.commit(),手动提交事务。
openSession(true):可以设置为自动提交事务
MybatisTest.java:

 1 public void testAdd() throws IOException {
 2     //接收参数
 3     int status = 1;
 4     String companyName = "手机";
 5     String brandName = "手机";
 6     String description = "手机";
 7     int ordered = 100;
 8 
 9     //封装对象
10     Brand brand = new Brand();
11     brand.setStatus(status);
12     brand.setCompanyName(companyName);
13     brand.setBrandName(brandName);
14     brand.setDescription(description);
15     brand.setOrdered(ordered);
16 
17     //1. 获取SqlSessionFactory
18     String resource = "mybatis-config.xml";
19     InputStream inputStream = Resources.getResourceAsStream(resource);
20     SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
21     //2. 获取SqlSession对象
22     SqlSession sqlSession = sqlSessionFactory.openSession();
23     //SqlSession sqlSession = sqlSessionFactory.openSession(true); //设置自动提交事务,这种情况不需要手动提交事务了
24     //3. 获取Mapper接口的代理对象
25     BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
26     //4. 执行方法
27     brandMapper.add(brand);
28     //提交事务
29     sqlSession.commit();
30     //5. 释放资源
31     sqlSession.close();
32 }

添加–主键返回

在数据添加成功后,需要获取插入数据库数据的主键
比如:添加订单和订单项,订单项中需要设置所属订单的id
  在insert标签上添加如下属性:
    useGeneratedKeys:获取自动增长的主键值。true表示获取。
    keyProperty:指定将获取到的主键值封装到哪儿个属性里
BrandMapper.xml:

<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>

批量插入

方式一 循环插入

mapper.xml

<insert id="insert" parameterType="Student">
    INSERT INTO tb_student (name, age, phone, address, class_id) VALUES (#{name},#{age},#{phone},#{address},#{classId})
</insert>

mapper接口:

1 public interface StudentMapper{
2     int insert(Student student);
3 }

测试代码:

 1 @SpringBootTest
 2 class DemoApplicationTests {
 3     @Resource
 4     private StudentMapper studentMapper;
 5 
 6     @Test
 7     public void testInsert(){
 8         //数据生成
 9         List<Student> studentList = createData(100);
10         //循环插入
11         long start = System.currentTimeMillis();
12         studentList.stream().forEach(student -> studentMapper.insert(student));
13         System.out.println(System.currentTimeMillis() - start);
14     }
15     private List<Student> createData(int size){
16         List<Student> studentList = new ArrayList<>();
17         Student student;
18         for(int i = 0; i < size; i++){
19             student = new Student();
20             student.setName("小王" + i);
21             student.setAge(18);
22             student.setClassId(1);
23             student.setPhone("1585xxxx669");
24             student.setAddress("未知");
25             studentList.add(student);
26         }
27         return studentList;
28     }
29 }

方式二 foreach标签

mapper.xml

<insert id="insertBatch" >
    INSERT INTO tb_student (name, age, phone, address, class_id) VALUES
    <foreach collection="list" separator="," item="item">
        (#{item.name},#{item.age},#{item.phone},#{item.address},#{item.classId})
    </foreach>
</insert>

mapper接口:

1 public interface StudentMapper{
2     int insertBatch(List<Student> studentList);
3 }

测试代码:

 1 @SpringBootTest
 2 class DemoApplicationTests {
 3     @Resource
 4     private StudentMapper studentMapper;
 5 
 6     @Test
 7     public void testInsert(){
 8         //数据生成
 9         List<Student> studentList = createData(100);
10         //使用foreach标签拼接sql插入
11         long start = System.currentTimeMillis();
12         studentMapper.insertBatch(studentList);
13         System.out.println(System.currentTimeMillis() - start);
14     }
15     private List<Student> createData(int size){
16         List<Student> studentList = new ArrayList<>();
17         Student student;
18         for(int i = 0; i < size; i++){
19             student = new Student();
20             student.setName("小王" + i);
21             student.setAge(18);
22             student.setClassId(1);
23             student.setPhone("1585xxxx669");
24             student.setAddress("未知");
25             studentList.add(student);
26         }
27         return studentList;
28     }
29 }

方式三 批处理

mapper.xml

<insert id="insert" parameterType="Student">
    INSERT INTO tb_student (name, age, phone, address, class_id) VALUES (#{name},#{age},#{phone},#{address},#{classId})
</insert>

mapper接口:

1 public interface StudentMapper{
2     int insert(Student student);
3 }

测试代码:

 1 @SpringBootTest
 2 class DemoApplicationTests {
 3     @Autowired
 4     private SqlSessionFactory sqlSessionFactory;
 5 
 6     @Test
 7     public void testInsertBatch(){
 8         //数据生成
 9         List<Student> studentList = createData(100);
10         //使用批处理
11         long start = System.currentTimeMillis();
12         SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
13         StudentMapper studentMapperNew = sqlSession.getMapper(StudentMapper.class);
14         //studentList.stream().forEach(student -> studentMapperNew.insert(student));  //实现一
15         for (Student student : studentList) {  //实现二
16             studentMapperNew.insert(student);
17         }
18         sqlSession.commit();
19         sqlSession.clearCache();
20         System.out.println(System.currentTimeMillis() - start);
21     }
22     private List<Student> createData(int size){
23         List<Student> studentList = new ArrayList<>();
24         Student student;
25         for(int i = 0; i < size; i++){
26             student = new Student();
27             student.setName("小王" + i);
28             student.setAge(18);
29             student.setClassId(1);
30             student.setPhone("1585xxxx669");
31             student.setAddress("未知");
32             studentList.add(student);
33         }
34         return studentList;
35     }
36 }

三种方式中,批处理的方式效率是最高的,其次是foreach标签,最后是循环插入的方式。

修改

修改全部字段

BrandMapper.xml:

<update id="update">
    update tb_brand set 
        brand_name = #{brandName},
        company_name = #{companyName},
        ordered = #{ordered},
        description = #{description},
        status = #{status}
    where id = #{id};
</update>

修改部分字段

BrandMapper.xml:

<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>

删除

删除一个

BrandMapper.xml:

<delete id="deleteById">
    delete from tb_brand where id = #{id};
</delete>

批量删除

编写SQL时需要遍历数组来拼接SQL语句。Mybatis提供了foreach标签使用

foreach标签

用来迭代任何可迭代的对象(如数组,集合)。
collection 属性:
  mybatis会将数组参数,封装为一个Map集合。
    默认:1.array = 数组
       2.使用@Param注解改变map集合的默认key的名称
item 属性:本次迭代获取到的元素。
separator 属性:集合项迭代之间的分隔符。foreach标签不会错误地添加多余的分隔符。也就是最后一次迭代不会加分隔符。
open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次
close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次

1.使用array

UserMapper.java:

void deleteByIds(int[] ids);

BrandMapper.xml:

<delete id="deleteByIds">
    delete from tb_brand where id
    in
    <foreach collection="array" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
    ;
</delete>

2.使用@Param注解

UserMapper.java:

void deleteByIds(@Param("ids") int[] ids);

BrandMapper.xml:

<delete id="deleteByIds">
    delete from tb_brand where id 
    in
    <foreach collection="ids" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
    ;
</delete>

Mybatis参数传递

Mybatis 接口方法中可以接收各种各样的参数,如下:
  多个参数
  单个参数:
    POJO 类型
    Map 集合类型
    Collection 集合类型
    List 集合类型
    Array 类型
    其他类型

多个参数

我们在接口方法中定义多个参数,Mybatis 会将这些参数封装成 Map 集合对象,值就是参数值,而键在没有使用 @Param 注解时有以下命名规则:
  以 arg 开头 :第一个参数就叫 arg0,第二个参数就叫 arg1,以此类推。如:
    map.put("arg0",参数值1);
    map.put("arg1",参数值2);
  以 param 开头 : 第一个参数就叫 param1,第二个参数就叫 param2,依次类推。如:
    map.put("param1",参数值1);
    map.put("param2",参数值2);
在接口方法参数上使用 @Param 注解,Mybatis 会将 arg 开头的键名替换为对应注解的属性值。

User select(@Param("username") String username,@Param("password") String password);
<select id="select" resultType="user">
    select * from tb_user where 
        username=#{username}
        and password=#{password}
</select>

结论:以后接口参数是多个时,在每个参数上都使用 @Param 注解。这样代码的可读性更高。

单个参数

POJO 类型
  直接使用。要求属性名和参数占位符名称一致
Map 集合类型
  直接使用。要求map集合的键名和参数占位符名称一致
Collection 集合类型
  Mybatis 会将集合封装到 map 集合中,如下:
    map.put("arg0",collection集合);
    map.put("collection",collection集合;
  可以使用 @Param 注解替换map集合中默认的 arg 键名。
List 集合类型
  Mybatis 会将集合封装到 map 集合中,如下:
    map.put("arg0",list集合);
    map.put("collection",list集合);
    map.put("list",list集合);
  可以使用 @Param 注解替换map集合中默认的 arg 键名。
Array 类型
  Mybatis 会将集合封装到 map 集合中,如下:
    map.put("arg0",数组);
    map.put("array",数组);
  可以使用 @Param 注解替换map集合中默认的 arg 键名。
其他类型
  比如int类型,参数占位符名称叫什么都可以。尽量做到见名知意.

注解实现CRUD

UserMapper.java

@Select(value = "select * from tb_user where id = #{id}")
public User select(int id);

Mybatis 针对 CURD 操作都提供了对应的注解,已经做到见名知意。如下:
  查询 :@Select
  添加 :@Insert
  修改 :@Update
  删除 :@Delete

注:注解完成简单功能,配置文件完成复杂功能。

posted @ 2023-07-04 12:54  溯鸣  阅读(18)  评论(0编辑  收藏  举报