MyBatis学习笔记(一)

一、介绍

(一)什么是框架及优势

框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。

简而言之,框架其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。简单说就是使用别人搭好的舞台,你来做表演。而且,框架一般是成熟的,不断升级的软件。

我们是由于效率和易用性的考虑使用框架。框架能节省开发时间。框架强制使用公共的约定,因此它能有效地解决一些共有的问题,它能让决定更连贯,避免我们写一大堆自定义模块来实现这些性能,框架节省了我们不少的时间和精力,并且让扩展变得更容易。

现实生活中容易理解的框架实例进行说明,比较毛坯房等。

(二)框架解决的问题

1.技术整合问题

框架要解决的最重要的一个问题是技术整合的问题,在 J2EE 的 框架中,有着各种各样的技术,不同的软件企业需要从 J2EE 中选择不同的技术,这就使得软件企业最终的应用依赖于这些技术,技术自身的复杂性和技术的风险性将会直接对应用造成冲击。而应用是软件企业的核心,是竞争力的关键所在,因此应该将应用自身的设计和具体的实现技术解耦。这样,软件企业的研发将集中在应用的设计上,而不是具体的技术实现,技术实现是应用的底层支撑,它不应该直接对应用产生影响。框架一般处在低层应用平台(如 J2EE )和高层业务逻辑之间的中间层。

2.三层架构

框架的重要性在于它实现了部分功能,并且能够很好的将低层应用平台和高层业务逻辑进行了缓和。为了实现软件工程中的"高内聚、低耦合"。把问题划分开来各个解决,易于控制,易于延展,易于分配资源。我们常见的MVC 软件设计思想就是很好的分层思想。

框架的重要性在于它实现了部分功能,并且能够很好的将低层应用平台和高层业务逻辑进行了缓和。为了实现软件工程中的"高内聚、低耦合"。把问题划分开来各个解决,易于控制,易于延展,易于分配资源。我们常见的MVC 软件设计思想就是很好的分层思想

三层架构就是为了符合"高内聚,低耦合"思想,把各个功能模块划分为表示层(UI)、业务逻辑层(BLL)和数据访问层(DAL)三层架构,各层之间采用接口相互访问,并通过对象模型的实体类(Model)作为数据传递的载体,不同的对象模型的实体类一般对应于数据库的不同表,实体类的属性与数据库表的字段名一致。"高内聚,低耦合",可以使开发人员分工更明确,将精力更专注于应用系统核心业务逻辑的分析、设计和开发,加快项目的进度,提高了开发效率,有利于项目的更新和维护工作。

分层后的优势:

1.避免了表示层直接访问数据访问层,表示层只和业务逻辑层有联系,提高了数据安全性。

2.有利于系统的分散开发,每一个层可以由不同的人员来开发,只要遵循接口标准,利用相同的对象模型实体类就可以了,这样就可以 大大提高系统的开发速度。

3.方便系统的移植,如果要把一个 C/S 的系统变成 B/S 系统,只要修改三层架构的表示层就可以了,业务逻辑层和数据访问层几乎 不用修改就可以轻松的把系统移植到网络上。

4.项目结构更清楚,分工更明确,有利于后期的维护和升级。

3.持久层技术解决方案

1.jdbc技术是jdk的原生API,提供对数据进行持久化底层技术,是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。

下面列出Jdbc技术中常用对象及含义:

DriverManager:负责加载各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接。

Driver:驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库连接。

Connection:数据库连接,负责与进行数据库间通讯,SQL执行以及事务处理都是在某个特定Connection环境中进行的。可以产生用以执行SQL的Statement。

Statement:用以执行SQL查询和更新(针对静态SQL语句和单次执行)。

PreparedStatement:用以执行包含动态参数的SQL查询和更新(在服务器端编译,允许重复执行以提高效率)。

  1. Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能,同时内置事务自动提交。

主要方法:

DbUtils类:启动类。

ResultSetHandler接口:转换类型接口。

MapListHandler类:实现类,把记录转化成List。

BeanListHandler类:实现类,把记录转化成List,使记录为JavaBean类型的对象。

Query Runner类:执行SQL语句的类。

QueryRunner进行查询的操作时handlers类型列表:

ArrayHandler :将ResultSet中第一行的数据转化成对象数组。

ArrayListHandler将ResultSet中所有的数据转化成List,List中存放的是Object[]。

BeanHandler :将ResultSet中第一行的数据转化成类对象。

BeanListHandler :将ResultSet中所有的数据转化成List,List中存放的是类对象。

ColumnListHandler :将ResultSet中某一列的数据存成List,List中存放的是Object对象。

KeyedHandler :将ResultSet中存成映射,key为某一列对应为Map。Map中存放的是数据。

MapHandler :将ResultSet中第一行的数据存成Map映射。

MapListHandler :将ResultSet中所有的数据存成List。List中存放的是Map。

ScalarHandler :将ResultSet中一条记录的其中的某一列的数据存成Object。

  1. jdbc做为持久层技术存在的问题
  1. 数据库连接创建、释放频繁造成系统资源浪费,从而影响系统性能。

  2. Sql语句在代码中硬编码,造成代码不易维护,实际应用中sql变化的可能较大,sql变动需要改变java代码。

  3. 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。

  4. 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对(javaBean)解析比较方便。

  5. 代码冗余太大,sql与Java程序耦合太紧密,不利于sql维护。

(三)常见框架

  1. 持久化框架
  1. 持久化框架Mybatis

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了googlecode,并且改名为MyBatis 。2013年11月迁移到Github。

iBATIS一词来源于"internet"和"abatis"的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和 Data Access Objects(DAOs)。

  1. 持久化框架Hibernate

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数库。Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JavaEE架构中取代CMP,完成数据持久化的重任。但是,这个框架因为各种原因目前在国内的流行 程度下降太多,现在公司开发也越来越少使用。目前使用 Spring Data 来实现数据持久化也是一种趋势。

  1. Web层MVC框架

Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能MVC模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts 2(一般老项目使用)等等。

  1. 技术整合框架

Spring是Java EE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002 年最早提出并随后创建,是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。Spring是一个开源容器框架,它集成各类型的工具,通过核心的Bean factory实现了底层的类的实例化和生命周期的管理。在整个框架中,各类型的功能被抽象成一个个的 Bean,这样就可以实现各种功能的管理,包括动态加载和切面编程。

二、Mybatis概述

mybatis是一个优秀的基于 java 的持久层框架,它内部封装了jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。

mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql 的动态参数进行映射生成最终执行的sql语句,最后由 mybatis 框架执行sql并将结果映射为 java 对象并返回。

采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。

mybatis的特点:

(1) 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

(2) 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。

(3) 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

(4)提供映射标签,支持对象与数据库的orm字段关系映射

(5) 提供对象关系映射标签,支持对象关系组建维护

(6) 提供xml标签,支持编写动态sql。

(7) 支持数据缓存和注解开发。

mybatis的官方网址: https://www.mybatis.org/

三、Mybatis环境搭建

(一)前期准备

1. 创建数据库
create table user(
id int not null auto_increment,
name varchar(30),
gender varchar(6),
age int,
birthday date,
primary key(id)
);
2. 创建maven Java项目

打开Idear-->选择File-->选择new-->选择project

3. 导入依赖
XML 复制代码
<dependencies>

    <dependency>

        <groupId>org.mybatis</groupId>

        <artifactId>mybatis</artifactId>

        <version>3.5.11</version>

    </dependency>

    <dependency>

        <groupId>mysql</groupId>

        <artifactId>mysql-connector-java</artifactId>

        <version>8.0.33</version>

        <scope>runtime</scope>

    </dependency>

    <dependency>

        <groupId>junit</groupId>

        <artifactId>junit</artifactId>

        <version>4.13.2</version>

        <scope>test</scope>

    </dependency>

    <dependency>

        <groupId>org.apache.logging.log4j</groupId>

        <artifactId>log4j-api</artifactId>

        <version>2.20.0</version>

    </dependency>

    <dependency>

        <groupId>org.apache.logging.log4j</groupId>

        <artifactId>log4j-core</artifactId>

        <version>2.20.0</version>

    </dependency>

</dependencies>

(二)搭建项目环境

1. 编写User实体类
2. 编写持久层User接口
java 复制代码
public interface UserDao {
//查询所有User对象
    public List<User> queryAllUsers();
}
3. 编写持久层User接口映射文件UserDao.xml
XML 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jn.dao.UserDao">


    <!--配置查询的sql语句-->
   <select id="queryAllUsers" resultType="com.jn.entity.User">
       select * from users
   </select>
</mapper>
4. 编写mybatis主配置文件SqlMapperConfig.xml
XML 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置mybatis的环境-->
<environments default="development">
    <!--配置环境-->
    <environment id="development">
        <!--配置事物的类型-->
        <transactionManager type="JDBC"></transactionManager>
        <!--配置连接数据库的信息:用的是数据源【连接池】-->
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatisdatabase?rewriteBatchedStatements=true "/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>
    <mappers>
        <mapper resource="com/jn/dao/UserDao.xml"/>
    </mappers>
</configuration>

四、Mybatist入门案例

(一)入门案例编写

1.编写工具类读取配置文件

利用Mybatis对数据库进行操作时,都需要产生Mybatis总配置文件的输入流对象、构建SqlSessionFactoryBuilder对象、调用build方法进产生SqlSessionFactory工厂对象、调用工厂类openSession()方法创建SqlSession对象,代码冗余量大,步聚颇有繁琐,所以我们对产生SqlSession对象利用工具类进行优化。

java 复制代码
package com.jn.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisUtils {

    //定义静态变量sqlSessionFactory
    private static SqlSessionFactory sqlSessionFactory;

    //创建静态块,当MyBatisUtils类被加载时,自动执行该静态块,初始化数据
    static {
        //获取mybatis主配置文件SqlMapperConfig.xml的输入流
        try {
            InputStream inputStream = Resources.getResourceAsStream("SqlMapperConfig.xml");
            //创建SqlSessionFactoryBuilder构造对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //调用build方法返回SqlSessionFactory工厂对象
            sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("初始化数据失败" + e.getMessage());
        }
    }

    //创建getSqlSessionFactory方法返回SqlSessionFactory对象
    public static SqlSessionFactory getSqlSessionFactory(){
         return sqlSessionFactory;
    }

    //创建一个SqlSession对象并返回
    public static SqlSession getSession(){
        return sqlSessionFactory.openSession();
    }

    //关闭SqlSession方法
    public static void close( SqlSession session){
        if(session!=null){
            session.close();
        }
    }
}
2.使用代理对象操作数据库
java 复制代码
package com.jn.test;

import com.jn.dao.UserDao;
import com.jn.entity.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.io.InputStream;
import java.util.List;

public class UserTest {
    //查询数据库里面的所有数据
@Test
public void testQueryAllUsers() throws Exception{
    //获取SqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSession();

    //调用SqlSession创建UserDao接口的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);

    //调用代理对象的queryAllUsers方法查询所有的User
    List<User> users = userDao.queryAllUsers();
    for (User user : users) {
        System.out.println(user);
    }

    //释放资源
    sqlSession.close();
}

}
3.出现错误

错误信息显示UserDao的路径不正确

但是查看路径名称以后没问题

然后修改错误,在pom.xml文件里面添加过滤器

XML 复制代码
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

然后运行代码查询数据库,得到结果

五、Mybatis执行原理分析

(一)配置文件分析

  1. 核心配置文件
XML 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置mybatis的环境-->
<environments default="development">
    <!--配置环境-->
    <environment id="development">
        <!--配置事物的类型-->
        <transactionManager type="JDBC"></transactionManager>
        <!--配置连接数据库的信息:用的是数据源【连接池】-->
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatisdatabase?rewriteBatchedStatements=true "/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>
    <mappers>
        <mapper resource="com/jn/dao/UserDao.xml"/>
    </mappers>
</configuration>

核心配置文件参数详解:

(1)environments标签中的id属性值必须和environments标签中的default属性一致。

(2)事务管理器:

第一种采用JDBC事务类型,直接使用了 JDBC的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。

第二种采用MANAGED事务类型,它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:

XML 复制代码
<transactionManager type="MANAGED">
       <property name="closeConnection" value="false"/>
    </transactionManager>

(3) 数据源(dataSource)

dataSource元素使用标准的JDBC数据源接口来配置JDBC连接对象的资源。大多数 MyBatis 应用程序会按示例中的例子来配置数据源。 虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")

UNPOOLED-- 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。

POOLED-- 这种数据源的实现利用"池"的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

JNDI -- 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的数据源引用。

(5) mapper中的resource属性用于指定映射文件的位置。mapper中有多个属性可以描述映射文件,分别为:

XML 复制代码
  <mappers>
        <mapper resource="com/jn/dao/UserDao.xml"/>
    </mappers>

    文件在多级目录中要用分隔符"/"隔开。
  <mapper namespace="com.jn.dao.UserDao">

指定UserDao接口文件位置,但此时UserDao.xml必须和接口处在同一个包中并且文件名要相同。

  1. 映射文件
XML 复制代码
<mapper namespace="com.jn.dao.UserDao">
    <!--配置查询的sql语句-->
   <select id="queryAllUsers" resultType="com.jn.entity.User">
       select * from users
   </select>
    </mapper>

映射文件参数详解:

(1) namespace必须为接口的完全限定名(即包名+类名的格式)。

(2) select标签中的id必须和接口中声明的方法同名。

(3) 如果接口中方法有返回值,resyultType必须跟方法返回值一致并采用返回值的完全限定名来表示。

(二)底层源码分析

1.利用Resources的getResourceAsStream方法读取mybatis核心配置文件,该配置文件中注册数据源【dataSource】和映射文件的位置【mappers标签中的mapper子标签的resource属性】。

(三)执行原理图

六、MyBatis基于代理Dao实现CRUD操作

(一) 添加数据

1. 定义数据添加的接口方法addUser
java 复制代码
//添加数据
public void addUser(User user);
2. 配置文件中配置对应的sql
XML 复制代码
<!--向数据库表中添加数据    -->
<insert id="addUser" parameterType="com.jn.entity.User">
    insert into users(name,age,gender,birthday)
    values(#{name},#{age},#{gender},#{birthday})
</insert>

#{}为mybatis的占位符,如果方法中传递的参数为实体类类型,#{实体类属性}

3. 添加测试方法
java 复制代码
//向数据库表里面添加一条数据
@Test
public void addUser() throws Exception{
    //获取SqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSession();
    //调用SalSession创建UserDao接口的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);

    User user = new User("杨逍",32,"男",new Date());
    userDao.addUser(user);

    //提交事物
    sqlSession.commit();

    //关闭连接
    MyBatisUtils.close(sqlSession);

    System.out.println("添加成功");
}
4.测试结果

(二) 数据更新

1. 定义数据更新的接口方法updateUser
java 复制代码
//更新数据
public void updataUser(User user);
2. 配置文件中配置对应的sql
XML 复制代码
<!--更新数据库表中的数据-->
<update id="updataUser" parameterType="com.jn.entity.User">
    update users set name=#{name},age=#{age},gender=#{gender},birthday=#{birthday} where id=#{id}
</update>
3.添加测试方法
java 复制代码
//update
@Test
public void updateUser() throws Exception{
    //获取SqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSession();
    //调用SqlSession创建UserDao接口的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);

    User user = new User("范瑶",28,"男",new Date());
    user.setId("10");
    userDao.updataUser(user);

    //提交事物
    sqlSession.commit();

    //close
    MyBatisUtils.close(sqlSession);

    System.out.println("更新数据成功");

}
4.测试结果:

(三) 数据删除

1.定义数据删除的接口方法deleteUser
java 复制代码
//删除数据
public void deleteUser(String id);
2. 配置文件中配置对应的sql
XML 复制代码
<!--删除数据库表中的数据-->
<delete id="deleteUser" parameterType="String">
    delete from users where id=#{id}
</delete>
3. 添加测试方法
java 复制代码
//delete
@Test
public void deleteUser() throws Exception{
    //获取SqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSession();
    //用SqlSession创建UserDao接口的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);

    userDao.deleteUser("11");

    //提交
    sqlSession.commit();

    //关闭连接
    sqlSession.close();
    System.out.println("删除成功");
}
4.测试结果

(四) #{}和${}的区别

1.#{}是预编译处理,${}是字符串替换。

2.Mybatis在处理{}时,就是把{}替换成变量的值。

3.Mybatis在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值。

4.使用#{}可以有效的防止 SQL 注入,提高系统安全性。

演示:
java 复制代码
//查询一条数据
public List<User> getQueryByName(User user);
XML 复制代码
<!--查询一条数据-->
<select id="getQueryByName" parameterType="com.jn.entity.User" resultType="com.jn.entity.User">
    select * from users where name = '${name}'
</select>
java 复制代码
//查询一条数据
@Test
public void getUserByName() throws Exception{
    //获取SqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSession();
    //用SqlSession创建UserDao接口的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);

    User user = new User();
    user.setName("张无忌");
    List<User> userList = userDao.getQueryByName(user);
    userList.forEach(System.out::println);

    //关闭连接
    sqlSession.close();
}

如果不加''就会报错。如图:

(五) MyBatis实现模糊查询

模糊查询在我们开发中是一项必不可缺少的重要内容。对于我们mybatis实现模糊查询有三种方式,以下具体的实现步聚:

1.添加模糊查询的接口方法likeSearchUsers

java 复制代码
//模糊查询
public List<User> likeSearchUsers(String name);

2.配置接口对应的sql文件

XML 复制代码
<!--模糊查询-->
<select id="likeSearchUsers" parameterType="String" resultType="com.jn.entity.User">
    <!--方式1:配置占位符的方式-->
            <!--select * from users where name like #{name}-->

    <!--方式2:配置拼接字符串-->
            <!--select * from users where name like '${name}%'-->

    <!--方式3:配置函数查询-->
    select * from users where name like concat('',#{name},'%')
</select>

3.模糊查询测试

java 复制代码
//模糊查询
@Test
public void likeQueryUsers() throws Exception{
    //获取SqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSession();
    //用SqlSession对象创建UserDao接口的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);

    //配置占位符的方式得到查询数据的配置代码
    //List<User> userList = userDao.likeSearchUsers("张%");

    List<User> userList = userDao.likeSearchUsers("张");
    for (User user : userList) {
        System.out.println(user);
    }

    //关闭连接
    sqlSession.close();
}

(六) MyBatis的参数处理

1. parameterType 配置参数
1) 参数的使用说明

上一章节中已经介绍了SQL语句传参,使用标签的 parameterType 属性来设定。该属性的取值可以是基本类型,引用类型(例如:String 类型),还可以是实体类类型(POJO 类)。同时也可以使用实体类的包装类,本章节将介绍如何使用实体类的包装类作为参数传递。

2) 参数配置的注意事项

基本类型和String可以直接写类型名称也可以使用包名.类名的方式,例如:java.lang.String。

实体类类型,目前我们只能使用全限定类名。

究其原因,是mybaits在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,而我们的是实体类并没有注册别名,所以必须写全限定类名。在今天课程的最后一个章节中将讲解如何注册实体类的别名。

mybatis 的官方文档的说明请参考下面表格数据。

|------------|------------|
| 别名 | 映射的类型 |
| _byte | byte |
| _long | long |
| _short | short |
| _int | int |
| _integer | int |
| _double | double |
| _float | float |
| _boolean | boolean |
| string | String |
| byte | Byte |
| long | Long |
| short | Short |
| int | Integer |
| integer | Integer |
| double | Double |
| float | Float |
| boolean | Boolean |
| date | Date |
| decimal | BigDecimal |
| bigdecimal | BigDecimal |
| object | Object |
| map | Map |
| hashmap | HashMap |
| list | Listg |
| arraylist | ArrayList |
| collection | Collection |
| iterator | Iterator |

这些都是支持的默认别名。我们也可以从源码角度来看它们分别都是如何定义出来的。可以参考 TypeAliasRegistery.class 的源码

2. 传递 pojo 包装对象

开发中通过 pojo 传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数,Pojo 类中包含 pojo。

需求:根据用户名查询用户信息,查询条件放到 QueryVo 的 user 属性中。

1) 编写QueryVo
java 复制代码
package com.jn.entity;

public class QueryVo {
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
2) 编写持久层接口UserMapper
java 复制代码
public interface UserMapper {
    public List<User> getUserByName(QueryVo queryVo);
}
3) 配置接口方法对应的sql文件
XML 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jn.dao.UserMapper">
   
    <!--使用包装类对象的方法实现查询一条数据-->
    <select id="getUserByName" parameterType="com.jn.entity.QueryVo" resultType="com.jn.entity.User">
        select * from users where name like #{user.name}
    </select>
</mapper>
4) 注册QueryVo
XML 复制代码
<mapper resource="com/jn/dao/UserMapper.xml"/>
5) 测试QueryVo对象作为参数
java 复制代码
//QueryVo作为参数的模糊查询测试
@Test
public void testQueryVo() throws Exception{
    SqlSession sqlSession = MyBatisUtils.getSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    //创建User对象
    User user = new User();
    user.setName("张%");

    QueryVo queryVo = new QueryVo();
    queryVo.setUser(user);

    List<User> userList = userMapper.getUserByName(queryVo);
    for (User user1 : userList) {
        System.out.println(user1);
    }

    //关闭连接
    sqlSession.close();
}
6)测试结果
3. map集合数据作为参数的处理方式
1) 添加接口方法参数使用map集合
java 复制代码
//根据map字段来查询精确数据
public List<User> getUserByNameAhdAge(Map<String, Object> map);
2) 配置接口对应的sql配置
XML 复制代码
<!--使用包装类对象的方法查询适合Map条件的数据。#{}中参数必须和Map集合中的key保存一致,表示取Map集合中指定key的值-->
<select id="getUserByNameAhdAge" parameterType="Map" resultType="com.jn.entity.User">
    select * from users where name=#{name} and age = #{age}
</select>
3) 测试map集合作为参数
java 复制代码
//测试map集合作为参数
@Test
public void testMapParameter() throws Exception{
    SqlSession sqlSession = MyBatisUtils.getSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    //向map中存入数据
    Map<String,Object> map = new HashMap<String, Object>();

    map.put("name","张三丰");
    map.put("age","300");

    List<User> userList = userMapper.getUserByNameAhdAge(map);
    for (User user1 : userList) {
        System.out.println(user1);
    }
    //关闭连接
    sqlSession.close();
}
4)结果测试
4. @Param方式解决多参数处理
1)@Param注解的介绍

@Param注解用于给方法内的参数取别名,当方法中拥有多个参数时,我们无法一次性将这些参数进行传递,尤其多个参数具有不同的数据类型时无法传递,所以我们利用@Param给每个方法中的参数取一个别名,在映射文件中使用别名进行取值。

2) 添加接口方法参数使用Param注解
java 复制代码
//使用Param注解设置别名来查询精确数据
public List<User> getUserByGenderAndName(@Param("gen") String gender, @Param("name") String name);
3) 配置接口对应的sql配置
XML 复制代码
<!--使用包装类对象的方法查询适合Param注解条件的数据。#{}中参数必须和注解里面的别名保持一致-->
<select id="getUserByGenderAndName" resultType="com.jn.entity.User">
    select * from users where gender = #{gen} and name=#{name}
</select>
4) 测试注解方法
java 复制代码
//测试Param注解根据别名作为参数的查询
@Test
public void testParamQuery() throws Exception{
    SqlSession sqlSession = MyBatisUtils.getSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   
    List<User> userList = userMapper.getUserByGenderAndName("男","张无忌");
    for (User user1 : userList) {
        System.out.println(user1);
    }
    //关闭连接
    sqlSession.close();
}
5)测试结果

(七) MyBatis查询结果封装

1. resultType结果类型
1) resultType属性介绍

resultType 属性可以指定结果集的类型,它支持基本类型和实体类类型。我们在前面的 CRUD 案例中已经对此属性进行过应用了。需要注意的是,它和 parameterType 一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须使用全限定类名。例如:我们的实体类此时必须是全限定类名(今天最后一个章节会讲解如何配置实体类的别名)同时,当是实体类名称是,还有一个要求,实体类中的属性名称必须和查询语句中的列名保持一致,否则无法实现封装。

2) resultType属性的使用
[1]基本类型
编写dao接口方法getTotalRecords参数是基本类型
java 复制代码
//查询所有记录
public int getTotalRecords();
配置接口方法对应的sql语句
XML 复制代码
<!--查询所有记录数-->
<select id="getTotalRecords" resultType="int">
    select count(*) from users
</select>
测试查询结果
java 复制代码
//查询所有结果
@Test
public void testgetTotalRecords() throws Exception{
    SqlSession sqlSession = MyBatisUtils.getSession();
    UserDao userDao = sqlSession.getMapper(UserDao.class);

    int total = userDao.getTotalRecords();
    System.out.println(total);
    //关闭连接
    sqlSession.close();
}
[2] 实体类型
编写dao接口方法queryAllUsers参数是对象类型
java 复制代码
//查询所有User对象
public List<User> queryAllUsers();
配置接口方法对应的sql语句
XML 复制代码
<!--配置查询的sql语句-->
<select id="queryAllUsers" resultType="com.jn.entity.User">
    select * from users
</select>
测试查询结果
java 复制代码
//查询数据库里面的所有数据
@Test
public void testQueryAllUsers() throws Exception{
    //获取SqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSession();

    //调用SqlSession创建UserDao接口的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);

    //调用代理对象的queryAllUsers方法查询所有的User
    List<User> users = userDao.queryAllUsers();
    for (User user : users) {
        System.out.println(user);
    }

    //释放资源
    sqlSession.close();
}
3) 特殊情况
修改属性值、get和set、toString方法

如果个修改了实体类User中的id属性值,比如修改成了userId,此时查询出的结果没有把表中的id值映射到userId属性中,因为属性和表中的列名不一致,内部无法用反射技术进行映射,所以为空。

java 复制代码
package com.jn.entity;

import javax.xml.crypto.Data;
import java.util.Date;

public class User {
    private   String userId;
    private   String name;
    private   int age;
    private   String gender;
    private Date birthday;

    public User() {
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Date getBirthday() {
        return birthday;
    }

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

    public User(String name, int age, String gender, Date birthday) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id='" + userId + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}
测试代码以及结果
java 复制代码
//查询数据库里面的所有数据
@Test
public void testQueryAllUsers() throws Exception{
    //获取SqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSession();

    //调用SqlSession创建UserDao接口的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);

    //调用代理对象的queryAllUsers方法查询所有的User
    List<User> users = userDao.queryAllUsers();
    for (User user : users) {
        System.out.println(user);
    }

    //释放资源
    sqlSession.close();
}
问题分析

经过上述测试结果发现userId属性为null,没有被赋上值。

解决方案:修改映射配置,采用别名设置,让结果集中的列与实体类中的属性对应。

2. resultMap自定义结果类型
1) resultMap标签介绍

resultMap 标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。在 select 标签中使用 resultMap 属性指定引用即可。同时 resultMap 可以实现将查询结果映射为复杂类型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。

2) 定义接口方法getUserById
java 复制代码
public User getUserById(String id);
3) 在sql的配置文件中定义resultMap
XML 复制代码
<select id="getUserById" parameterType="String" resultMap="UserResultMap">
  select * from users where userId=#{id}
</select>

此处我们使用resultMap而不是resultType, resultType是直接写结果类型,resultMap是映射结果集与类中属性对应关系。

resultMap标签中的id表示一个唯一标记是resultMap的名称。

type: 表示该resultMap返回的类型。

4) 使用定义的resultMap配置查询的sql语句
java 复制代码
<resultMap id="UserResultMap" type="com.jn.entity.User">
<id column="id" property="userId"></id>
<result column="name" property="userName"></result>
<result column="gender" property="gender"></result>
<result column="age" property="age"></result>
<result column="birthday" property="userBirthday"></result>
</resultMap>

id 标签:用于指定主键字段

result 标签:用于指定非主键字段

column 属性:用于指定数据库列名

property 属性:用于指定实体类属性名称

5) 测试查询结果
java 复制代码
@Test
public void testGetUserById() throws  Exception{
//获取SqlSession对象
SqlSession session = MyBatisUtils.getSession();
//调用SqlSession 创建 UserDao接口的代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
//查询user
User user = userMapper.getUserById("1");
//打印user
System.out.println(user);
//关闭连接
MyBatisUtils.close(session);
}

(八) SqlMapConfig.xml配置文件内容解析

在主配置SqlMapConfig.xml中,定义了很多标签,我们现在只是使用了一部分标签 ,主配置文件中可以出现的标签 用dtd文 件进行约束。下面介绍其它标签的使用含义。

1.标签的配置规范,查看dtd规范文件
XML 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!--

       Copyright 2009-2022 the original author or authors.

       Licensed under the Apache License, Version 2.0 (the "License");
       you may not use this file except in compliance with the License.
       You may obtain a copy of the License at

          https://www.apache.org/licenses/LICENSE-2.0

       Unless required by applicable law or agreed to in writing, software
       distributed under the License is distributed on an "AS IS" BASIS,
       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       See the License for the specific language governing permissions and
       limitations under the License.

-->
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>

<!ELEMENT databaseIdProvider (property*)>
<!ATTLIST databaseIdProvider
type CDATA #REQUIRED
>

<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
>

<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>

<!ELEMENT settings (setting+)>

<!ELEMENT setting EMPTY>
<!ATTLIST setting
name CDATA #REQUIRED
value CDATA #REQUIRED
>

<!ELEMENT typeAliases (typeAlias*,package*)>

<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
type CDATA #REQUIRED
alias CDATA #IMPLIED
>

<!ELEMENT typeHandlers (typeHandler*,package*)>

<!ELEMENT typeHandler EMPTY>
<!ATTLIST typeHandler
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
handler CDATA #REQUIRED
>

<!ELEMENT objectFactory (property*)>
<!ATTLIST objectFactory
type CDATA #REQUIRED
>

<!ELEMENT objectWrapperFactory EMPTY>
<!ATTLIST objectWrapperFactory
type CDATA #REQUIRED
>

<!ELEMENT reflectorFactory EMPTY>
<!ATTLIST reflectorFactory
type CDATA #REQUIRED
>

<!ELEMENT plugins (plugin+)>

<!ELEMENT plugin (property*)>
<!ATTLIST plugin
interceptor CDATA #REQUIRED
>

<!ELEMENT environments (environment+)>
<!ATTLIST environments
default CDATA #REQUIRED
>

<!ELEMENT environment (transactionManager,dataSource)>
<!ATTLIST environment
id CDATA #REQUIRED
>

<!ELEMENT transactionManager (property*)>
<!ATTLIST transactionManager
type CDATA #REQUIRED
>

<!ELEMENT dataSource (property*)>
<!ATTLIST dataSource
type CDATA #REQUIRED
>

<!ELEMENT mappers (mapper*,package*)>

<!ELEMENT mapper EMPTY>
<!ATTLIST mapper
resource CDATA #IMPLIED
url CDATA #IMPLIED
class CDATA #IMPLIED
>

<!ELEMENT package EMPTY>
<!ATTLIST package
name CDATA #REQUIRED
>
2. properties标签详解

在使用 properties 标签配置时,在xml中可以引用properties属性文件中key的值,日后修改properties属性文 件中key的值时,不用修改xml文件,从而提高了效率。

1) 新建一个dbconfig.properties属性文件
XML 复制代码
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatisdatabase?rewriteBatchedStatements=true
username=root
password=123456
2) 在SqlMapperConfig.xml中可以引用属性文件中key的值
XML 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

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

<!--配置mybatis的环境-->
<environments default="development">
    <!--配置环境-->
    <environment id="development">
        <!--配置事物的类型-->
        <transactionManager type="JDBC"></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>
    <mappers>
        <mapper resource="com/jn/dao/UserDao.xml"/>
        <mapper resource="com/jn/dao/UserMapper.xml"/>
    </mappers>
</configuration>

然后测试一切正常

3. typeAliases标签详解

在前面我们讲的 Mybatis 支持的默认别名,我们也可以采用自定义别名方式来开发。

1) 在SqlMapperConfig.xml中定义别名
XML 复制代码
<!--配置多个实体类的别名封装-->
    <typeAliases>
        <!--
        单个定义别名,区分大小写
        <typeAlias type="com.jn.entity.User" alias="User"></typeAlias>
        -->
   
        <!--批量定义别名,扫描整个包下面的类,别名为类名,不区分大小写-->
        <package name="com.jn.entity"/>
    </typeAliases>
2) 在userMapper.xml中使用别名

改变上述一种标签的返回方式为User,然后进行该方法的测试

3)测试结果
4. mappers映射器标签详解

mappers映射器用于指定映射文件的位置。

XML 复制代码
 <mappers>
<!--        <mapper resource="com/jn/dao/UserDao.xml"/>-->
<!--        <mapper resource="com/jn/dao/UserMapper.xml"/>-->

        <!--注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。-->
        <mapper class="com.jn.dao.UserDao"></mapper>
        <mapper class="com.jn.dao.UserMapper"></mapper>

<!--在有很多接口的时候也可以这样设置
            <package name="com.jn.dao"/>
    -->

    </mappers>

(九) MyBatis自动返回主键值

1. 返回主键值技术的业务应用场景

对于自增主键在某些业务中保存一个对象后,需要使用到这个主键完成后续的业务逻辑,比如:要保存订单后,还要保存订单项信息,订单项相信需要用到订单主键。所以应用场合很多,下面我们来应用一下返回主键值操作。

2. 接口中编写saveUser方法
java 复制代码
    //保存数据,返回主键值
    public void saveUser(User user);
3. 在sql的配置文件中添加接口方法对应的sql配置
第一种方式
XML 复制代码
    <!--保存一条数据,然后返回主键值-->
    <insert id="saveUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        insert into users(name,age,gender,birthday)
        values(#{name},#{age},#{gender},#{birthday})
    </insert>

useGeneratedKeys: 表示开启获取自增主键值。

keyProperty: 表示从表中取到主键值后赋给User类中的哪个属性。

第二种方式

使用selectKey标签和mysql内置函数

XML 复制代码
    <insert id="saveUser" parameterType="User">
        <selectKey resultType="String" keyProperty="id" order="AFTER">
            select last_insert_id()
        </selectKey>
        insert into users(name,age,gender,birthday)
        values(#{name},#{age},#{gender},#{birthday})
    </insert>

keyColumn:指定取数据库中哪一列的值(通常指主键列)。

keyProperty: 表示取出主键值后赋值User对象的哪个属性。

resultType: 表示对象的属性类型 order:表示完后sql语句之前还是之后把主键值赋给实体类对应属性。

4.测试
java 复制代码
    //添加一条数据,然后返回主键
    @Test
    public void testSaveUser() throws Exception{
        SqlSession sqlSession = MyBatisUtils.getSession();
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        User user = new User("小龙女",28,"女",new Date());
        userDao.saveUser(user);
        //打印主键
        System.out.println(user.getId());
        //提交事物
        sqlSession.commit();
        //关闭连接
        MyBatisUtils.close(sqlSession);
    }

(十)sql片段

1. sql的配置文件中出现sql冗余

在开发中,SQL的拼接很常见,有很多对拼接的sql具有重复性高的特点,有sql冗余,不仅不美观还导致映射文件配置臃肿,这时最好把重复的sql抽取出来,作为公用的sql片段,尤其在动态sql中应用中更加显著,提高可重用性。

2. 定义sql片段
XML 复制代码
<!--使用sql标签定义一个sql片段-->
<sql id="baseColumn ">
    id,name,gender,age
</sql>
3. 使用sql片段
XML 复制代码
<select id="getUsers" resultType="User">
  select <include refid= "baseColumn"></include> 
  
    from user
</select>

七、动态sql语句

(一) 动态sql介绍

1. 动态sql的概念

顾名思义,SQL 是动态拼接成的,根据传入的变量值进行逻辑操作并动态拼接,方便实现多条件下的数据库操作。在业务逻辑复杂,即简单 SQL 无法完成时,需要拼接时就要使用动态 SQL

2. 动态sql解决的问题

动态sql 主要解决根据条件判断附加条动态 sql 主要解决多条件变化查询,实现自动判断记录字段是否需要更新,根据条件判断附加条 sql 条件,实现批量添加数据、批量修改数据、批量修删除数据等,优化 sql 语句,提高执行效率。

例如:在下面表单进行提交查询结果的时候,查询的条件随机

(二) 构建测试环境

1. 创建maven项目
2.编写框架配置文件和sql映射文件

(三) where标签

1.where标签简介

where 标签用于代替 sql 中的 where 关键字,可以根据条件判断是否附加 where 关键字。如果 where 标签中有条件成立就会附加 where 关键字,如果没有成立的条件就不会附加 where 关键字 . 可以去掉离他最近一个无关的 and or 关键字 .where 标签的书写格式为 <where> 添写附加条件 </where>

2.where标签使用
1)编写接口方法findByUser
java 复制代码
    // 根据用户名查询用户
    public List<User> findByUser(User user);
2) 使用where标签进行sql处理
XML 复制代码
<!--根据name和age查询用户-->
<select id="findByUser" parameterType="com.jn.entity.User" resultType="com.jn.entity.User">
    select * from users
    <where>
        and  name = #{name} and age = #{age}
    </where>
</select>
3) 测试findByUser方法
java 复制代码
 //查询符合where标签的数据
    @Test
    public void testFindByUser() throws  Exception{
        //获取session对象
        SqlSession sqlSession = MyBatisUtils.getSession();
        //使用sqlSession获取UserDao的代理对象
        UserDao  userDao = sqlSession.getMapper(UserDao.class);
        User user = new User();
        user.setName("张无忌");
        user.setAge(33);
        List<User> userList = userDao.findByUser(user);
        for (User user1 : userList) {
            System.out.println(user1);
        }
        sqlSession.close();
    }

(四) if标签标签的应用

1. if标签简介

if 标签表示逻辑条件判断,如果条件成立就附加 <if></if> 之间的 sql 语句 , 如果条件不成立就不附加 <if></if> 之间的 sql 语句。书写格式为 :<if test=" 表达式 ">sql 语句 </if>

2. if标签使用
1) 编写接口方法findUsersByCondition
java 复制代码
    // 根据用户名查询用户
    public List<User> findIfByUser(User user);
2) 使用if标签进行sql处理
XML 复制代码
<!--有if条件的查询-->
    <select id="findIfByUser" parameterType="User" resultType="User">
        select * from users
        <trim prefix="where" prefixOverrides="and|or">
            <if test="name!=null">
                name=#{name}
            </if>
            <if test="gender!=null">
                and gender=#{gender}
            </if>
            <if test="age!=null">
                and age=#{age}
            </if>
        </trim>
    </select>
3) 测试findUsersByCondition方法
java 复制代码
    //查询符合where If标签的数据
    @Test
    public void testIf() throws  Exception{
        //获取session对象
        SqlSession sqlSession = MyBatisUtils.getSession();
        //使用sqlSession获取UserDao的代理对象
        UserDao  userDao = sqlSession.getMapper(UserDao.class);
        User user = new User();
        //user.setName("张无忌");
        user.setAge(28);
        List<User> userList = userDao.findIfByUser(user);
        for (User user1 : userList) {
            System.out.println(user1);
        }
        sqlSession.close();
    }

通过产生的sql 语句可以看出,当 if 标签中 test 属性表达式为 true 时,就会附加 if 标签之间的条件。
注意: < if*>* 标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。

(五) set标签的应用

1. set标签简介

set 标签用于更新语句中,代替 set 关键字,可以有效对指定字段进行更新,提升 sql 的执行效率。,当 set 标签中有条件成立时就会附加 set 标签, set 标签会去除无关的逗号。 set 标签中一般嵌套 if 标签进行使用其格式为
<set>
<if test="name">
name=#{name},
</if>
<if test="age">
age=#{age},
</if>
......
</set>

2. set标签使用
1) 编写接口方法updateUser
java 复制代码
    //根据ID更新数据
    public void updateById(User user);
2) 使用set标签进行sql处理
XML 复制代码
<!--set标签的使用,更新数据-->
    <select id="updateById" parameterType="User" resultType="User">
        update users
        <set>
            <if test="name!=null">
                name=#{name},
            </if>
            <if test="gender!=null">
                gender=#{gender},
            </if>
            <if test="age!=null">
                age=#{age},
            </if>
            <if test="birthday!=null">
                birthday=#{birthday}
            </if>
        </set>
        <where>
            id = #{id}
        </where>
    </select>
3) 测试updateUser方法
java 复制代码
    //更新符合where set标签的数据
    @Test
    public void testSet() throws  Exception{
        //获取session对象
        SqlSession sqlSession = MyBatisUtils.getSession();
        //使用sqlSession获取UserDao的代理对象
        UserDao  userDao = sqlSession.getMapper(UserDao.class);
        User user = new User();

        user.setId("15");
        user.setName("李莫愁");
        user.setAge(27);
        user.setGender("女");
        userDao.updateById(user);

        //提交
        sqlSession.commit();
        sqlSession.close();
    }


通过产生的 sql 语句可以看出,当 set 标签中有条件成立时就会附加 set 关键字,字段为 null 时该列不会被更新。 set 可以忽略与 sql 无关的逗号。

(六) trim标签

1. trim标签简介

trim 标签为万能标签,可用于 set where 等。

prefix 表示要附加的前缀关键字

suffix 表示要附加的后缀关键字

prefixOverrides 表示要忽略前置字符

suffixOverrides 表示要忽略后置字符

2. trim标签使用
1) 修改where标签和set标签

1) 应用于 where

XML 复制代码
<!--有if条件的查询-->
    <select id="findIfByUser" parameterType="User" resultType="User">
        select * from users
        <trim prefix="where" prefixOverrides="and|or">
            <if test="name!=null">
                name=#{name}
            </if>
            <if test="gender!=null">
                and gender=#{gender}
            </if>
            <if test="age!=null">
                and age=#{age}
            </if>
        </trim>
    </select>

2) 用于 set 标签

XML 复制代码
<!--set标签的使用,更新数据-->
    <select id="updateById" parameterType="User" resultType="User">
        update users
        <trim prefix="set" suffixOverrides=",">
            <if test="name!=null">
            name=#{name},
            </if>
            <if test="gender!=null">
                gender=#{gender},
            </if>
            <if test="age!=null">
                age=#{age},
            </if>
            <if test="birthday!=null">
                birthday=#{birthday},
            </if></trim>
        <where>
            id = #{id}
        </where>
    </select>
2) 测试方法测试updateUser和findUsersByCondition方法
java 复制代码
    //查询符合where If标签的数据
    @Test
    public void testIf() throws  Exception{
        //获取session对象
        SqlSession sqlSession = MyBatisUtils.getSession();
        //使用sqlSession获取UserDao的代理对象
        UserDao  userDao = sqlSession.getMapper(UserDao.class);
        User user = new User();
        //user.setName("张无忌");
        user.setAge(28);
        List<User> userList = userDao.findIfByUser(user);
        for (User user1 : userList) {
            System.out.println(user1);
        }
        sqlSession.close();
    }
java 复制代码
 //更新符合where set标签的数据
    @Test
    public void testSet() throws  Exception{
        //获取session对象
        SqlSession sqlSession = MyBatisUtils.getSession();
        //使用sqlSession获取UserDao的代理对象
        UserDao  userDao = sqlSession.getMapper(UserDao.class);
        User user = new User();

        user.setId("15");
        //user.setName("李莫愁");
        user.setAge(28);
        //user.setGender("女");
        userDao.updateById(user);

        //提交
        sqlSession.commit();
        sqlSession.close();
    }

(七) choose标签

1. choose标签简介

choose 标签作用条件判断来拼接指定的条件,它和 if 不太相同, choose 似类于 java 中的 switch 语句用法,直要有条件成立,其它判断将得不到执行,如果所有条件都不成立则执行 otherwise 标签中的内容。
格式 :

XML 复制代码
<choose>
  <when test=条件1>
    执行的代码;
  </when>
<when test=条件2>
    执行的代码;
  </when>
  ......
  <otherwise>
      执行的代码;
  </when>
  </otherwise>
</choose>
2. choose标签使用
1) 编写接口方法getInfoByUser
java 复制代码
    //根据choose里面的条件来查询数据
    public List<User> findUserByChoose(User user);
2) 使用choose标签进行sql处理
java 复制代码
<!--根据choose条件来进行查询-->
    <select id="findUserByChoose" parameterType="User" resultType="User">
        select * from users
        <where>
            <choose>
                <when test="name!=null">
                    name=#{name}
                </when>
                <when test="age!=null">
                    age=#{age}
                </when>
                <when test="gender!=null">
                    gender=#{gender}
                </when>
                <otherwise>
                    birthday=2024-10-08
                </otherwise>
            </choose>
        </where>
    </select>
3.测试接口方法getInfoByUser
java 复制代码
    //根据choose条件来进行查询
    @Test
    public void testChoose() throws  Exception{
        //获取session对象
        SqlSession sqlSession = MyBatisUtils.getSession();
        //使用sqlSession获取UserDao的代理对象
        UserDao  userDao = sqlSession.getMapper(UserDao.class);
        User user = new User();
        user.setName("张起灵");
        user.setAge(333);

        List<User> userList = userDao.findUserByChoose(user);
        for (User user1 : userList) {
            System.out.println(user1);
        }
        sqlSession.close();
    }

(八) foreach标签

1. foreach标签简介

foreach 标签表示循环,对 sql 中有重复的部分可以使用此循环来动态拼接 sql 语句。可以实现批量添加、批量删除、批量更新操作。 foreach 标签中有很多的属性,请参考下面的 Foreach 标签属性表。

Foreach标签属性表

|------------|---------------------|
| 属性名称 | 含义 |
| collection | 指定你要使用的集合类型 |
| item | 集合中每个元素。 |
| open | 在起始时,需要附加字符串,只附加一次。 |
| close | 在结束时,需要附加字符,只附加一次。 |
| separator | 在每个循环结时需要附加的字符串。 |
| index | 每个循环的索引值。 |

2. foreach标签使用
批量添加
java 复制代码
    //插入多条数据
    public void addBathUser(List<User> userList);
XML 复制代码
<!--使用foreach的批量插入-->
    <insert id="addBathUser" >
        insert into users(name,age,gender,birthday) values
        <foreach collection="list" item="user" separator=",">
            (#{user.name},#{user.age},#{user.gender},#{user.birthday})
        </foreach>
    </insert>
java 复制代码
    //批量插入数据
    @Test
    public void testAddBathUser() throws  Exception{
        //获取sqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSession();
        //通过sqlsession获得UserDao的代理对象
        UserDao userDao = sqlSession.getMapper(UserDao.class);

        List<User> userList = new ArrayList<>();
        userList.add(new User("令狐冲", 28, "男", new Date()));
        userList.add(new User("任盈盈", 25, "女", new Date()));
        userList.add( new User("岳不群", 50, "男", new Date()));
        userList.add( new User("林平之", 22, "男", new Date()));
        userList.add(  new User("东方不败", 35, "男/女", new Date()));
        userList.add( new User("风清扬", 70, "男", new Date()));

        userDao.addBathUser(userList);
        sqlSession.commit();
        System.out.println("批量添加数据成功");
        sqlSession.close();
    }
批量更新

更新之前的数据库

在进行批量更新之前,先添加一个满参的构造函数

java 复制代码
    public User(String id, String name, Integer age, String gender, Date birthday) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.birthday = birthday;
    }
java 复制代码
//批量更新
    public void updateBatchUser(List<User> userList);
XML 复制代码
<!--批量更新-->
<update id="updateBatchUser">
    update users set
  <foreach collection="list" item="user" separator=";">
      name=#{user.name},gender=#{user.gender},age=#{user.age},birthday=#{birthday}
      where id=#{id}
  </foreach>
</update>
java 复制代码
    //批量更新数据
    @Test
    public void testUpdateBathUser() throws  Exception{
        //获取sqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSession();
        //通过sqlsession获得UserDao的代理对象
        UserDao userDao = sqlSession.getMapper(UserDao.class);

        List<User> userList = new ArrayList<>();
        userList.add(new User(23,"田伯光", 30, "男", new Date()));
        userList.add(new User(24,"琳仪", 22, "女", new Date()));
        userList.add( new User(25,"左冷禅", 50, "男", new Date()));

        userDao.updateBatchUser(userList);
        sqlSession.commit();
        System.out.println("批量更新数据成功");
        sqlSession.close();
    }
出现错误
上网查看错误原因

发现是没有加允许批量操作的路径配置

还是错了
再次查看错误

数据库里面的主键约束阻碍了批量操作

又发现错误

主键解除不了绑定

终极大招

用了很多方法:其中把id的属性值改为int(上面没修改回来学到这自己修改),修改update标签里面的查询语句还是不行。最后直接这样改就完全没有问题了

java 复制代码
    //批量更新
    public void updateBatchUser(User user);
XML 复制代码
    <!--使用foreach的批量数据更新-->
    <update id="updateBatchUser">
        update users set name=#{name},age=#{age},gender=#{gender},birthday=#{birthday} where id = #{id}
    </update>
java 复制代码
    //批量更新数据
    @Test
    public void testUpdateBathUser() throws  Exception{
        //获取sqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSession();
        //通过sqlsession获得UserDao的代理对象
        UserDao userDao = sqlSession.getMapper(UserDao.class);

        List<User> userList = new ArrayList<>();
        userList.add(new User(23,"田伯光", 30, "男", new Date()));
        userList.add(new User(24,"琳仪", 22, "女", new Date()));
        userList.add( new User(25,"左冷禅", 50, "男", new Date()));

        for (User user : userList) {
            userDao.updateBatchUser(user);
        }
        sqlSession.commit();
        System.out.println("批量更新数据成功");
        sqlSession.close();
    }

完全更新成功,草!!

批量删除

删除之前的数据库

java 复制代码
    //批量删除
    public void deleteBatchUser(List<Integer> ids);
XML 复制代码
<!--使用foreach的批量删除-->
    <delete id="deleteBatchUser">
        delete from users where id in
        <foreach collection="list" item="userId" open="(" close=")" separator=",">
            #{userId}
        </foreach>
    </delete>
java 复制代码
    //批量删除数据
    @Test
    public void testDeleteBathUser() throws  Exception{
        //获取sqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSession();
        //通过sqlsession获得UserDao的代理对象
        UserDao userDao = sqlSession.getMapper(UserDao.class);

        List<Integer> idList = Arrays.asList(20,21,22);
        userDao.deleteBatchUser(idList);

        sqlSession.commit();
        System.out.println("批量删除数据成功");
        sqlSession.close();
    }

删除之后的数据库

相关推荐
小码的头发丝、10 分钟前
Java进阶学习笔记|面向对象
java·笔记·学习
坊钰42 分钟前
【Java 数据结构】移除链表元素
java·开发语言·数据结构·学习·链表
Purple Coder1 小时前
第6章 图论
笔记
阿七想学习2 小时前
数据结构《排序》
java·数据结构·学习·算法·排序算法
胡西风_foxww2 小时前
【ES6复习笔记】数值扩展(16)
前端·笔记·es6·扩展·数值
Somnus陳3 小时前
软考架构师笔记-计算机系统组成-1
笔记·系统架构
LuH11244 小时前
【论文阅读笔记】IC-Light
论文阅读·笔记
汤姆和佩琦4 小时前
2024-12-25-sklearn学习(20)无监督学习-双聚类 料峭春风吹酒醒,微冷,山头斜照却相迎。
学习·聚类·sklearn
是小菜呀!4 小时前
实验四 触发器
笔记
悲伤小伞4 小时前
C++_数据结构_详解二叉搜索树
c语言·数据结构·c++·笔记·算法