【MybBatis应用篇】MyBatis 快速入门和重点详解

Mybatis简介

MyBatis 是一个优秀的基于 Java 的持久层框架,它内部封装了 JDBC,使开发者只需关注 SQL 语句本身,而不用再花费精力去处理诸如注册驱动、创建 Connection、配置 Statement 等繁杂过程。

Mybatis 通过 xml 或注解的方式将要执行的各种 Statement(Statement、PreparedStatement 等)配置起来,并通过 Java 对象和 Statement 中 SQL 的动态参数进行映射生成最终执行的 SQL 语句,最后由 MyBatis 框架执行 SQL 并将结果映射成 Java 对象并返回

在这里插入图片描述

MyBatis 与 Hibernate

Hibernate 框架是提供了全面的数据库封装机制的 “全自动” ORM,即实现了 POJO 和数据库表之间的映射,以及 SQL 的自动生成和执行。

相对于此,MyBatis 只能算作是 半自动 ORM。其着力点,是在 POJO 类与 SQL 语句之间的映射关系。也就是说,MyBatis 并不会为程序员自动生成 SQL 语句。具体的 SQL 需要程序员自己编写,然后通过 SQL 语句映射文件,将 SQL 所需的参数,以及返回的结果字段映射到指定 POJO。因此,MyBatis 成为了 “全自动” ORM 的一种有益补充。

MyBatis 的特点

  • 在 XML 文件中配置 SQL 语句,实现了 SQL 语句与代码的分离,给程序的维护带来了很大便利。
  • 因为需要程序员自己去编写 SQL 语句,程序员可以结合数据库自身的特点灵活控制 SQL 语句,因此能够实现比 Hibernate 等全自动 ORM 框架更高的查询效率,能够完成复杂查询。
  • 简单,易于学习,易于使用,上手快。
    在这里插入图片描述

测试MyBatis 对象关系映射

POM

编写完相关代码后,我们可以使用单元测试查看 MyBatis 的执行效果,需要增加单元测试相关依赖,配置如下:

<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis</artifactId>
   <version>3.5.7</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.27</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.18</version>
</dependency>

jdbc.properties

数据库配置

jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/ry?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true

jdbc.driver=com.mysql.cj.jdbc.Driver

mybatis-config.xml

全局配置文件

<?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>
    <!-- 加载类路径下数据源配置信息   -->
    <properties resource="jdbc.properties"/>
    <!-- 全局参数 -->
    <settings>
        <!-- 打印 SQL 语句 -->
        <setting name="logImpl" value="STDOUT_LOGGING" />

        <!-- 使全局的映射器启用或禁用缓存。 -->
        <setting name="cacheEnabled" value="false"/>

        <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 -->
        <setting name="lazyLoadingEnabled" value="true"/>

        <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->
        <setting name="aggressiveLazyLoading" value="true"/>

        <!-- 是否允许单条 SQL 返回多个数据集 (取决于驱动的兼容性) default:true -->
        <setting name="multipleResultSetsEnabled" value="true"/>

        <!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->
        <setting name="useColumnLabel" value="true"/>

        <!-- 允许 JDBC 生成主键。需要驱动器支持。如果设为了 true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。 default:false  -->
        <setting name="useGeneratedKeys" value="false"/>

        <!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不映射 PARTIAL:部分 FULL:全部  -->
        <setting name="autoMappingBehavior" value="PARTIAL"/>

        <!-- 这是默认的执行类型 (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新) -->
        <setting name="defaultExecutorType" value="SIMPLE"/>

        <!-- 使用驼峰命名法转换字段。 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>

        <!-- 设置本地缓存范围 session:就会有数据的共享 statement:语句范围 (这样就不会有数据的共享 ) defalut:session -->
        <setting name="localCacheScope" value="SESSION"/>

        <!-- 设置 JDBC 类型为空时,某些驱动程序 要指定值, default:OTHER,插入空值时不需要指定类型 -->
        <setting name="jdbcTypeForNull" value="NULL"/>

    </settings>

    <typeAliases>
        <package name="cn.zysheep.entity"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.user}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!--   适用于类路径下的文件加载(注意:mybatis原始开发Mapper.xml文件与接口文件类不在同一路径下,仅能用resource加载映射文件)     -->
        <mapper resource="cn/zysheep/mapper/DeptMapper.xml"></mapper>
        <!--   仅适用于类路径下,接口文件类与映射文件在同一路径下,且接口名与映射文件名相同,并且映射文件命名为接口全类名的情况.     -->
        <mapper class="cn.zysheep.mapper.DeptMapper"></mapper>
        <!--  不常用  路径对应的是网络上了某个文件,注意file:// 前缀 +路径+文件名    -->
        <mapper url="file://"></mapper>
        <!--   适用于类路径下,接口文件与映射文件在同一路径下,且接口名与映射文件名相同,并且映射文件命名为接口全类名的情况.     -->
        <package name="cn.zysheep.mapper"/>
    </mappers>
</configuration>

关于 <mappers/>的用法,如:

场景一 : 接口文件DeptMapper类与映射文件DeptMapper.xml在同一路径下,且接口名与映射文件名相同,并且映射文件命名为接口全类名的情况
在这里插入图片描述
这种情况都可以使用

 <mapper resource="cn/zysheep/mapper/DeptMapper.xml"></mapper>
 <mapper class="cn.zysheep.mapper.DeptMapper"></mapper>
 <package name="cn.zysheep.mapper"/>

场景二 : 接口文件DeptMapper类与映射文件DeptMapper.xml不在同一路径下
在这里插入图片描述
这种情况下只能使用

 <mapper resource="mapper/DeptMapper.xml"></mapper>

原因: 编写后的映射文件DeptMapper.xml和接口文件明显不在一个路径下,所以无法使用class,和package方法
在这里插入图片描述

定义实体类

sys_dept 表为例,实体类代码如下:

@Data
public class Dept {
    private Integer deptId;
    private Integer parentId;
    private String ancestors;
    private String deptName;
    private Integer orderNum;
    private String leader;
    private String phone;
    private String email;
    private String status;
    private String delFlag;
    private String createBy;
    private Date createTime;
}

定义数据访问接口

注意:Spring 集成 MyBatis 后,不需要手动实现 DAO 层的接口,所有的 SQL 执行语句都写在对应的关系映射配置文件中。

public interface DeptMapper {
    List<Dept>  selectDeptList();
}

DeptMapper.xml

映射文件,简称为 Mapper,主要完成 DAO 层中 SQL 语句的映射。映射文件名随意,一般放在 src/resources/mapper 文件夹中。这里映射文件名称定为 TbUserMapper.xml

<?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="cn.zysheep.mapper.DeptMapper">
    <select id="selectDeptList" resultType="Dept">
        select * from sys_dept
    </select>
</mapper>

创建单元测试

所有工作准备就绪,我们就可以测试 MyBatis 是否能够正常执行了。创建一个单元测试类,代码如下:

public class DeptMapperTest {
    SqlSessionFactory sqlSessionFactory = null;
    {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }


    @Test
    public void selectDeptList() {
        try(SqlSession sqlSession = sqlSessionFactory.openSession()) {
            DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
            List<Dept> depts = mapper.selectDeptList();
            depts.forEach(System.out::println);
        }
    }
}

parameterType

将会传入这条语句的参数的完全限定名或别名。这个属性是可选的,因为Mybaits可以通过Type
Handler推断出具体传入语句的参数。

Mybatis中SQL接受的参数分为:
(1) 单个参数

  • 可以接受基本类型,对象类型,集合类型的值。这种情况MyBatis可直接使用这个参数,不需要经过任何处理。

(2)多个参数

  • 任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,0,1…,值就是参数的值。

(3)命名参数

  • 参数使用@Param起一个名字,MyBatis就会将这些参数封装进map中,key就是我们自己指定的名字

(4)POJO

  • 当这些参数属于我们业务POJO时,我们直接传递POJO

(5)Map

  • 我们也可以封装多个参数为map,直接传递

方法一:不需要写parameterType参数

public List<XXXBean> getXXXBeanList(String xxId, String xxCode);  
<select id="getXXXBeanList" resultType="XXBean">

  select t.* from tableName where id = #{0} and name = #{1}  

</select>  

任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,0,1…,值就是参数的值

方法二:基于注解(最简单)

public List<XXXBean> getXXXBeanList(@Param("id")String id, @Param("code")String code);  
<select id="getXXXBeanList" resultType="XXBean">

  select t.* from tableName where id = #{id} and name = #{code}  

</select>  

由于是多参数那么就不能使用parameterType, 这里用@Param来指定哪一个

方法三:Map封装

public List<XXXBean> getXXXBeanList(HashMap map);  
<select id="getXXXBeanList" parameterType="hashmap" resultType="XXBean">

  select 字段... from XXX where id=#{xxId} code = #{xxCode}  
  
</select>  

其中hashmap是mybatis自己配置好的直接使用就行。map中key的名字是那个就在#{}使用那个,map如何封装就不用了我说了吧。

方法四:List封装

public List<XXXBean> getXXXBeanList(List<String> list);  
<select id="getXXXBeanList" resultType="XXBean">
  select 字段... from XXX where id in
  <foreach item="item" index="index" collection="list" open="(" separator="," close=")">  
    #{item}  
  </foreach>  
</select> 

总结

传递list和map在资源消耗上肯定远大于方法一和方法二,但是有一些特殊的情形需要传递list,比如你需要传递一个id集合并批量对id进行sql操作然后再返回等等。所以都需要了解。

resultType,resultMap

resultType:从这条语句中返回期望类型的类的完全限定名或别名。注意如果是集合,那应该是集合可以包含的类型,而不是集合本身。 如: select * from user查询出多个User的记录,resultType应该为User实体类本身,而不是list集合。该属性和resultMap不能同时使用。

resultMap:外部resultMap的命名引用。和resultType属性不能同时使用。

返回一般数据类型

比如要根据 id 属性获得数据库中的某个字段值。
mapper 接口:

//根据 id 获得数据库中的 username 字段的值
    String getEmpNameById(Integer id);

SQL 映射文件:

  <!-- 
        指定 resultType 返回值类型时 String 类型的,
        string 在这里是一个别名,代表的是 java.lang.String 

        对于引用数据类型,都是将大写字母转小写,比如 HashMap 对应的别名是 'hashmap'
        基本数据类型考虑到重复的问题,会在其前面加上 '_',比如 byte 对应的别名是 '_byte'
    -->
    <select id="getEmpNameById" resultType="string">
        select username from t_employee where id = #{id}
    </select>

返回 JavaBean 类型

比如根据某个字段获得数据库中的信息,把查询的结果信息封装成某个 JavaBean 类型的数据。
mapper 接口:

   // 根据 id 查询信息,并把信息封装成 Employee 对象
    Employee getEmpById(Integer id);

SQL 映射文件:

 <!-- 
        通过 resultType 指定查询的结果是 Employee 类型的数据  
        只需要指定 resultType 的类型,MyBatis 会自动将查询的结果映射成 JavaBean 中的属性
    -->
    <select id="getEmpById" resultType="employee">
        select * from t_employee where id = #{id}
    </select>

返回List类型

有时候我们要查询的数据不止一条,比如:模糊查询,全表查询等,这时候返回的数据可能不止是一条数据,对于多数据的处理可以存放在List集合中。
mapper 接口:

 // 假如是全表查询数据,将查询的数据封装成 Employee 类型的集合
    List<Employee> getAllEmps();

SQL 映射文件:

  <!--
        注意这里的 resultType 返回值类型是集合内存储数据的类型,不是 'list'
    -->
    <select id="getAllEmps" resultType="employee">
        select * from t_employee
    </select>

返回Map类型

MyBatis 还支持将查询的数据封装成Map。

  1. 如果查询的结果是一条,我们可以把查询的数据以{表字段名, 对应的值}方式存入到Map中。
    mapper 接口:
   //  根据 id 查询信息,并把结果信息封装成 Map 
    Map<String, Object> getEmpAsMapById(Integer id);

SQL 映射文件:

  <!-- 
        注意这里的 resultType 返回值类型是 'map'
     -->
    <select id="getEmpAsMapById" resultType="map">
        select * from t_employee where id = #{id}
    </select>

下面把查询的结果数据贴出来供大家参考:

{email=zysheep@126.com,username=李四}
email 表中的字段名 username=字段对应的数据
  1. 如果查询的结果是多条数据,我们也可以把查询的数据以{表中某一字段名, JavaBean}方式来封装成Map。
    mapper 接口:
 // 查询所有员工的信息,把数据库中的 'id' 字段作为 key,对应的 value 封装成 Employee 对象
    // @MapKey 中的值表示用数据库中的哪个字段名作 key
    @MapKey("id")
    Map<Integer, Employee> getAllEmpsAsMap();

SQL 映射文件:

   <!--
        注意 resultType 返回值类型,不再是 'map',而是 Map 的 value 对应的 JavaBean 类型
    -->
    <select id="getAllEmpsAsMap" resultType="employee">
        select * from t_employee
    </select>

下面是查询的结果 (只截取了一部分):

{16=Employee{id=16,username='James'}}

MyBatis 允许查询的结果封装成Map,这种机制是极好的。

resultType,resultMap的区别

1、如果只是返回一个值,比如说String或者int,那直接用resultType就行了,resultType="java.lang.String

<select id="getUserName" resultType="java.lang.String">
    select user_name from t_users
</select>

2、 如果sql查询结果返回的列名和实体类中的字段名一致,可以使用resultTypeMyBatis会自动把查询结果赋值给和字段名一致的字段。

@Data
public class Users {
    
    private Long id;
    private String userName;
    private Integer sex;

}

Mapper:

<select id="getUsersType" resultType="com.zysheep.mybatis.domain.Users">
    select user_name userName, sex from t_users
</select>

如果不一致,sql语句中可以使用别名的方式使其一致。

3、当sql的列名和实体类的列名不一致,这时就可以使用resultMap了。

<resultMap id="userMap" type="com.zysheep.mybatis.domain.Users">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
    <result property="sex" column="sex"/>
</resultMap>

<select id="getUsersMap" resultMap="userMap">
    select id, user_name, sex from t_users
</select>

property是实体类的字段名,column是sql查询的列名。

对于简单的映射,resultType和resultMap区别不大。但是resultMap功能更强大,可以通过设置typeHander来自定义实现功能。

MyBatis动态SQL

动态 SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的 SQL 语句不同。若将每种可能的情况均逐一列出,对所有条件进行排列组合,将会出现大量的 SQL 语句。此时,可使用动态 SQL 来解决这样的问题。

动态 SQL,即通过 MyBatis 提供的各种标签对条件作出判断以实现动态拼接 SQL 语句。

这里的条件判断使用的表达式为 OGNL 表达式。常用的动态 SQL 标签有 <if><where><choose><foreach> 等。

注意事项

在 mapper 的动态 SQL 中若出现大于号(>)、小于号(<)、大于等于号(>=),小于等于号(<=)等符号,最好将其转换为实体符号。否则,XML 可能会出现解析出错问题。

特别是对于小于号(<),在 XML 中是绝对不能出现的。否则,一定出错。xml文件解析用下面代替

if 标签

对于该标签的执行,当test的值为 true 时,会将其包含的 SQL 片断拼接到其所在的 SQL 语句中。

本例实现的功能是:查询出满足用户提交查询条件的所有学生。用户提交的查询条件可以包含一个姓名的模糊查询,同时还可以包含一个年龄的下限。当然,用户在提交表单时可能两个条件均做出了设定,也可能两个条件均不做设定,也可以只做其中一项设定。

这引发的问题是,查询条件不确定,查询条件依赖于用户提交的内容。此时,就可使用动态 SQL 语句,根据用户提交内容对将要执行的 SQL 进行拼接

test 条件注意事项

1、test中使用大于、小于

<if test="condition.dataDealStas.indexOf(',') gte 0">

gt: greater than 大于
gte: greater than or equal 大于等于
lt: less than 小于
lte: less than or equal 小于等于
<select id="selectAllById" resultType="stock">
    select * from stock
    <where>
        <if test="address !=null and address != ''">
            <if test="address.indexOf(',') gte 0">
                and address in
                <foreach collection="address.split(',')" open="(" close=")" item="item" separator=",">
                    #{item}
                </foreach>
            </if>
            <if test="address.indexOf(',') lt 0">
                and address = #{address}
            </if>
        </if>
    </where>
</select>

2、String字符串类型的坑:

// POJO 类
private String  LYJDLX;
<!--XML SQL片段-->
<if test="LYJDLX=='1' and JNDL!=null">
    #{LYJDLX}
</if>

单个的字符要写到双引号里面才行,改为<if test='LYJDLX=="1" and JNDL!=null'>或者改为<if test="LYJDLX== '1'.toString() ">

原因是:mybatis是用OGNL表达式来解析的,在OGNL的表达式中,'1'会被解析成字符,java是强类型的,char 和 一个string 会导致不等,所以if标签中的sql不会被解析,从而sql片段不会执行。

总结下使用方法 :单个的字符要写到双引号里面或者使用.toString()才行!

3、 Integer 类型的坑

private Integer status;//状态,可能为0、1、2、3
<if test="status != null and status !=''">
	and status = #{status}
</if>

当status的值为 0时该and status = 0并未正常拼接,也就是说test内的表达式为false,从而导致查询结果错误。但是,显然该值(Integer :0)!= null也!= ’ ',应该为true才对。

当status为Integer类型,并且status值为0时(其他值正常,0是特殊值),该if判断却为false。

原因: 当status为0时,Mybatis会解析成' ' 空字符串。显然if条件status !=''不成立。

为了避免这个问题,改成下面这样写,去掉对空字符的判断,就解决了该问题
<if test="status != null">and status = #{status}</if>

这里有必要再提一个“坑”,如果你有类似于String str ="A"; <if test="str!= null and str == 'A'">这样的写法时,你要小心了。因为单引号内如果为单个字符时,OGNL将会识别为Java 中的 char类型,显然String 类型与char类型做==运算会返回false,从而导致表达式不成立。解决方法很简单,修改为<if test='str!= null and str == "A"'>即可。

定义映射文件

为了解决两个条件均未做设定的情况,在 where 后添加了一个“1=1”的条件。这样就不至于两个条件均未设定而出现只剩下一个 where,而没有任何可拼接的条件的不完整 SQL 语句。

<?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.lusifer.mybatis.dao.DynamicStudentDao">
    <!-- if -->
    <select id="selectByIf" resultType="com.lusifer.mybatis.entity.Student">
        SELECT
            id,
            name,
            age,
            score
        FROM
            student
        WHERE 1 = 1
        <if test="name != null and name != ''">
            AND name LIKE concat('%', #{name}, '%')
        </if>
        <if test="age != null and age > 0">
            AND age > #{age}
        </if>
    </select>
</mapper>

where 标签

<if/> 标签的中存在一个比较麻烦的地方:需要在where后手工添加 1=1 的子句。因为,若 where 后的所有<if/>条件均为 false,而 where 后若又没有 1=1 子句,则 SQL 中就会只剩下一个空的 whereSQL 出错。所以,在 where 后,需要添加永为真子句 1=1,以防止这种情况的发生。但当数据量很大时,会严重影响查询效率。

定义映射文件

<!-- where-->
<select id="selectByWhere" resultType="com.lusifer.mybatis.entity.Student">
    SELECT
        id,
        name,
        age,
        score
    FROM
      student
    <where>
        <if test="name != null and name != ''">
            AND name LIKE concat('%', #{name}, '%')
        </if>
        <if test="age != null and age > 0">
            AND age > #{age}
        </if>
    </where>
</select>

choose 标签

该标签中只可以包含 <when/> <otherwise/>,可以包含多个 <when/> 与一个 <otherwise/>。它们联合使用,完成 Java 中的开关语句 switch…case 功能。

本例要完成的需求是,若姓名不空,则按照姓名查询;若姓名为空,则按照年龄查询;若没有查询条件,则没有查询结果

foreach 标签

foreach 标签-遍历数组

<foreach/> 标签用于实现对于数组与集合的遍历。对其使用,需要注意:

  • collection 表示要遍历的集合类型,这里是数组,即 array。
  • open、close、separator 为对遍历内容的 SQL 拼接。

本例实现的需求是,查询出 id 为 2 与 4 的学生信息。

定义映射文件

动态 SQL 的判断中使用的都是 OGNL 表达式。OGNL 表达式中的数组使用 array 表示,数组长度使用 array.length 表示。

<!-- foreach -->
<select id="selectByForeach" resultType="com.lusifer.mybatis.entity.Student">
    <!-- select * from student where id in (2, 4) -->
    SELECT
        id,
        name,
        age,
        score
    FROM
      student
         <!--不能识别写,必须是array表示数组-->
    <if test="array != null and array.length > 0">
        WHERE id IN
        <!--指定遍历集合类型为数组-->
        <foreach collection="array" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </if>
</select>

foreach 标签-遍历集合

遍历集合的方式与遍历数组的方式相同,只不过是将 array 替换成了 list

遍历泛型为基本类型的 List

定义 DAO 接口
/**
 * 使用 foreach 标签以 list 基本类型的形式查询
 * @param ids
 * @return
 */
public List<Student> selectByForeachWithListBase(List<Long> ids);
定义映射文件
<!-- foreach -->
<select id="selectByForeachWithListBase" resultType="com.lusifer.mybatis.entity.Student">
    <!-- select * from student where id in (2, 4) -->
    SELECT
        id,
        name,
        age,
        score
    FROM
      student
    <if test="list != null and list.size > 0">
        WHERE id IN
        <foreach collection="list" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </if>
</select>

如果指定collection=“list” 或者collection="array"Mybatis会自动解析Dao层的入参为对应的结构

如果需要指定Dao层参数名称可以使用@Param("ids")对应的xml指定collection="ids"
在这里插入图片描述
在这里插入图片描述

遍历泛型为自定义类型的 List

定义 DAO 接口
/**
 * 使用 foreach 标签以 list 自定义类型的形式查询
 * @param students
 * @return
 */
public List<Student> selectByForeachWithListCustom(List<Student> students);
定义映射文件
<!-- foreach -->
<select id="selectByForeachWithListCustom" resultType="com.lusifer.mybatis.entity.Student">
    <!-- select * from student where id in (2, 4) -->
    SELECT
        id,
        name,
        age,
        score
    FROM
      student
    <if test="list != null and list.size > 0">
        WHERE id IN
        <foreach collection="list" open="(" close=")" item="student" separator=",">
            #{student.id}
        </foreach>
    </if>
</select>

遍历执行多条语句 /批量更新/批量删除

Mybatis里面实现多条SQL语句执行

int deleteByIds(@Param("ids") List<String> rids);

<delete id="deleteByIds">
    <foreach collection="ids"  separator=";" item="id">
        delete  from stock where id like  CONCAT(#{id},'%')
    </foreach>
</delete>
<update id="update">
   <foreach collection="xxList" item="item" index="index" open="" close="" separator=";">
     update t_xxx
     <set>
       xxx = #{item.xxx}
     </set>
     where id = #{item.id}
   </foreach>
</update>

需要在jdbc连接中加上allowMultiQueries参数,设置为true

jdbc.url=jdbc:mysql://localhost:3306/ry?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true

sql 标签

<sql/> 标签用于定义 SQL 片断,以便其它 SQL 标签复用。而其它标签使用该 SQL 片断, 需要使用<include/>子标签。该 <sql/> 标签可以定义 SQL 语句中的任何部分,所以 <include/> 子标签可以放在动态SQL的任何位置

修改映射文件

<sql id="select">
    SELECT
        id,
        name,
        age,
        score
    FROM
      student
</sql>
<!-- foreach -->
<select id="selectByForeachWithListCustom" resultType="com.lusifer.mybatis.entity.Student">
    <!-- select * from student where id in (2, 4) -->
    <include refid="select" />

    <if test="list != null and list.size > 0">
        WHERE id IN
        <foreach collection="list" open="(" close=")" item="student" separator=",">
            #{student.id}
        </foreach>
    </if>
</select>

trim标签

​trim​​​标记是一个格式化的标记,主要用于拼接sql的条件语句(前缀或后缀的添加或忽略),可以完成​​set​​​或者是​​where​​标记的功能。

trim属性主要有以下四个

  • ​prefix​​:在trim标签内sql语句加上前缀
  • ​​suffix​​:在trim标签内sql语句加上后缀
  • ​​prefixOverrides​​​:指定去除多余的前缀内容,如:​​prefixOverrides="AND | OR​",去除trim标签内sql语句多余的前缀​"​and"​​​或者"​or"​​
  • ​​suffixOverrides​​:指定去除多余的后缀内容。

update

<update id="updateByPrimaryKey" parameterType="Object">
  update student set 
  <trim  suffixOverrides=",">
    <if test="name != null">
        NAME=#{name},
    </if>
    <if test="hobby != null">
        HOBBY=#{hobby},
    </if>
  </trim> 
  where id=#{id}
</update>

如果name和hobby的值都不为空的话,会执行如下语句

update student set NAME='XX',HOBBY='XX' /*,*/ where id='XX'

会忽略最后一个“,” ;

select

<select id="selectByNameOrHobby" resultMap="BaseResultMap">
  select * from student 
  <trim prefix="WHERE" prefixOverrides="AND | OR">
    <if test="name != null and name.length()>0"> 
      AND name=#{name}
    </if>
    <if test="hobby != null and hobby.length()>0">
      AND hobby=#{hobby}
    </if>
  </trim>
</select>

如果name和hobby的值都不为空的话,会执行如下语句

select * from user WHERE /*and*/ name = 'xx' and hobby= 'xx'

会为片段添加 “WHERE” 前缀,并忽略第一个 “and” ;

insert

<insert id="insert" parameterType="Object">
    insert into student 
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="name != null">
            NAME,
        </if>
        <if test="hobby != null  ">
            HOBBY,
        </if>
    </trim>
    <trim prefix="values(" suffix=")" suffixOverrides=",">
        <if test="name != null  ">
            #{name},
        </if>
        <if test="hobby != null  ">
            #{hobby},
        </if>
    </trim>
</insert>
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李熠漾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值