阶段六-Day04-MyBatis2

news2024/12/20 22:47:51

一、别名 Alias

1. 为什么使用别名

一般映射文件中会包含大量<select>标签, 每个<select>中都需要配置resultType="com.bjsxt.pojo.People",MyBatis提供了别名机制可以对某个类起别名或给某个包下所有类起别名,简化resultType取值的写法。

2. 明确指定别名

通过<typeAlias>标签明确设置类型的别名。

  • type:类型全限定路径。

  • alias:别名名称。

指定别名方式优点和缺点:

  • 优点:别名可以设置为单个字母,以后使用别名时比较方便。

  • 缺点:当需要定义别名的类比较多时,定义别名是个较大工程量。

在mybatis.cfg.xml全局配置文件中,添加下面配置

 <typeAliases>
        <!-- type 类型是 包名+类名 全限定名-->
        <!-- alias 是指定的别名 -->
        <!--
        (1)一个类可以有多个别名
​	    (2)设置别名后,类的全限定路径依然可以使用。
        -->
        <typeAlias type="com.sh.pojo.People" alias="a"/>
    </typeAliases>

别名定义后,可以在resultType中使用别名,类全限定路径方式还可以使用。

<!--  配置SQL映射的文件 -->
<mapper namespace="a.b">
    <select id="select1" resultType="a">
        select * from people;
    </select>
</mapper>

(1)一个类可以有多个别名

(2)设置别名后,类的全限定路径依然可以使用。

尝试修改resultType="a"中a为大写的A,依然运行成功。说明在明确配置别名时不区分大小写。

3. 指定包中所有类的别名

当类个数较多时,明确指定别名工作量较大,可以通过<package>标签指定包下全部类的别名。

指定后所有类的别名就是类名。

<typeAliases>
        <!-- 指定包名 -->
       <package name="com.sh.pojo"/>
    </typeAliases>

在映射文件中测试,别名是否生效。

<!-- 在给包名进行别名设置后 ,resultType直接写类名 不区分大小写-->
    <select id="select2" resultType="people">
        select * from people;
    </select>

4. MyBatis内置别名

MyBatis框架中内置了一些常见类型的别名。这些别名不需要配置

别名映射的类型别名映射的类型别名映射的类型
_bytebytestringStringdateDate
_longlongbyteBytedecimalBigDecimal
_shortshortlongLongbigdecimalBigDecimal
_intintshortShortobjectObject
_integerintintIntegermapMap
_doubledoubleintegerIntegerhashmapHashMap
_floatfloatdoubleDoublelistList
_booleanbooleanfloatFloatarraylistArrayList
booleanBooleancollectionCollection
iteratorIterator

二、SQL查询结果填充的几种方式(面试题)

1. 介绍

MyBatis会根据句映射关系把查询到的结果填充到指定结果集类型中。支持方式:

  • auto mapping:自动映射。当列名或列的别名与实体类属性名相同时不需要做额外配置。

  • resultMap:手动定义映射关系。

  • camel case:驼峰命名转换规则。

2. Auto Mapping

自定映射方式就是前面所讲的方式,只要保证数据库中列名和属性名相同,就可以自动进行映射

自动映射到对象的属性中,如果表中的列名和属性不一样,可以在查询的时候添加别名,这样也能自动映射

select eid 'id',ename 'name',eaddr 'addr'  from people;(单引号可以省略)

3. resultMap

当数据库列名和属性名不同时是无法进行自动映射的,这时需要手动指定映射关系。

举个例子

3.1 创建表

在数据库中创建表。

表名:无论哪种方式,表名和实体类名称是否相同都没有关系。

列名:列名和实体类名称不对应。

3.2 创建实体类

实体类的类名和表名不是必须相同的。

实体类中属性名和列名也不相同。

package com.sh.pojo;

public class People1 {
    private int id;
    private String name;

    @Override
    public String toString() {
        return "People1{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public People1(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public People1() {
    }
}
3.3修改映射文件

在映射文件中需要注意的是<select>标签不再使用resultType属性,而是使用resultMap属性。

resultMap属性表示使用哪个<resultMap>作为结果映射配置

resultMap的作用是可以手动进行映射,表中的字段名和属性名不同时使用

注意:

<resultMap>标签的子标签的property属性必须和类的属性名严格对应,区分大小写。如果没有对应上,会出现反射异常.数据库查询结果填充到对应属性中优先使用setter方法,如果没有setter方法,通过反射直接对属性进行赋值。

column属性对应数据库列名,由于数据库是不区分大小写的,所以column的值也是不区分大小写的。

resultMap可以存在多个。只要id属性不重名就可以。

<!--
    resultMap: 将表中字段和实体类中属性手动映射
        id : resultMap的唯一标识
        type : 手动映射返回的结果类型
    -->
    <!-- 这里的type 设置别名的话可以使用别名 -->
    <resultMap id="abc" type="People1">
        <!-- 主键 和 属性的 映射-->
        <!--
            column : 字段名
            property : 属性名
        -->
        <!--如果有主键设置为id-->
        <id column="pid" property="id"></id>
        <!-- 其他的字段使用result -->
        <result column="pname" property="name"></result>
    </resultMap>
    <select id="select3" resultMap="abc">
        select * from people1;
    </select>

4. 驼峰转换

MySQL中列名命名规范是xxx_yyy,多个单词之间使用下划线进行分割。在Java中属性命名规范是xxxYyy,小驼峰的方式进行命名。这两种技术的命名习惯是不一样的,这就导致每次都需要手动配置映射关系。

MyBatis发现了这个问题,提供了驼峰转换的能力。通过全局配置文件开启驼峰转换功能后,就可以让xxx_yyy自动映射到xxxYyy上。例如:列名叫做peo_id,可以自动映射到peoId的属性上。转换时去掉列中的下划线,把下划线后面单词首字母变大写。

4.1 修改全局配置文件

要想使用驼峰转换,必须在全局配置文件中添加设置,开启驼峰转换功能。

<settings>
        <!-- 开启驼峰转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

三、接口绑定方案

1. 接口绑定方案存在的意义

前面所学的方式都是通过SqlSession的方法调用指定的一个statement。在学习MyBatis之前,我们的习惯都是创建Dao对象,通过一个对象调用类中多个方法。这样的好处是一次创建,多次调用。显然要比前面所学的方式用起来更加方便。

MyBatis提供了也提供了一种接口绑定方案,通过SqlSession的getMapper方法产生接口的动态代理对象。然后通过对象调用接口中提供的功能。

2. 完整项目流程

数据库以ssm数据库的people为例。

2.1 创建项目并配置pom.xml

创建项目,命名为mybatis2。并配置pom.xml,配置的内容和之前一样,只需要导入MyBatis框架依赖和数据库驱动。

2.2 创建全局配置文件

在resources中创建mybatis.cfg.xml。

配置文件中保留了别名的配置,通过<mapper>的子标签换成了<mapper>和<package>标签j

<!-- 映射路径-->
    <mappers>
        <!-- 接口绑定 -->
        <!--&lt;!&ndash; 方式一 单独指定 mapper接口 &ndash;&gt;
        <mapper class="com.sh.mapper.PeopleMapper"></mapper>-->
        <!-- 方式二 只需要指定包名 -->
        <package name="com.sh.mapper"/>
    </mappers>
2.3 创建实体类

在src/main/java下新建com.bjsxt.pojo.People实体类。

package com.sh.pojo;

import java.io.Serializable;

public class People implements Serializable {
    private int id;
    private String name;
    private String addr;

    @Override
    public String toString() {
        return "People{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", addr='" + addr + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    public People(int id, String name, String addr) {
        this.id = id;
        this.name = name;
        this.addr = addr;
    }

    public People() {
    }
}
2.4 创建映射文件(常用)

在接口绑定方案中,对于映射文件有几点强制要求:

  1. 映射文件和接口需要在同一个包中

  2. 映射文件名称要和接口名称相同

两种方法,一种是设置资源拷贝,还有一种就是下面图中的方法

设置相同的路径,在resources中设置路径,分层要使用/而不是.

  1. namespace取值必须是接口的全限定路径

  2. id属性值必须和方法名对应

  3. resultType必须和方法返回值类型对应。如果方法返回值是集合类型,resultType中写泛型的类型

2.5 添加资源拷贝插件的使用

由于映射文件添加到了com.bjsxt.mapper包中,所以需要添加资源拷贝插件,保证映射文件能被编译。

小提示:

资源拷贝插件配置的主要目的是为了让src/main/java下映射文件能被编译时包含。

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>
2.6 编写测试类

创建测试类com.bjsxt.test.Test类

三、接口绑定方案下参数传递

接口绑定方式其底层调用的就是前面学习的SqlSession的相关方法

接口中方法对应以前测试类中直接调用的SqlSession中的方法

对于映射文件的使用还是和之前学习的相同。

接口中方法SqlSession的方法
int insertPeople(People peo);session.insert("insertPeople",peo);
int deleteById(int id);session.delete("deleteById",id);
int updatePeople(People peo);session.update("updatePeople",peo);
People selectById(int id);session.selectOne("selectById",id);
List<People> selectAll();session.selectList("selectAll");
List<People> selectByUnameAndAddr(String name,String address);session.selectList("selectByUnameAndAddr",Map类型参数)

在接口绑定方案中唯一需要重点注意的是:

当方法带有多个参数将使用session.selectList("",Map类型参数)或session.selectOne("",Map类型参数)作为底层调用。

通过前面的学习清楚的知道,Map类型当做参数时,在映射文件中通过Map的key进行获取value值。

Mybatis会自动创建Map的key:

  1. 如果接口中方法没有使用注解定义名称,MyBatis使用内置名称作为key。

    规则:arg0、arg1、argM(M为从0开始的数字,和方法参数顺序对应)或param1、param2、paramN(N为从1开始的数字,和方法参数顺序对应)。

  2. 也可以在接口方法参数中通过@Param("key名称")的形式进行定义key。一定使用了注解argN的这种形式就不能使用了,但是paramN的方式还是可以使用。

 增删改查

3.1 配置接口
package com.sh.mapper;

import com.sh.pojo.People;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface PeopleMapper {
     List<People> selectAll();

     //查询
     //一个参数 根据姓名查询
     List<People> select1(String name);

     //两个参数 姓名,地址
     List<People> select2(String name,String addr);

     //两个参数 姓名,地址
     List<People> select3(@Param("name") String name,@Param("addr") String addr);

     //传递一个对象
     List<People> select4(People people);

     //增加
     int insert1(People people);

     //修改
     int alter1(People people);

     //删除
     int del1(int id);
}
3.2 配置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">
<!--  配置SQL映射的文件 -->
<mapper namespace="com.sh.mapper.PeopleMapper">
    <select id="selectAll" resultType="People">
        select * from people
    </select>

    <!-- 有参数的接口绑定-->
    <!-- 1. 一个参数 -->
    <select id="select1" resultType="People">
        select * from people where name = #{name};
    </select>

    <!-- 2. 两个参数 -->
    <select id="select2" resultType="People">
        <!-- 这里的参数不能直接映射, 因为mybatis分不清楚 -->
        <!--底层代码 使用了一个ParamMap集合进行了封装 */
        /*  两组:
            [arg0, arg1,
            param1, param2]
            */-->
        select * from people where name = #{arg0} and addr = #{arg1};
    </select>

    <!-- 3. 两个参数 -->
    <select id="select3" resultType="People">
        <!-- 这里的参数不能直接映射, 因为mybatis分不清楚 -->
        <!-- /*也可以定义别名,在接口类中使用注解 @Param 定义自己想要的名字 */ -->
        select * from people where name = #{name} and addr = #{addr};
    </select>

    <!-- 4. 两个参数 -->
    <select id="select4" resultType="People">
        <!-- 这里的参数不能直接映射, 因为mybatis分不清楚 -->
        <!-- 使用类中的属性来传递参数  -->
        <!-- 属性名必须一一对应 -->
        select * from people where name = #{name} and addr = #{addr};
    </select>

    <!-- 添加操作 -->
    <!-- 参数的传递和查询同理 -->
    <insert id="insert1">
        insert into people values (0,#{name},#{addr})
    </insert>

    <!-- 修改操作 -->
    <update id="alter1">
        update people set name = #{name},addr = #{addr} where id = #{id}
    </update>

    <!-- 删除操作 -->
    <delete id="del1">
        delete from people where id = #{id}
    </delete>
</mapper>
3.3 配置测试类
import com.sh.mapper.PeopleMapper;
import com.sh.pojo.People;
import org.apache.ibatis.annotations.Param;
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 org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class PeopleTest {
    SqlSessionFactory factory;

    //@Before在@Test方法执行前执行
    @Before
    public void before(){
        //将流的操作放入try()中,可以自动关闭流
        try(InputStream is = Resources.getResourceAsStream("mybatis-config.xml")){
            /*
            *   解析mybatis-config.xml
            *       1.读取db.properties,将结果存储到properties中
            *       2.创建事务工厂
            *       3.创建数据库连接池
            *       4.读取指定包中所有的接口,因为在mapper中配置了package
            *           获取每一个接口,获取 包名.接口名 + .xml
            *           读取包名.接口名.xml的映射文件,完成映射文件的解析
            *           id :  namespace.sql的id
            *           sqlSource : sql语句
            *       5.把一些解析到的文件都存储到了一个叫configuration的类中
            * */
            factory = new SqlSessionFactoryBuilder().build(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


//查询操作
    //动态绑定的查询底层使用的还是 selectList()方法
    @Test
    public void test() throws IOException {

        //获取SqlSession对象
        /*
        * 1.事务控制
        * 2.增删改查
        * 3.ORM映射
        * */
        SqlSession session = factory.openSession();
        //4.执行查询
        //mybatis为PeopleMapper创建了实现类对象(动态代理对象)
        PeopleMapper mapper = session.getMapper(PeopleMapper.class);
        List<People> list = mapper.selectAll();
        /*
         * 获取到 接口的 包名.接口名.方法名  com.zqwl.mapper.PeopleMapper.selectAll
         *
         * sqlSession.selectList("com.zqwl.mapper.PeopleMapper.selectAll");
         *
         * mapper映射文件中解析的内容:
         *   id:  com.zqwl.mapper.PeopleMapper.selectAll
         *   sql: select * from people;
         * */

        System.out.println(list);
        //5.资源释放
        session.close();
    }

    //一个参数
    @Test
    public void test1(){
        SqlSession session = factory.openSession();
        PeopleMapper mapper = session.getMapper(PeopleMapper.class);
        List<People> list = mapper.select1("小明");
        System.out.println(list);
        session.close();
    }

    //两个参数 使用默认的map映射 /*  两组:
    //            [arg0, arg1,
    //            param1, param2]
    //            */
    @Test
    public void test2(){
        SqlSession session = factory.openSession();
        PeopleMapper mapper = session.getMapper(PeopleMapper.class);
        List<People> list = mapper.select2( "小明","北京");
        System.out.println(list);
        session.close();
    }

    // 两个参数 在接口类中使用@Param注解
    @Test
    public void test3(){
        SqlSession session = factory.openSession();
        PeopleMapper mapper = session.getMapper(PeopleMapper.class);
        List<People> list = mapper.select3( "小明","北京");
        System.out.println(list);
        session.close();
    }

    //也可以使用对象传递参数
    @Test
    public void test4(){
        SqlSession session = factory.openSession();
        PeopleMapper mapper = session.getMapper(PeopleMapper.class);
        People people = new People(0,"小明","北京");
        List<People> list = mapper.select4(people);
        System.out.println(list);
        session.close();
    }

    //添加操作
    @Test
    public void test5(){
        SqlSession session = factory.openSession();
        PeopleMapper mapper = session.getMapper(PeopleMapper.class);
        People people = new People(0,"小花","北京");
        int i = mapper.insert1(people);
        System.out.println(i);
        //增删改查手动提交事务
        session.commit();
        session.close();
    }

    //修改操作
    @Test
    public void test6(){
        SqlSession session = factory.openSession();
        PeopleMapper mapper = session.getMapper(PeopleMapper.class);
        People people = new People(9,"小","北京");
        int i = mapper.alter1(people);
        System.out.println(i);
        //增删改查手动提交事务
        session.commit();
        session.close();
    }

    //删除操作
    @Test
    public void test7(){
        SqlSession session = factory.openSession();
        PeopleMapper mapper = session.getMapper(PeopleMapper.class);
        int id = 9;
        int i = mapper.del1(id);
        System.out.println(i);
        //增删改查手动提交事务
        session.commit();
        session.close();
    }
}

四、主键回填

在一些特殊的需求中,需要执行完新增的SQL后立即知道新增时主键的值。如果主键的值是自己设置的固定值,可以知道主键的值。但是很多时候都是使用MySQL的主键自增,这时想要获取主键的值就需要通过特殊的方式获取了。

MyBatis中有两种方式可以获取到自增主键的值:

  • 使用<selectKey>子标签编写SQL进行回填属性值。

  • 使用<select>的useGeneratedKeys属性值进行自动回填。

4.1 mapper映射设置

 <!-- 主键回填 -->
    <insert id="getPrimaryKey">
        <!-- 需要手动设置主键回填 -->
        <!--
        keyColumn 哪个字段是主键
        keyProperty 回填到对象的哪个属性当中
        resultType 返回结果的类型
        order 在什么时候回填 我们肯定是after
        -->
        <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
            select @@identity
        </selectKey>
        insert into people values(default, #{name}, #{addr});
    </insert>

<!-- 主键回填简化操作 -->
    <!-- selectKey 中的属性可以写在 insert 里面-->
    <insert id="getPrimaryKey1" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        insert into people values(default, #{name}, #{addr});
    </insert>

4.2 接口类设置

//主键回填(自增长的主键值回填到指定的对象属性中)
     //获取添加数据的主键自增值
     int getPrimaryKey(People people);

4.3 测试类设置

//获取主键自增值
    @Test
    public void test8(){
        SqlSession session = factory.openSession();
        PeopleMapper mapper = session.getMapper(PeopleMapper.class);
        People people = new People(0,"小花","北京");
        int i = mapper.getPrimaryKey(people);
        //返回的是影响的行数,还是添加操作
        System.out.println(i);
        //回填的主键值存储在people对象当中
        System.out.println(people);
        //输出结果
        /*
        *       1
                People{id=10, name='小花', addr='北京'}
        * */
        //增删改查手动提交事务
        session.commit();
        session.close();
    }

五、动态SQL

1. 动态SQL引入

MyBatis在简化操作方法中提出了动态SQL功能,将使用Java代码拼接SQL语句,改变为在XML映射文件中截止标签拼接SQL语句。相比而言,大大减少了代码量,更灵活、高度可配置、利于后期维护。

MyBatis中动态SQL是编写在mapper.xml中的,其语法和JSTL类似,但是却是基于强大的OGNL表达式实现的。

为了查看动态SQL最终执行时的SQL,先把日志配置上。

2. if标签

通过if处理用户多变的查询条件

接口代码

List<People> selectIf(People people);

mapper映射文件通过if进行判断参数的属性是否为null。

<if>标签的test属性值为OGNL(对象导航图语言)表达式,通过对象属性名可以快速获取到对象属性值。

代码解释说明:

name!=null : OGNL 表达式,直接写属性名可以获取到属性值。不需要添加${}或#{}。

name=#{name} 中name是表中的列名。#{name}是MyBatis获取参数对象属性值的写法(之前学习的)。

where 1=1 中1=1是为了保证SQL语法的正确性。如果if成立没有1=1最后的SQL就是where and name=xxx 这种写法是不对的。

2.1 配置映射文件
 <!-- 动态sql -->
    <!-- if : 满足条件参与执行 相当于java中的if -->
    <select id="select11" resultType="People">
        select * from people where 1=1
        <!--
        因为两个if 中都带有 and ,当有一个为空时语句会变为
        select * from people where and name = #{name}
        语法错误,所以在前面拼接一个1=1
        select * from people where 1=1 and name = #{name}
        保证语句正确
        -->
        <if test="name != null">
            and name = #{name}
        </if>
        <if test="addr != null">
            and addr = #{addr}
        </if>
    </select>
2.2 配置接口类
 //动态sql方法
     //if
     List<People> select11(@Param("name") String name,@Param("addr") String addr);
2.3 配置测试类
//动态sql
    //if
    @Test
    public void test9(){
        SqlSession session = factory.openSession();
        PeopleMapper mapper = session.getMapper(PeopleMapper.class);
        List<People> list = mapper.select11( "小明","北京");
        System.out.println(list);
        session.close();
    }

3. choose

choose when otherwise 相当于 if else if else
注意:只有一个when 或 otherwise能够被执行
<select id="selectIf" resultType="People">
    select * from people where 1=1
    <choose>
        <when test="name!=null">
            and name=#{name}
        </when>
        <when test="address!=null">
            and address=#{address}
        </when>
        <otherwise>
            // do something
        </otherwise>
    </choose>
</select>

4. trim标签

上面的if标签中为了保证语法的正确性,需要在SQL中明确指定where 1=1,其中1=1存在的意义单纯为了保证语法的正确性,没有实际意义的。可以通过trim标签动态进行截取添加,省略where 1=1。

trim标签包含四个属性:

prefix:只要子内容不是空字符串(""),就在子内容前面添加特定字符串。

prefixOverrides:如果子内容是以某个内容开头,去掉这个内容。

suffix:只要内容不是空字符串(""),就在子内容后面添加特定字符串。

suffixOverrides:如果里面内容以某个内容结尾,就去掉这个内容。

小提示:

trim作为很多其它标签的底层。

无论是开头操作还是结尾的操作,都是先去掉内容,后添加。

trim只会对里面的子内容进行操作。如果子内容为空则不进行任何操作。

后添加的内容会有空格。

特例:

如果内部字符串为要去掉的字符串,去掉后认为内容不为空,prefix依然添加。

<select id="selectIf" resultType="People">
    select * from people
    <trim prefix="where" prefixOverrides="and">
        <if test="name!=null">
            and name=#{name}
        </if>
        <if test="address!=null">
            and address=#{address}
        </if>
    </trim>
</select>

5. where标签

where标签属于trim标签的简化版,被where标签包含的内容具备:

简化:<trim prefix="where" prefixOverrides="and">

  1. 如果里面内容不为空串,在里面内容最前面添加where。

  2. 如果里面内容是以and开头,去掉最前面的and。

6. set标签

set标签是专门用在修改SQL中的,属于trim的简化版,带有下面功能:

简化: 

<set> 简化 <trim prefix="set" suffixOverrides=",">
  1. 如果子内容不为空串,在最前面添加set。

  2. 去掉最后一个逗号。

7. foreach标签

foreach标签表示循环,主要用在in查询或批量新增的情况。

foreach标签的属性解释说明:

collection:要遍历的数组或集合对象。

1. 如果参数没有使用@Param注解:arg0或array或list。

2. 如果使用@Param注解,使用注解的名称或param1。

open:遍历结束在前面添加的字符串。

close:遍历结束在后面添加的字符串。

item:迭代变量。在foreach标签里面#{迭代变量}获取到循环过程中迭代变量的值。

separator:分隔符。在每次循环中间添加的分割字符串。

index:迭代的索引。从0开始的数字。

nullable:是否允许数组或集合对象为null。如果设置为true,表示集合或数组允许为null。如果设置为false表示不允许数组或集合对象为null,一旦为null会出现:org.apache.ibatis.builder.BuilderException: The expression 'array' evaluated to a null value。

8. bind标签

bind标签表示对传递进来的参数重新赋值。最多的使用场景为模糊查询。通过bind可以不用在Java代码中对属性添加%。

拼接操作 

<select id="selectLike" resultType="People">
    <bind name="name" value="'%'+name+'%'"/>
    select * from people where name like #{name}
</select>

9. sql和include标签

在企业开发中的表可能都会有很多列,当使用多表联合查询时列的个数更多。

很多功能或SQL都需要使用这些列的话,其实在做很多重复工作。即使复制粘贴,一旦碰到表结构改变或添加、删除列的时候也需要修改很多SQL。

MyBatis的sql标签用于定义SQL片段,include标签用于引用sql标签定义的片段。

<sql id="mysqlpart">
    id,name,address
</sql>
<select id="selectSQL" resultType="People">
    select <include refid="mysqlpart"></include> from people
</select>
<?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">
<!--  配置SQL映射的文件 -->
<mapper namespace="com.sh.mapper.PeopleMapper">
    <select id="selectAll" resultType="People">
        select * from people
    </select>

    <!-- 有参数的接口绑定-->
    <!-- 1. 一个参数 -->
    <select id="select1" resultType="People">
        select * from people where name = #{name};
    </select>

    <!-- 2. 两个参数 -->
    <select id="select2" resultType="People">
        <!-- 这里的参数不能直接映射, 因为mybatis分不清楚 -->
        <!--底层代码 使用了一个ParamMap集合进行了封装 */
        /*  两组:
            [arg0, arg1,
            param1, param2]
            */-->
        select * from people where name = #{arg0} and addr = #{arg1};
    </select>

    <!-- 3. 两个参数 -->
    <select id="select3" resultType="People">
        <!-- 这里的参数不能直接映射, 因为mybatis分不清楚 -->
        <!-- /*也可以定义别名,在接口类中使用注解 @Param 定义自己想要的名字 */ -->
        select * from people where name = #{name} and addr = #{addr};
    </select>

    <!-- 4. 两个参数 -->
    <select id="select4" resultType="People">
        <!-- 这里的参数不能直接映射, 因为mybatis分不清楚 -->
        <!-- 使用类中的属性来传递参数  -->
        <!-- 属性名必须一一对应 -->
        select * from people where name = #{name} and addr = #{addr};
    </select>

    <!-- 添加操作 -->
    <!-- 参数的传递和查询同理 -->
    <insert id="insert1">
        insert into people values (0,#{name},#{addr})
    </insert>

    <!-- 修改操作 -->
    <update id="alter1">
        update people set name = #{name},addr = #{addr} where id = #{id}
    </update>

    <!-- 删除操作 -->
    <delete id="del1">
        delete from people where id = #{id}
    </delete>

    <!-- 主键回填 -->
    <insert id="getPrimaryKey">
        <!-- 需要手动设置主键回填 -->
        <!--
        keyColumn 哪个字段是主键
        keyProperty 回填到对象的哪个属性当中
        resultType 返回结果的类型
        order 在什么时候回填 我们肯定是after
        -->
        <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
            select @@identity
        </selectKey>
        insert into people values(default, #{name}, #{addr});
    </insert>

    <!-- 主键回填简化操作 -->
    <!-- selectKey 中的属性可以写在 insert 里面-->
    <insert id="getPrimaryKey1" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        insert into people values(default, #{name}, #{addr});
    </insert>

    <!-- 动态sql -->
    <!-- if : 满足条件参与执行 相当于java中的if -->
    <select id="select11" resultType="People">
        select * from people where 1=1
        <!--
        因为两个if 中都带有 and ,当有一个为空时语句会变为
        select * from people where and name = #{name}
        语法错误,所以在前面拼接一个1=1
        select * from people where 1=1 and name = #{name}
        保证语句正确
        -->
        <if test="name != null">
            and name = #{name}
        </if>
        <if test="addr != null">
            and addr = #{addr}
        </if>
    </select>

    <!-- choose when otherwise-->
    <!-- 相当于 if   else if  else -->
    <select id="select12" resultType="People">
           select * from people where 1=1
            <choose>
                <when test="name != null">
                    and name = #{name}
                </when>
                <when test="addr != null">
                    and addr = #{addr}
                </when>
                <!-- 所有when都不成立,执行 otherwise -->
                <otherwise>
                    and 2 = 2
                </otherwise>
            </choose>
    </select>

    <!-- trim 简化if -->
    <select id="select13" resultType="People">
        select * from people
        <trim prefix="where" prefixOverrides="and">
        <if test="name != null">
            name = #{name}
        </if>
        <if test="addr != null">
            and addr = #{addr}
        </if>
        </trim>
    </select>

    <!-- trim 简化 choose -->
    <!-- 简化操作有多种,只要能实现 -->
    <select id="select14" resultType="People">
        select * from people
        <trim prefix="where" >
        <choose>
            <when test="name != null and addr != null">
                name = #{name} and addr = #{addr}
            </when>
            <when test="name != null">
                name = #{name}
            </when>
            <when test="addr != null">
                addr = #{addr}
            </when>
            <!-- 所有when都不成立,执行 otherwise -->
            <!-- otherwise 前面也会拼接where -->
            <otherwise>
                2 = 2
            </otherwise>
        </choose>
        </trim>
    </select>

    <select id="select15" resultType="People">
        select * from people
        <where>
            <if test="name != null">
                name = #{name}
            </if>
            <if test="addr != null">
                and addr = #{addr}
            </if>
        </where>
    </select>

    <!-- where 也可以简化 choose -->

    <!-- 更新操作 -->
    <update id="update1" >
        update people
        <!-- prefix 只拼接一次 -->
        <!-- 最后的语句 进行拼接 -->
        <!--
        比如当name,addr都不为空时
        name = #{name},addr = #{addr},
        prefix="set" 前面拼一个set
        set name = #{name},addr = #{addr},
        suffixOverrides="," 后面去掉逗号
        set name = #{name},addr = #{addr}
        -->
        <trim prefix="set" suffixOverrides=",">
        <if test="name != null">
            name = #{name},
        </if>
        <if test="addr != null">
            addr = #{addr},
        </if>
        <!--
        因为拼接了一个where 如果name和addr都为空时
        语句会变成
        update people where id = ?
        语法错误
        所以要加一个 id = #{id}
        相当于不修改
        -->
        id = #{id}
        </trim>
        where id = #{id}
    </update>

    <!-- set 简化上面的操作 -->
    <!--
          <set> 简化 <trim prefix="set" suffixOverrides=",">
          作用:
           1.子串不为空,添加set
           2.子串以,结尾,去掉,
       -->
    <update id="update2" >
        update people
        <set>
            <if test="name != null">
                name = #{name},
            </if>
            <if test="addr != null">
                addr = #{addr},
            </if>
            id = #{id}
        </set>
        where id = #{id}
    </update>

    <!-- 一次操作多条数据 -->
    <!-- foreach 遍历 -->
    <!-- 正常的删除多条数据 -->
    <!--
        delete from people where id in(7,10);

    -->
    <!-- 参数可以是数组 , 也可以是集合 -->
    <!-- 数组 -->
    <!--
        数组类型参数:
                没有注解:array, arg0
                有注解:  注解中的值, param1
    -->
    <delete id="delMany" >
        delete from people where id in
        <!-- 因为我设置了ints的注解 -->
        <!--
            collection 参数传递过来的名字
            open 以什么开头
            separator 以什么分割
            close 以什么结尾
            item 遍历的元素叫什么 , 叫什么都行,下面要使用
            #{num}
        -->
        <foreach collection="ints" open="(" separator="," close=")" item="num">
            #{num}
        </foreach>
    </delete>

    <delete id="delMany1" >
        delete from people where id in
        <!-- 因为我设置了list的注解 -->
        <!--
        delete from people where id in(7,8);
            数组类型list集合:
                没有注解:arg0, collection, list
                有注解:  注解中的值, param1
        -->
        <foreach collection="list" open="(" separator="," close=")" item="num">
            #{num}
        </foreach>
    </delete>

    <!-- 模糊查询 -->
    <!-- 正常模糊查询需要%%,如果想在sql这边操作 -->
    <!-- 使用concat 单行函数 字符串拼接-->
    <!--<select id="selectLike" resultType="People">
        select * from people where name like concat('%', #{name}, '%');
    </select>-->

    <!-- 动态sql也封装了方法 -->
    <!-- bind 标签-->
    <select id="selectLike" resultType="People">
        <bind name="abc" value="'%'+name+'%'"/>
        select * from people where name like #{abc}
    </select>

    <!-- sql 片段 -->
    <!--
     一般在实际开发中我们不会使用*
     而是使用 id , name , addr
    -->
    <!--
            sql 标签来设置片段
            include 来代替
    -->
    <sql id="a">
        id , name , addr
    </sql>
    <select id="selectL" resultType="People">
        select <include refid="a"/>  from people;
    </select>
</mapper>

七、MyBatis中常用注解

在MyBatis中对于特别简单的SQL、尤其不需要定义resultMap的SQL可以使用注解进行实现。通过注解能简化映射文件的编写。

MyBatis的注解通过全局配置文件<mappers>进行加载。

  • 如果一个Mapper接口中所有方法都使用注解定义SQL,可以在全局文件中配置。

<mappers>
    <mapper class="com.bjsxt.mapper.PeopleMapper"></mapper>
</mappers>
  • 如果一个Mapper接口中既有注解又有mapper.xml定义SQL。可以在全局配置文件中通过<package>进行加载。这种方式和之前的接口绑定方案的配置是一样的。也就是说MyBatis在扫描这个包的时候就可以加载到注解。

<mappers>
    <package name="com.bjsxt.mapper"/>
</mappers>

在MyBatis中注解都是写在Mapper接口的方法上中,所有的注解都在org.apache.ibatis.annotations包中,常见注解:

注解解释
@Select查询
@Insert新增
@Delete删除
@Update修改
@SelectKey主键回填
@SelectProvider调用SQL构建器。查询专用
@DeleteProvider调用SQL构建器。删除专用
@UpdateProvider调用SQL构建器。修改专用
@INSERTProvider调用SQL构建器。删除专用
@Param定义参数的名称

2. 主键回填

使用注解时,主键回填需要通过@SelectKey注解。

该注解中:必有属性

keyProperty:表示回填属性名。

statement:执行的sql。

before:表示是否在@Insert的SQL之前执行。resultType:表示statement对应SQL执行结果。

@SelectKey(keyProperty = "id",statement = "select @@identity",before = false,resultType = Integer.class)

3. SQL构建器(Provider)

在以后工作时,有的功能对应SQL可能会非常长,在Navicat中一页显示不下。对于这种情况可以使用映射文件没有什么太大问题。如果放在@Select注解的参数中,显然看起来不是太方便。如果还是希望使用注解,最好使用SQL构建器。

MyBatis的SQL构建器赋予了程序员在Java类中编写SQL的方式。把以前写在注解参数中的复杂SQL转移到了类的方法中进行书写。

3.1 直接写SQL方式代码演示

在接口中提供方法。并在方法上面添加@SelectProvider注解,注解中属性含义:

  • type:编写SQL的类。

  • method:类中哪个方法返回SQL。

@SelectProvider(type = MySQLProvider.class,method = "selectprovider")
List<People> select(People peo);

新建MySQLProvider类,并在类中提供selectprovider方法。

下面SQL看起来写的不是特别费劲,但是一定要注意关键字前后都有空格。下面代码每行前面都有空格,其实这点非常不友好。

public class MySQLProvider {
    public String selectprovider(){
        return "select *" +
                " from people" +
                " where name=#{name} and address like #{address}" +
                " order by id desc";
    }
}
3.2 使用SQL类

MyBatis提供了SQL类,该类中封装了很多方法,方法名称和SQL的关键字名称正好对应。

里面需要注意的点:

  1. 如果多个条件可以放在一个where中,也可以放在多个连续where中(放在多个where方法中不需要and关键字)。

  2. 最终需要调用toString()转换为字符串。

public class MySQLProvider {
    public String selectprovider2(){
        return "select *" +
                "from people" +
                "where name=#{name} and address like #{address}" +
                "order by id desc";
    }
    public String selectprovider(){
        return new SQL()
                .SELECT("*")
                .FROM("people")
                .WHERE("name=#{name}")
                // 没有and,多个并列条件使用WHERE方法
                .WHERE("address like #{address}")
                .ORDER_BY("id desc")
                .toString();
    }
}

4.使用注解进行结果映射

@Results的value属性类型Result[]。

@Result注解:

column:数据库列

property:属性名

id:是否为主键,默认false

    @Results(value = {
            @Result(column = "peo_id",property = "id",id = true),
            @Result(column = "peo_name",property = "name")
    })
    @Select("select * from tb_people where peo_name=#{name}")
    List<People> select2(People peo);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面向对象【构造器】

文章目录 构造器定义构造器的作用构造器的使用说明无参构造器带参数的构造器构造器的重载使用构造器创建对象 总结 构造器定义 构造器是一种特殊类型的方法&#xff0c;它与类同名&#xff0c;没有返回值&#xff0c;并且用于在创建对象时执行初始化操作。构造器的名称必须与类…

C++笔记之关于函数名前的取址符

C笔记之关于函数名前的取址符 相关博文&#xff1a;C之指针探究(十一)&#xff1a;函数名的本质和函数指针 code review! 文章目录 C笔记之关于函数名前的取址符一.函数名可以被视为指向函数的地址二.sayHello和&sayHello是不是等同?三.Qt信号与槽中的取地址符& 一…

【Java】<泛型>,在编译阶段约束操作的数据结构,并进行检查。

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ JAVA泛型 泛型介绍&#xff1a; ①泛型&#…

julia笔记:函数

1 函数的定义&#xff08;两种方法&#xff09; function f(x,y)x yend #f (generic function with 1 method) f(x,y) x y #f(x,y) x y 2 匿名函数&#xff08;两种方法&#xff09; function (x,y)x yend ##3 (generic function with 1 method) (x,y)->x y ##5…

【2023年11月第四版教材】软考高项极限冲刺篇笔记(2)

1 我们要知道的事 1、考试的选择题不会出假大空的管理,一般较为具体 2.3 信息系统治理 首先治理的目标是什么 治理的管理层分为三层 原则:简单透明适合 COBIT IT审计范围:总体、组织、物理、逻辑、其他 IT审计风险:固有、控制、检查、总体审计 IT审计方法:访谈、调查、…

【Linux】进程间通信——共享内存

目录 一、什么是共享内存 二、共享内存的原理 三、使用共享内存实现进程间通信 3.1 shmget接口 3.1.1 key形参详解 3.2 释放共享内存 3.2.1 ipcs指令 3.2.2 ipcrm指令 3.2.3 shmctl接口 3.3 关联共享内存 3.4 去关联共享内存 3.5 使用共享内存进行进程间通信实例 …

YMK_周报2

周报 读论文 投机采样 为什么大语言模型&#xff08;LLM&#xff09;的推理过程文本生成这么慢&#xff1f; 因为运行大型模型的前向传递很慢&#xff0c;你可能需要依次执行数百次迭代。那么为什么前向传递速度慢&#xff1f;前向传递通常以矩阵乘法为主。内存带宽是此操作的…

【数据结构】八大排序算法(内含思维导图和画图分析)

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《JAVA数据结构》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力…

跳表:为什么Redis一定要用跳表来实现有序集合

文章来源于极客时间前google工程师−王争专栏。 二分查找底层依赖的是数组随机访问的特性&#xff0c;所以只能用数组来实现。如果数据存储在链表中&#xff0c;就真的没法使用二分查找算法了吗&#xff1f; 我们可以对链表稍加改造&#xff0c;就可以支持类似“二分”的查找算…

LVGL_文件系统FS

LVGL_文件系统FS 前言&#xff1a; LVG 内置支持以下文件系统&#xff1a; 1、FATFS 2、STDIO (Linux 和 Windows 都可以使用的 C 标准函数接口&#xff0c;比如&#xff1a;fopen, fread…) 3、POSIX (Linux 和 Windows 都可以使用的 POSIX 函数接口&#xff0c;比如&#xff…

vue3 element-plus 组件table表格 勾选框回显(初始化默认回显)完整静态代码

<template><el-table ref"multipleTableRef" :data"tableData" style"width: 100%"><el-table-column type"selection" width"55" /><el-table-column label"时间" width"120">…

Go学习第三章——运算符与进制

Go学习第三章——运算符与进制 1 算术运算符2 关系运算符3 逻辑运算符4 赋值运算符5 其他运算符5.1 位运算符5.2 跟指针有关的运算符 6 运算符的优先级7 获取用户终端输入8 进制转换8.1 进制基本使用8.2 进制之间的转换8.3 原码 反码 补码8.4 位运算符详解 运算符是—种特殊的符…

KubeSphere一键安装部署K8S集群(单master节点)-亲测过

1. 基础环境优化 hostnamectl set-hostname master1 && bash hostnamectl set-hostname node1 && bash hostnamectl set-hostname node2 && bashcat >> /etc/hosts << EOF 192.168.0.34 master1 192.168.0.45 node1 192.168.0.209…

python查询数据库发送邮件,附件csv格式,xlsx格式

# 设置liunx系统运行python代码的解释器 #!/usr/bin/python3# python声明文件的编码格式为UTF-8 # python2默认以ASCII编码来读取文件&#xff0c;如果不声明编码格式&#xff0c;它可能会无法正确地解析非ASCII字符&#xff08;比如中文字符&#xff09;。 # python3开始默认支…

【ACO-KELM预测】基于蚁群算法优化核极限学习机回归预测研究(matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

VS Code C# 开发工具包正式发布

前言 微软于本月正式发布Visual Studio Code C#开发工具包&#xff0c;此前该开发套件已经以预览版的形式在6月份问世。经过4个月的测试和调整&#xff0c;微软修复了350多个问题&#xff0c;其中大部分是用户反馈导致的问题。此外&#xff0c;微软还对产品进行了300多项有针对…

【MicroSoft Edge】格式化的显示JSON格式的数据

当我们没有进行任何操作的时候&#xff0c;默认浏览器给我们展示的JSON的数据是这样的&#xff1a; 看着十分不便。 解决方案&#xff1a; 首先点击 MicroSoft Edge 浏览器右上角的三点&#xff0c;如何选择扩展 点击 获取Microsoft Edge 扩展 搜索 JSONView&#xff0c;第一…

智慧公厕系列产品:为您提供更便捷、更卫生的厕所体验

智慧公厕系列产品致力于改善公共厕所的管理和使用体验&#xff0c;通过引入先进的科技和智能设备&#xff0c;提升厕所的安全、卫生、舒适性。这些产品涵盖了从厕位监测到环境调控&#xff0c;从安全防范到能耗监测的各个方面&#xff0c;为用户提供了一个更加方便、舒适、卫生…

【每日一题】做菜顺序

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;贪心排序 写在最后 Tag 【贪心排序】【数组】【2023-10-22】 题目来源 1402. 做菜顺序 题目解读 每一道菜都有一个满足程度&#xff08;是一个整数&#xff09;&#xff0c;制作完成每道菜的时间为 1&#xff0c;每一…

Xray联动RAD实现自动扫描教程

Rad下载地址&#xff1a;https://github.com/chaitin/rad xray下载地址&#xff1a;https://github.com/chaitin/xray Xray启动监听&#xff1a; xray_windows_amd64.exe webscan --listen 127.0.0.1:7777 --html-output xray-xxx.html RAD启动爬虫抓包&#xff1a; rad_win…