MyBatis笔记整理(较详细)

视频:黑马程序员
参考文档:MyBatis中文网

篇章一:介绍和环境搭建

什么是MyBatis?

​ MyBatis是Apache软件基金会下的一个开源项目,前身是iBatis框架。2010年这个项目由apache 软件基金会迁移到google code下,改名为mybatis。2013年11月又迁移到了github(GitHub 是一个面向开源及私有 软件项目的托管平台)。
​ MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射(多表)。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。它对 jdbc 的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建 connection、创建 statement、手动设置参数、结果集检索等 jdbc 繁杂的过程代码。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录

ORM思想

  • ORM是指(Object Relationship Mapping)对象关系映射

  • 其中

    • 对象:Java的实体类对象
    • 关系:关系型数据库
    • 映射:二者之间的对应关系
  • 体现

    Java概念数据库概念
    属性字段/列
    对象记录/行

环境搭建

1.准备数据表

create table user (
  id int primary key auto_increment,
  username varchar(20) not null,
  birthday date,
  sex char(1) default '男',
  address varchar(50)
);

insert into user values (null, '孙悟空','1980-10-24','男','花果山水帘洞');
insert into user values (null, '白骨精','1992-11-12','女','白虎岭白骨洞');
insert into user values (null, '猪八戒','1983-05-20','男','福临山云栈洞');
insert into user values (null, '蜘蛛精','1995-03-22','女','盤丝洞');

select * from user;

1.创建maven项目

2.在pom.xml文件中导入依赖

<dependencies>
       <!--mybatis核心包-->
       <dependency>
           <groupId>org.mybatis</groupId>
           <artifactId>mybatis</artifactId>
           <version>3.5.0</version>
       </dependency>
       <!--logback日志包-->
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
           <version>1.7.26</version>
       </dependency>
       <dependency>
           <groupId>ch.qos.logback</groupId>
           <artifactId>logback-core</artifactId>
           <version>1.2.3</version>
       </dependency>
       <dependency>
           <groupId>ch.qos.logback</groupId>
           <artifactId>logback-classic</artifactId>
           <version>1.2.3</version>
       </dependency>
       <!--mysql驱动-->
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>5.1.18</version>
       </dependency>
		<!--单元测试-->
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.10</version>
           <scope>test</scope>
       </dependency>

   </dependencies>

3.创建核心配置文件

<?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>
    <!--
        1.加载外部的配置文件
    -->
    <properties resource="db.properties"/>
    <!--    
        2.经典命名
    -->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <!--    
        3.别名 只需要使用实体类类名User
    -->
    <typeAliases>
        <package name="com.itheima.sh.pojo"/>
    </typeAliases>
    <!--
        开发环境配置
    -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--    
        加载接口加载映射文件
    -->
    <mappers>
       <package name="com.itheima.sh.dao"/>
    </mappers>
</configuration>

4.创建数据库配置文件db.properties

# 连接数据库的驱动
jdbc.driver=com.mysql.jdbc.Driver
# 连接数据库的连接地址
jdbc.url=jdbc:mysql:///mybatis01
# 连接数据库的用户名
jdbc.username=root
# 连接数据库的密码
jdbc.password=123456

5.映射文件

<?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.sh.dao.UserMapper">
    <select id="queryAllUsers" resultType="User">
        select * from user
    </select>
</mapper>

6.创建UserMapper接口

package com.itheima.sh.dao;

public interface UserMapper {
    List<User> queryAllUsers();
}

7.创建实体类User

package com.itheima.sh.pojo;

import java.util.Date;

public class User {
    private Integer id;
    private String userName;
    private Date birthday;
    private String sex;
    private String address;

    public User() {
    }

    public User(Integer id, String userName, Date birthday, String sex, String address) {
        this.id = id;
        this.userName = userName;
        this.birthday = birthday;
        this.sex = sex;
        this.address = address;
    }

    public Integer getId() {
        return id;
    }

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

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

8.测试类

package com.itheima.sh.a_test_01;

import com.itheima.sh.dao.UserMapper;
import com.itheima.sh.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 org.junit.Test;

import java.util.List;

public class MyBatisTest {

    @Test
    public void queryAllUsers() throws Exception{
        //1.创建会话工厂创造类的对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //2.创建会话工厂对象
        SqlSessionFactory factory = builder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        //3.获取会话对象
        SqlSession sqlSession = factory.openSession();
        //4.使用sqlSession调用方法获取接口代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //5.使用接口代理对象调用方法
        List<User> list = mapper.queryAllUsers();
        //6.输出
        System.out.println("list = " + list);
    }
}

9.项目结构

image-20220121173412932

篇章二:核心配置文件和映射文件

核心配置文件

​ 核心配置文件是MyBatis的全局配置文件,包含全局配置信息,如数据库连接参数、插件等。整个框架中只需要一个即可,建议命名为mybatis-config.xml,不做强制要求。

1、mybatis全局配置文件是mybatis框架的核心配置,整个框架只需一个;
2、mybatis全局配置文件中的配置顺序:注意如果配置多项,必须按照以下顺序进行配置
	properties:属性配置
	settings:设置
	typeAliases:类型别名设置
	typeHandlers:类型处理器
	enviroments:环境配置
		environment(环境变量)
		transactionManager(事务管理器)
		dataSource(数据源)
	mappers:映射器

1.properties标签

作用:

  1. 加载外部的java资源文件(properties文件);
  2. 通过子标签property设置属性;

范例:

<properties resource="db.properties"></properties>

2.settings标签

settings参数有很多

1564450693748

mapUnderscoreToCamelCase属性
开启驼峰匹配:完成经典的数据库命名到java属性的映射
		经典数据库命名:如果多个单词之间,通常使用下划线进行连接。
		java中命名:第二个单词首字母大写。
驼峰匹配:相当于去掉数据库的数据中的名字的下划线,和java进行匹配  

应用场景:

从数据库中查到数据的字段名user_name和实体类属性名userName,不一致,导致查到的数据无法封装到实体类中。
但是,实体类中的属性userName符合驼峰命名,数据库字段名user_name符合数据库经典字段命名。

范例:

<!--
    2、settings设置:
 mapUnderscoreToCamelCase :驼峰自动映射配置 满足条件:数据库字段名  user_name   ,实体类属性名 userName
 -->
<settings>
  <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

解决字段名和属性名不一致的方法

如果数据库字段名和属性名不一致,或者是也不满足上面的命名规则。可以通过在SQL语句中为字段名取别名进行映射。

3.typeAliases(类型别名)

作用:类型别名是给类的全类名(包名.类名) 取一个短名称。存在的意义仅在于用来减少类完全限定名的冗余

范例:

<!-- 方式:使用typeAliases标签的子标签package包扫描映射别名;-->
<!-- package扫描指定包下的所有类,扫描之后的别名就是类名,大小写不敏感(不区分大小写),建议使用的时候和类名一致。-->
<typeAliases>
        <package name="com.itheima.sh.pojo"></package>
</typeAliases>
<!--typeAliases还有一个子标签typeAlias 单独取别名
<typeAlias type="com.heima.mybatis.pojo.User" alias="User"/>
-->

【内置别名】

​ MyBatis中内建了一些类型的别名,常见的有。它们都是不区分大小写的,注意对基本类型名称重复采取的特殊命名风格。

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

4.environments标签

MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置;尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境的分离。

父标签: environments(环境配置)
    子标签:
    environment(环境变量)
    transactionManager(事务管理器)
    dataSource(数据源) 
  • 在environments标签中配置多个environment,通过属性default指定一个默认环境配置;
<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>

5.mapper标签

mappers(映射器):UserMapper.xml====>UserMapper.java接口 关联.

作用:维护接口和映射文件之间的关系.

​ 既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。 但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。 Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。方式如下:

1、加载映射文件,关联UserMapper.java接口
    <mapper resource="org/mybatis/example/BlogMapper.xml"/> 从src下加载映射文件;注意使用的是"/"

2、加载接口,关联映射文件
   条件:1、接口名和映射文件名保持一致;2、路径保持一致;
     【2】批量加载class:<package name="com.heima.mybatis.dao"/>

映射文件

​ Mapper映射文件中定义了操作数据库的sql,每一个sql都被包含在一个statement中。映射文件是mybatis操作数据库的核心。

1564474153031

在映射文件中我们需要研究的内容有:SQL语句,入参,返回值

1564474377224

范例:

<?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="Mapper接口的全类名">
  <select id="Mapper接口的方法名" resultType="接口方法的返回值类型">
    select * from Blog where id = #{id}
  </select>
</mapper>
  • 映射配置文件要保证两个一致

    • mapper接口的全类名和映射文件的命名空间namespace保持一致
    • mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
  • <insert>标签

    • 用于书写插入数据的SQL语句

    • id属性指定对应mapper接口的方法名

    • 范例

       <!--
      	注意:#{username},#{birthday},#{sex},#{address} 大括号里面的值必须和pojo的实体类User类中的属性名一致,否则会报错。其实这里看的是User类中的getXxx()的get后面的xxx内容
      例如 getUserName---看的是userName ,就是将get去掉,U变为小写u,后面不变
      -->
      <insert id="saveUser">
          insert into user values (null ,#{username},#{birthday},#{sex},#{address})
      </insert>
      
  • <delete>标签

    • 用于删除表中的数据

    • id属性指定对应mapper接口的方法名

    • 范例

      <!--删除-->
      <delete id="deleteUser" >    
          delete from user where id = #{id}
      </delete>
      
  • <update>标签

    • 用于更新表中数据

    • id属性指定对应mapper接口中的方法名

    • 范例

      <update id="updateUser">
          SQL语句
      </update>
      
  • <select>标签

    • 用于查询表中的数据

    • id属性指定mapper接口中对应的方法名

    • resultType属性表示自动映射,用于属性名和表中字段名一致的情况

    • resultMap属性表示自定义映射,用于一对多或多对一或字段名和属性名不一致的情况

    • 范例

      查询一条数据:

      <!--根据id查询用户数据-->
      <!--parameterType="int" 表示sql语句参数id的类型,int是Integer的别名-->
      <select id="queryById" resultType="user" parameterType="int">
          select * from user where id = #{id}
      </select>
      

      查询多条数据到List集合:

      <select id="getUserList" resultType="com.lxq.pojo.User">
          SQL语句
      </select>
      

篇章三:基于Mapper代理和注解开发

参数值的获取#{}和${}

获取接口方法中传入的参数。获取参数,有两种方式:#{}${}

1、SQL语句中获取参数的方式:

#{xxx}  sql:select * from user where id = ?
${xxx} sql:select * from user where id = 1

2、取值的相同点:
都能够获取接口方法传入的参数值
3、取值的不同点:

#{}取值:是以预编译的形式将参数设置到SQL语句中。PreparedStatement 防止SQL注入;
${}取值:直接把获取到的参数值,拼接到sql语句中,会有安全问题;不能防止SQL注入;

4、小结:

SQL传入参数的获取使用#{};
拼接参数使用${};

Mapper代理

1.传入的参数

​ CRUD标签都有一个属性parameterType,底层的statement通过它指定接收的参数类型。入参数据有以下几种类型:HashMap,基本数据类型(包装类),实体类;

设置传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数类型。
简单数据类型:int、string、long、Date
  • 单个参数:接口方法传入一个参数

    当接口方法传入一个参数时,mybatis不做特殊处理,只需要#{任意变量名}都可接收;但是尽量见名知意
    
  • 多个参数:接口方法传入多个参数

    当传入多个参数时,mybatis底层进行了处理
    
    • 使用参数索引获取:arg0,arg1

      <!--根据用户名和性别查询-->
      <select id="queryByUserNameAndSex" resultType="User">
              select * from user where username=#{arg0} and sex=#{arg1}
      </select>
      
    • 使用参数位置获取:param1,param2

      <!--根据用户名和性别查询-->
      <select id="queryByUserNameAndSex" resultType="User">
          select * from user where username=#{param1} and sex=#{param2}
      </select>
      
    • 使用命名参数获取,明确指定传入参数的名称:(建议使用这个)

      //步骤一:在接口中传入参数时通过@Param指定参数名称
      //根据用户名和性别查询
      User queryByUserNameAndSex(@Param("username") String userName, @Param("sex") String sex);
      
      <!--步骤二:在接收参数时,通过指定的名称获取参数值;-->
      <!--根据用户名和性别查询-->
      <select id="queryByUserNameAndSex" resultType="User">
          <!--#{username}这里的username是@Param("username"),也就是说@Param("标识符")标识符是什么,这里就写什么-->
          select * from user where username=#{username} and sex=#{sex}
      </select>
      
复杂数据类型:pojo实体类、Map集合
  • pojo实体类

    接口添加方法:

        //新增
        void saveUser(User user);
    

    映射文件:

    <!--新增-->
    <insert id="saveUser">
        insert into user values (null ,#{username},#{birthday},#{sex},#{address})
    </insert>
    

    说明:接口方法传入pojo类型的数据时,mybatis底层直接使用pojo封装数据。 sql语句中 #{username}取值==》到pojo中调用 getUsername(){}

    测试:

    @Test
    public void saveUser() throws Exception {
        User user = new User();
        user.setUsername("蔡徐坤");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("上海");
        userMapper.saveUser(user);
    }
    
  • HashMap参数

    需求:模拟用户登录,登录方法参数是Map集合,泛型都是String类型分别表示用户名和性别。

    在UserMapper接口中添加以下方法:

    /**
     * 用户登陆,参数为map
     * @return
     */
    User login(Map<String,String> map);
    

    UserMapper配置文件:

    <!--
       将map的key作为参数名称来传递参数
    -->
    <select id="login" resultType="User">
         select * from user where username=#{username} and sex=#{sex}
    </select>
    

    测试用例:

        @Test
        public void login(){
            Map<String, String> map = new HashMap<>();
            map.put("username","孙悟空");
            map.put("sex","男");
            User user = userMapper.login(map);
            System.out.println(user);
        }
    

2.结果映射

在使用原生的JDBC操作时,对于结果集ResultSet,需要手动处理。mybatis框架提供了resultType和resultMap来对结果集进行封装。

即:只要一个方法有返回值需要处理,那么 resultType和resultMap必须有一个

resultType
    从sql语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。
  • 返回值是简单类型

​ 例如 int ,string ===>resultType=“书写对应的基本类型别名或者全名即可”

image-20200606103412396

  • 返回值为一个pojo(User)对象时

    【定义resultType为User】

    1564483479082

    【使用User来接收返回值】

    1564538720710

  • 返回值是一个List时

​ 当返回值为List集合时,resultType需要设置成集合中存储的具体的pojo数据类型:

【映射文件】

image-20200606103712740

【接口】

image-20200606103756850

【测试类】

//1.使用接口对象调用接口中的方法
List<User> userList = mapper.findAllUsers();
//2.遍历集合
for (User user : userList) {
    System.out.println(user);
}
  • 返回值是Map时

    • 返回一条数据,封装到map中

      需求:查询id是1的数据,将查询的结果封装到Map<String,Object>中

      接口方法:

       //需求:查询id是1的数据,将查询的结果封装到Map<String,Object>中
       Map<String,Object> selectByIdReturnMap(Integer id);
      

      SQL语句:

      <select id="selectByIdReturnMap" resultType="map">
              select * from user where id=#{id}
      </select> 
      

      测试:原来封装到对象中的数据也能够封装到map中

      @Test
      public void selectByIdReturnMap(){
          Map<String, Object> map = userMapper.selectByIdReturnMap(1);
          System.out.println("map = " + map);
      }
      

      结果:

      map = {birthday=1980-10-24, address=花果山水帘洞, sex=, id=1, username=孙悟空}
      

      通过上述结果我们发现如果返回一条数据放到Map中,那么列名会作为Map集合的key,结果作为Map集合的value:

      image-20200606094904280

    • 返回多条数据,封装到map中

      需求:查询数据表所有的数据封装到Map<String,User>集合中

      ​ 要求: Key值为一条记录的主键,Value值为pojo的对象.

      ​ 如下所示:

      1565524547986

      接口方法:接口方法上面通过注解 @MapKey指定key值封装的列数据

         //需求:查询数据表所有的数据封装到Map<String,User>集合中
          @MapKey("id")
          Map<String, User> selectReturnMap();
      

      说明:需要在接口的方法上使用注解@MapKey指定数据表中哪一列作为Map集合的key,否则mybatis不知道具体哪个列作为Map集合的key.

      SQL语句:

      <select id="selectReturnMap" resultType="map">
              select * from user
      </select>
      

      测试代码:

          @Test
          public void selectReturnMap(){
              Map<String, User> map = userMapper.selectReturnMap();
              System.out.println("map = " + map);
          }
      

      结果:

      map = {1={birthday=1980-10-24, address=花果山水帘洞, sex=, id=1, username=孙悟空}, 2={birthday=1992-11-12, address=白虎岭白骨洞, sex=, id=2, username=白骨精}, 3={birthday=1983-05-20, address=福临山云栈洞, sex=, id=3, username=猪八戒}, 4={birthday=1995-03-22, address=盤丝洞, sex=, id=4, username=蜘蛛精}, 7={birthday=2020-06-05, address=上海, sex=, id=7, username=蔡徐坤}, 8={birthday=2020-06-05, address=上海, sex=, id=8, username=蔡徐坤}, 9={birthday=2020-06-05, address=上海, sex=, id=9, username=蔡徐坤}, 10={birthday=2020-06-05, address=上海, sex=, id=10, username=蔡徐坤}, 11={birthday=2020-06-06, address=上海, sex=, id=11, username=蔡徐坤}}
      
      

小结:

1.接口方法返回类型是简单类型(除了单列集合),那么在标签的resultType属性值中书写返回简单类型的类名或者别名
    举例:
    Integer show();====> resultType="int"
    String show();====> resultType="string"
2.如果接口方法返回类型是简单类型的单列集合,那么在标签的resultType属性值中书写集合的泛型类型
    举例:
    List<User> show();====> resultType="User"
3.如果接口方法返回类型是复杂类型的pojo,那么在标签的resultType属性值中书写pojo类型
     举例:
    User show();====> resultType="User"
4.如果接口方法返回类型是复杂类型的Map,那么在标签的resultType属性值中书写map类型,但是分为返回的是单行数据还是多行数据:
    	如果是单行数据,不用做处理
    	如果是多行数据,在方法上使用注解@MapKey("数据表字段名")告知mybatis,哪个字段名的值作为map集合的key
    举例: 如果是多行数据
    @MapKey("id")
    Map<Integer,Object> show();
resultMap

ResultMap是mybatis中最重要最强大的元素,使用ResultMap可以解决两大问题:

  1. pojo属性名和表结构字段名不一致的问题(有些情况下也不是标准的驼峰格式,比如id和userId)
  2. 完成高级查询,比如说,一对一、一对多、多对多。

【问题:】查询数据的时候,查不到userName的信息,原因:数据库的字段名是user_name,而POJO中的属性名字是userName
两端不一致,造成mybatis无法填充对应的字段信息。

解决方案1:在sql语句中使用别名

<select id="queryById" resultType="user" parameterType="int">
   select *,name as username from user where id = #{id}
</select>

解决方案2:参考驼峰匹配 — mybatis-config.xml 的时候

<settings>
   <setting name="mapUnderscoreToCamelCase" value="true" />
</settings>

注意:这种解决方案只能解决列名是下划线命名.

解决方案3:resultMap自定义映射

<!--
     type="user" 表示结果集的封装类型是user
-->
<resultMap id="userResultMap" type="User" autoMapping="true">
   <!--配置主键映射关系-->
   <id column="id" property="id"></id>
   <!--配置用户名的映射关系  column 表示数据表列  property表示pojo的属性-->
  <result column="name" property="username"></result>
</resultMap>

<!--
     resultMap属性:引用自定义结果集作为数据的封装方式.属性值是自定义resultMap标签的id属性值,这里表示通过id引入自定义的resultMap标签
-->
<select id="queryById" resultMap="userResultMap">
    select * from user where id = #{id}
</select>

动态SQL

​ MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

动态SQL中的业务逻辑判断需要使用到以下运算符: ognl表达式

1.   e1 or e2 满足一个即可
2.   e1 and e2 都得满足 
3.    e1 == e2,e1 eq e2 判断是否相等
4.    e1 != e2,e1 neq e2 不相等
5.    e1 lt e2:小于   lt表示less than 
6.    e1 lte e2:小于等于,其他gt(大于),gte(大于等于) gt 表示greater than
7.    e1 in e2 
8.    e1 not in e2
9.    e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2
10.   !e,not e:非,求反
11.   e.method(args)调用对象方法
12.   e.property对象属性值
13.   e1[ e2 ]按索引取值,List,数组和Map
14.   @class@method(args)调用类的静态方法
15.   @class@field调用类的静态字段值
  • if标签

    • if标签通过test属性给出判断的条件,如果条件成立,则将执行标签内的SQL语句

    • 范例

      <select id="getEmpByCondition" resultType="Emp">
          select * from t_emp where
          <if test="empName != null and empName != ''">
              emp_name = #{empName}
          </if>
          <if test="age != null and age != ''">
              and age = #{age}
          </if>
          <if test="gender != null and gender != ''">
              and gender = #{gender}
          </if>
      </select>
      
  • where标签

    • 考虑if标签中的范例出现的一种情况:当第一个if标签条件不成立而第二个条件成立时,拼接成的SQL语句中where后面连着的是and,会造成SQL语句语法错误,而where标签可以解决这个问题

    • 范例

      <select id="getEmpByCondition" resultType="Emp">
          select * from t_emp
          <where>
              <if test="empName != null and empName != ''">
                  emp_name = #{empName}
              </if>
              <if test="age != null and age != ''">
                  and age = #{age}
              </if>
              <if test="gender != null and gender != ''">
                  and gender = #{gender}
              </if>
          </where>
      </select>
      
    • where标签只会在子标签返回任何内容的情况下才插入WHERE子句。而且,若子句的开头有多余的and或者or,where标签也会将它们去除,但是子句末尾的and或者or不能去除

  • trim标签

    • trim标签用于去掉或添加标签中的内容

    • trim标签常用属性

      1. prefix:在trim标签中的内容的前面添加某些内容
      2. prefixOverrides:在trim标签中的内容的前面去掉某些内容
      3. suffix:在trim标签中的内容的后面添加某些内容
      4. suffixOverrides:在trim标签中的内容的后面去掉某些内容
    • 用trim实现where标签范例相同的功能

      <select id="getEmpByCondition" resultType="Emp">
          select * from t_emp
          <trim prefix="where" prefixOverrides="and">
              <if test="empName != null and empName != ''">
                  emp_name = #{empName}
              </if>
              <if test="age != null and age != ''">
                  and age = #{age}
              </if>
              <if test="gender != null and gender != ''">
                  and gender = #{gender}
              </if>
          </trim>
      </select>
      
  • choose、when、otherwise标签

    • 这三个标签是组合使用的,用于在多条件中选择一个条件,类似Java中的if…else if…else…语句

    • 范例

      <select id="getEmpByCondition" resultType="Emp">
          select * from t_emp where gender = #{gender}
          <choose>
              <when test="empName != null and empName != ''">
                  and emp_name = #{empName}
              </when>
              <when test="age != null and age != ''">
                  and age = #{age}
              </when>
          </choose>
      </select>
      
    • 当某个when标签的条件满足时将对应的SQL语句返回,如果都不满足并且有otherwise标签时,才会返回otherwise标签中的SQL语句

  • foreach标签

    • foreach标签允许指定一个集合或数组,并且对这个集合或数组进行遍历

    • foreach标签可以用的属性有

      1. collection:指定需要遍历的集合或数组
      2. item:当前遍历到的元素
      3. index:当前遍历到的元素的序号
      4. 当遍历的集合是Map类型时,index表示键,item表示值
      5. open:指定遍历开始前添加的字符串
      6. close:指定遍历开始后添加的字符串
      7. separator:指定每次遍历之间的分隔符
    • collection属性值注意事项

      • 如果遍历的是List时,属性值为list
      • 如果遍历的是数组时,属性值为array
      • 如果遍历的是Map时,属性值可以是map.keys()map.values()map.entrySet()
      • 除此之外,还可以在映射方法的参数中使用**@Param()**注解自定义collection属性值
    • 批量添加数据

      <insert id="addMoreEmp">
          insert into t_emp values
          <foreach collection="list" separator="," item="emp">
              (null,#{emp.empName},#{emp.age},#{emp.gender},null)
          </foreach>
      </insert>
      
    • 批量删除数据

      <delete id="deleteMoreEmp">
          delete from t_emp where emp_id in
          <foreach collection="array" item="empId" separator="," open="(" close=")">
              #{empId}
          </foreach>
      </delete>
      
  • sql标签

    • 用于记录一段通用的SQL语句片段,在需要用到该SQL语句片段的地方中通过include标签将该SQL语句片段插入

    • sql标签通过id属性唯一标识一个SQL语句片段,include标签通过refid属性指定使用某个SQL片段

    • 范例

      <sql id="item">
          emp_id,emp_name,age,gender,dept_id
      </sql>
      <select id="getEmpByEmpId" resultType="Emp">
      	select <include refid="item"></include>
          from t_emp
          where emp_id = #{empId}
      </select>
      
  • set标签

set标签:在update语句中,可以自动添加一个set关键字,并且会将动态sql最后多余的逗号去除。

案例:修改用户信息,如果参数user中的某个属性为null,则不修改。

如果在正常编写更新语句时,如下:

image-20200606220512579

update user SET username = ?, birthday=?, sex=?, where id = ? 
# 那么一旦在传递的参数中没有address,此时生成的sql语句就会因为多了一个逗号而报错。
 <!--* 修改用户信息,如果参数user中的某个属性为null,则不修改-->
    <!--void updateUserById(User user);-->
    <update id="updateUserById">
        update user
        <set>
            <if test="userName!=null">
                user_name=#{userName},
            </if>
            <if test="birthday!=null">
                birthday=#{birthday},
            </if>
            <if test="sex!=null">
                sex=#{sex},
            </if>
            <if test="address!=null">
                address=#{address}
            </if>
        </set>
        where id=#{id}
    </update>

自增主键回填

新增一条数据成功后,将这条数据的主键封装到实体类中,并查看主键的值。

  • 使用insert标签的属性useGeneratedKeys,keyProperty,keyColumn实现;

    属性说明
    useGeneratedKeystrue 获取自动生成的主键,相当于select last_insert_id()
    keyColumn表中主键的列名
    keyProperty实体类中主键的属性名

    映射文件

    <insert id="saveUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
            insert into user values (null ,#{username},#{birthday},#{sex},#{address})
    </insert>
    
    • 说明:直接在insert标签中增加属性的方式,只适合于支持自动增长主键类型的数据库,比如MySQL或SQL Server。

多表查询

多表查询使用resultMap标签完成

步骤:

​ 1、首先,编写接口方法。编写SQL语句;
​ 2、第二步:分析SQL,封装数据(关联对象);
​ 3、处理多表之间的数据封装(数据库字段名—》实体类的属性名之间的映射)

resultMap: 
	属性:
		id 唯一标识,被引用的时候,进行指定
        type 结果集对应的数据类型  Order
        autoMapping 开启自动映射
        extends 继承
	子标签:
	 id:配置id属性
	 result:配置其他属性
      association:配置一对一的映射
          property 定义对象的属性名
          javaType 属性的类型
          autoMapping 开启自动映射
      collection:配置一对多的映射
          property 定义对象的属性名
          javaType 集合的类型
          ofType 集合中的元素类型 泛型
  		  autoMapping 开启自动映射
一对一查询
一对一关联查询:
1、需要在Order实体类中关联User对象;最终将数据封装到Order中;
2、在OrderMapper.xml文件中书写关联语句并配置关系;
3、关联关系配置:
	    <resultMap id="orderAndUserResultRelative" type="Order" autoMapping="true">
            <!--主表主键-->
            <id column="oid" property="id"/>
            <!--关联关系-->
            <association property="user" javaType="User" autoMapping="true">
                <!--从表主键-->
                <id column="uid" property="id"/>
            </association>
        </resultMap>

一对多查询

一对多关系配置:
1、在对象中添加映射关系;
2、编写接口方法,编写SQL;
3、编写resultMap处理数据库字段和实体类之间数据的封装;

		<resultMap id="orderAndUserResultRelative" type="Order" autoMapping="true">
            <!--主表主键-->
            <id column="oid" property="id"/>
            <!--关联关系-->
            <collection property="user" javaType="User" autoMapping="true">
                <!--从表主键-->
                <id column="uid" property="id"/>
            </collection>
        </resultMap>

基于注解

上述我们已经学习mybatis的SQL映射文件可以使用xml的方式配置,但是我们发现不同的用户模块接口都对应一个映射文件,并且在映射文件中书写sql语句也比较麻烦。所以Mybatis为用户提供了快速的开发方式,因为有时候大量的XML配置文件的编写时非常繁琐的,因此Mybatis也提供了更加简便的基于注解(Annnotation)的配置方式。

注解实现CRUD

@Insert:保存  
         Value:sql语句(和xml的配置方式一模一样)
         
@Update:更新 
         Value:sql语句
         
@Delete: 删除
         Value:sql语句
         
@Select: 查询
         Value:sql语句
         
@Options:可选配置(获取主键)
         userGeneratedKeys:开关,值为true表示可以获取主键  相当于select last_insert_id()
         keyProperty     :对象属性
         keyColumn       : 列名

insert:

public interface UserMapper {
    //1、新增数据  #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量
    @Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})")
    void saveUser(User user);
}

delete:

/*
        2.根据id删除用户
     */
    @Delete("delete from user where id=#{id}")
    void deleteUserById(Long id);

update:

 /**
     * 3.修改用户数据
     * @param user
     */
    @Update("UPDATE tb_user SET user_name=#{userName}, password=#{password} ,name=#{name} ,age=#{age},sex=#{sex} where id=#{id}")
    void updateUser(User user);

query:

 /**
     * 3.修改用户数据
     * @param user
     */
    @Update("UPDATE tb_user SET user_name=#{userName}, password=#{password} ,name=#{name} ,age=#{age},sex=#{sex} where id=#{id}")
    void updateUser(User user);

自增主键回填

1、在新增数据注解 @Insert下面,添加@Options;
2、在Options注解中,设置useGeneratedKeys值为true,keyProperty为id,keyColumn id;
 //1、新增数据  #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量
@Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})")
@Options(useGeneratedKeys = true,keyColumn = "id",keyProperty = "id")
void saveUser(User user);

@Options原码

image-20220926162936643

动态SQL

方式1:@SelectProvider

使用 @SelectProvider 注解,注解中的type 参数是提供构建 SQL 的类,method 是构建 SQL 的方法。构建 SQL 的方法的参数要和接口的参数一致,并且多个参数要使用命名参数。

接口:

 @SelectProvider(type= ProviderUtils.class,method = "queryUsersBySexOrUsernameSQL")
 List<User> queryUsersBySexOrUsername(@Param("sex") String sex,@Param("username") String username);

动态sql生成类

package com.itheima.sh.utils;
import org.apache.ibatis.annotations.Param;
public class ProviderUtils {
    public String queryUsersBySexOrUsernameSQL(@Param("sex") String sex, @Param("username") String username){
        String sql = "select * from tb_user  where sex = #{sex}";
        if (username!=null && !"".equals(username)){
            sql += " and user_name like concat('%',#{username},'%')";
        }
        return  sql;
    }
}
方式2:@SelectProvider和SQL类
	为了防止没有必要的空格引起的错误,mybatis提供了一个SQL类来实现sql语句拼接,在SQL类中将sql语句的关键字都变为方法名,并且每个方法返回值都是SQL类型,可以链式编程.
	借助mybatis提供的一个对象:SQL。
public class ProviderUtils {
    public String queryUsersBySexOrUsernameSQL2(@Param("sex") String sex, @Param("username") String username) {
        //借助mybatis提供的一个对象:SQL
        //创建SQL对象
       SQL sql = new SQL();
       //链式编程,每个方法返回值都是SQL类对象
       sql.SELECT("*").FROM("tb_user").WHERE("sex = #{sex}");
       //判断用户是否为空,不为空就继续链式编程,即继续拼接
       if(username != null && !"".equals(username)){
           sql.AND().WHERE("user_name like concat('%',#{username},'%')");
       }
       //SELECT * FROM user WHERE (sex=? AND user_name like concat('%',?,'%')) 
       //转换为字符串并返回
       return sql.toString();   
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值