【JDBC & MyBatis】快速上手MyBatis

前言:这是笔者学习黑马程序员JDBCMyBatis课程的学习笔记。在阅读这篇文章学习之前,你需要了解maven的用法java语言基础、sql数据库基础。

初学者可以直接开始学习MyBatis的内容,而跳过JDBC部分和连接池部分的学习。

文章主要是视频课程知识点记录。许多知识点是通过例子的形式说明的。

目录

1. JDBC

1.1 jdbc入门例子(mysql+maven项目)

1.2 jdbc API

1.2.1 DriverManager

1.2.2 Connection

1.2.3 Statement

1.2.4 ResultSet 

1.2.5 PreparedStatement

原理

2.数据库连接池

2.1 基本概念

2.2 数据库连接池实现

标准接口:DataSource

2.3 Driud的使用

引入依赖

创建druid的properties配置文件

获取连接对象

3. MyBatis 

3.1 mybatis快速入门

3.2* Mapper代理开发

3.2.1 使用Mapper代理的原则

创建Mapper接口

设置SQL映射文件的namespace属性

在Mapper接口中定义方法

编码

包扫描的形式配置sql映射文件

*别名映射

3.3 核心配置文件

3.3.1顶层结构

3.3.2 environments

3.3.3 tpyeAliases 类型别名

3.4 MyBatis案例

3.4.1 环境准备

3.4.2 查询所有数据

3.4.3 单条件查询

*参数占位符

3.4.4 多条件查询的参数封装

法一:散装参数(使用@Param)

法二:对象参数

法三:map参数

小结

参数传递底层原理

3.4.5 动态条件查询

3.4.6 添加

获取返回的id

3.4.7 修改字段

修改动态字段

3.4.8 删除

删除单个

删除多个(使用 )

3.5 使用注解进行增删改查


1. JDBC

mybatis是一款持久层框架,用于简化jdbc的开发。而jdbc是java官方为了统一数据库使用而制定的一套数据库交互接口。

Java EE的三层架构:表现层、业务层、持久层(将数据保存到数据库的那一层)

1.1 jdbc入门例子(mysql+maven项目)

maven的快速上手参考我的另一篇博客【Maven】快速上手maven基础-CSDN博客前3部分内容。

引入依赖

首先创建一个maven项目。在pom.xml文件中,dependencies块中加入以下内容。

    <dependencies>
        <!-- 加入以下内容 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.2.0</version>
        </dependency>
    </dependencies>

创建并使用连接

package org.example;

//引入官方定义的jdbc
import java.sql.*;
public class App 
{
    public static void main( String[] args ) throws SQLException, ClassNotFoundException {
        //1、注册驱动(告诉jdbc使用的是什么数据库)
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2、获取连接
        String url = "jdbc:mysql://localhost:3306/db_22331030";
        String userName = "root";
        String password = "root";
        Connection conn = DriverManager.getConnection(url,userName,password);
        //定义sql语句
        String sql = "UPDATE customer_info SET CustomerName = 'what' WHERE (`CustomerID` = '4');\n ";
        //获取执行sql的语句对象
        Statement stmt = conn.createStatement();
        //执行sql
        stmt.executeUpdate(sql);
    }
}

1.2 jdbc API

1.2.1 DriverManager

驱动管理类,作用:注册驱动数据库连接

该类是一个静态工具类,常用方法有:

  1. getConnection(String url, String user, String password):依据指定的 URL、用户名和密码来获取数据库连接。
  2. getConnection(String url, Properties info):通过包含连接参数的Properties对象来获取数据库连接。
  3. registerDriver(Driver driver):手动注册一个 JDBC 驱动程序(在 JDBC 4.0 之后通常不需要这样做)。

ps:

在上面例子中,注册驱动的方式是使用语句Class.forName()。

Class.forName()是Java 反射机制中的一个重要方法,主要用于在运行时动态加载类。而由Driver类的定义中可以看出,当Driver被加载的时候,执行的静态代码块会进行驱动的注册。

【Driver类的定义】

1.2.2 Connection

数据库连接对象。主要功能:获取执行sql语句的对象管理事物。

  • 获取sql执行对象 常用方法
    • Statement createStatement():普通执行sql对象
    • PreparedStatement prepareStatement():预编译 SQL 的执行 SQL 对象:防止 SQL 注入
    • CallableStatement prepareCall():执行存储过程的对象

管理事物

java中可以利用try-catch语句进行事务操作:

【例子】

import java.sql.*;

public class TransactionDemo {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/testdb";
        String user = "root";
        String password = "password";
        Connection conn = DriverManager.getConnection(url, user, password);
        Statement stmt = conn.createStatement();

        try{
            // 1. 开启事务(关闭自动提交)
            conn.setAutoCommit(false);
            stmt.executeUpdate("INSERT INTO accounts (name, balance) VALUES ('Alice', 1000)");
            stmt.executeUpdate("INSERT INTO accounts (name, balance) VALUES ('Bob', 2000)");
            // 3. 提交事务
            conn.commit();
            System.out.println("事务提交成功");
        }
        catch(Exception e){
            conn.rollback();
            System.out.println("事务回滚: " + e.getMessage());
        }
        stmt.close();
        conn.close();
    }
}          

1.2.3 Statement

作用:执行sql语句

执行 SQL 语句的方法

  • executeUpdate()
    • 用于DML语句(增删改)和DDL语句(数据定义语言);
    • 执行DML语句,返回受影响的函数
    • 执行DDL语句,执行成功返回0;失败抛出异常
  • executeQuery()
    • 用于DQL语句(数据查询语言);
    • 返回ResultSet(结果集对象),通过该对象可以遍历和获取查询到的数据。

1.2.4 ResultSet 

结果集对象,executeQuery()方法执行之后的返回值。

核心功能

数据遍历 boolean next():将光标从当前位置向后移动一行(结果集游标默认从第一行的上一行开始),如果移动后的行是有效行,返回true;否则返回false

获取数据 getXxx(参数):对于当前行,获取Xxx类型的数据。如getString()、getInt()、getDate()……参数有两种重载形式,int类型参数代表列的编号,String类型参数代表列的名称。

一般使用形式

while(rs.next()){
    //获取数据
    rs.getXxx();
}

【完整例子】

public class App1
{
    public static void main( String[] args ) throws SQLException, ClassNotFoundException {
        //1、注册驱动(告诉jdbc使用的是什么数据库)
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2、获取连接
        String url = "jdbc:mysql://localhost:3306/db_22331030";
        String userName = "root";
        String password = "root";
        Connection conn = DriverManager.getConnection(url,userName,password);
        //定义sql
        String sql = "select * from customer_info ;";
        //获取执行sql的语句对象
        Statement stmt = conn.createStatement();
        //执行sql
        ResultSet rs=stmt.executeQuery(sql);

        //遍历结果集
        while (rs.next()){
            int id = rs.getInt("CustomerID");//获取列名为CustomerID的数据
            String name = rs.getString("Customername");
            String company = rs.getString(3); //第3列数据(索引从1开始)
            String sex = rs.getString(4);
            System.out.println(id+"--"+name+"--"+company+"--"+sex);
        }

        rs.close();
        stmt.close();
        conn.close();
    }
}

1.2.5 PreparedStatement

PreparedStatement继承自Statement。其作用是预编译SQL语句并执行,预防SQL注入问题。

【SQL注入】

通过一些特殊输入来修改实现定义好的sql语句,用以达到执行代码对服务器进行攻击的方法。

【例子】

正常 SQL 查询

SELECT * FROM users WHERE username = '输入的用户名' AND password = '输入的密码';

恶意输入示例:

 
' OR '1'='1

拼接后被篡改的 SQL

 
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';

由于'1'='1'恒为真,攻击者可以绕过身份验证直接访问数据库。

mysql默认关闭预编译功能,如果需要使用注意要开启

使用过程

public class App2
{
    public static void main( String[] args ) throws SQLException, ClassNotFoundException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        //使用参数useServerPrepStmts开启预编译功能
        String url = "jdbc:mysql://localhost:3306/db_22331030?useServerPrepStmts=true";
        String userName = "root";
        String password = "root";
        Connection conn = DriverManager.getConnection(url,userName,password);

        //定义sql,参数部分使用?占位符
        String sql = "select * from customer_info where Customername = ? and password = ?";
        //获取执行sql的语句对象,参数是sql语句
        PreparedStatement pstmt = conn.prepareStatement(sql);
        //设置参数
        pstmt.setString(1,"张三"); //设置Customername
        pstmt.setString(2,"123456");//设置password

        //执行语句
        ResultSet rs=pstmt.executeQuery();
        //判断结果
       if (rs.next()){
           System.out.println("登录成功");
       }else{
           System.out.println("登录失败");
       }
        //释放资源
        pstmt.close();
        rs.close();
        conn.close();
    }
}

其原理是将参数传入的时候,会进行转义,如将 ' 转义成 \' 。即,输入的符号 ' 不会与预定义好的sql语句中的 ' 相匹配。

原理

传统 Statement 的问题 普通Statement通过字符串拼接 SQL,每次执行都需要数据库重新解析和编译 SQL

预编译流程 

  1. 发送 SQL 模板:客户端将带占位符?的 SQL(如SELECT * FROM users WHERE username = ?)发送到数据库。
  2. 数据库编译:数据库解析 SQL 结构,生成执行计划并缓存。
  3. 多次执行:后续只需传递参数值,无需重复编译,提升效率。

2.数据库连接池

2.1 基本概念

  • 是一个容器,负责分配、管理数据库连接。
  • 允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
  • 释放空闲时间超过最大空闲时间的数据库连接,避免因为没有释放数据库连接而引起的数据库连接遗漏(连接池没有空闲连接了,导致某个用户一直等不到连接)。

2.2 数据库连接池实现

标准接口:DataSource

  • 由官方SUN公司提供的数据库连接池的标准接口,其他任何第三方连接池都需要实现该接口。
  • 功能:获取连接

常见数据库连接池

  • DBCP
  • C3P0
  • Druid:阿里巴巴开源的项目;是一个功能强大、性能优秀、Java语言最好的连接池之一。

2.3 Driud的使用

下面通过一个例子说明Driud的使用。

首先创建一个maven项目。

引入依赖

pom.xml

<!--引入druid连接池-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>
<!--引入mysql连接驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

创建druid的properties配置文件

创建一个后缀名为.properties的文件,参考一下格式进行配置。

其中必要的参数有:

  • driverClassName:数据库驱动类名。这里使用的是mysql因此使用mysql的驱动
  • url:数据库连接地址。格式jdbc:mysql://主机名:端口号/数据库名?参数
  • username:数据库登录用户名
  • password:数据库登录密码
#druis.properties
#基本参数:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306
username=root
password=root

#可选参数(参数有很多,具体查看官方文档)
#初始化连接数量
initialSize=5
#最大连接数
maxActive=10
#最大等待时间
maxWait=34000

获取连接对象

public class DruidUsing {
    public static void main(String[] args) throws Exception {
        //加载配置文件
        Properties prop=new Properties();
        prop.load(new FileInputStream("配置文件路径\\druid.properties"));
        //获取连接池对象
        DataSource dataSource= DruidDataSourceFactory.createDataSource(prop);
        //获取数据库连接 Connection
        Connection conn=dataSource.getConnection();
        System.out.println(conn);
    }
}

3. MyBatis 

JDBC的缺点:

  • 硬编码
    • 注册驱动、获取连接
    • SQL语句
  • 操作繁琐
    • 手动设置参数
    • 手动封装结果集

mybatis免除了几乎所有jdbc代码以及设置参数和获取结果集的工作

3.1 mybatis快速入门

官方教程MyBatis 3 插件 |入门 – mybatis

创建user表,添加数据

create database mybatis;
use mybatis;
drop table if exists tb_user;
create table tb_user(
    id int primary key auto_increment,
    username varchar(20),
    password varchar(20),
    gender char(1),
    addr varchar(30)
);
INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京');
INSERT INTO tb_user VALUES (2, '李四', '234', '女', '天津');
INSERT INTO tb_user VALUES (3, '王五', '11', '男', '西安');

创建模块,导入坐标

创建maven模块,将mybatis等必要依赖的坐标导入。

<dependencies>
  <!--    添加mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.5</version>
    </dependency>
<!--   导入Junit单元测试依赖-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
</dependencies>

编写MyBatis核心配置文件

在resources文件夹下创建配置文件mybatis-config.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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--数据库连接信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--指定映射文件路径-->
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

编写SQL映射文件

需要管理Xxx表,就要创建一个与之对应的Xxx实体类。因为管理的是user表,创建一个User实体类,类的成员变量就是表中的各个字段。

public class User {
    private  Integer id        ;
    private  String username  ;
    private  String password  ;
    private  String gender    ;
    private  String addr      ;

    //下面还包含所有属性的getter和setter方法,以及toString().
}

创建一个与要管理的Xxx表对应的XxxMapper.xml的SQL映射文件。这里我们要操作前面创建的user表,因此我们在resources文件夹下创建UserMapper.xml文件。

需要在该配置文件的<mapper>块里面配置sql语句的映射,其中这里面指定了一个resultType(返回值类型),因为下面例子中用到的是查询语句(select标签)所以返回值类型应该是封装好的一个个的实体类,也就是刚刚创建好的类。

<?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">
<!--namespace:名称空间,理论上可以自己命名-->
<mapper namespace="test">
    <!--id:sql语句的唯一标识  resultType:返回值类型-->
    <select id="selectAll" resultType="org.example.pojo.User">
        select * from tb_user;
    </select>
</mapper>

编码

public class App {
    public static void main( String[] args ) throws Exception {
       //加载mybatis的核心配置文件,获取SqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取sqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //执行sql
        List<User> users=sqlSession.selectList("test.selectAll");
        System.out.println(users);
        //释放资源
        sqlSession.close();
    }
}

 运行上面代码,输入如下:

3.2* Mapper代理开发

3.1小结的开发方式存在依赖于字符串字面量的硬编码问题。使用mapper代理方式能解决硬编码问题和简化后期SQL执行。

3.2.1 使用Mapper代理的原则

  • 定义与 SQL 映射文件同名的 Mapper 接口,并且将 Mapper 接口和 SQL 映射文件放置在同一目录下(编译后)
  • 设置 SQL 映射文件的 namespace 属性为 Mapper 接口全限定名
  • 在 Mapper 接口中定义方法,方法名就是 SQL 映射文件中 sql 语句的 id,并保持参数类型和返回值类型一致
  • 编码
    • 通过 SqlSession 的 getMapper 方法获取 Mapper 接口的代理对象
    • 调用对应方法完成 sql 的执行

细节:如果 Mapper 接口名称和 SQL 映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化 SQL 映射文件的加载

【例子】下面是一个使用mapper代理的例子。这个例子将承接3.1小结的内容。

创建Mapper接口

对于要管理的Xxx表,我们已经有了Xxx映射SQL文件。现在创建XxxMapper接口。一般这个接口放在mapper包里面,便于管理。

为了使这两个文件(编译后)在同一目录下,将UserMapper.xml文件在resources文件夹下的路径,和UserMapper.java文件在java文件下的路径一致。(resources文件夹和java文件夹在编译之后将会放在同一文件夹classes下)

在resources文件夹下创建文件夹,使之路径与mapper文件夹保持一致。

注意 创建文件夹的时候注意用 / 分隔,而不能使用 . 分隔,使用/ 分隔会形成多层文件夹,而使用 . 分隔会仅仅创建以 . 命名的文件夹。

然后将UserMapper.xml移到刚刚创建好的文件夹下。

编译好的项目将会放在target文件夹中。编译项目后查看target文件夹,将会发现此时由UserMapper.java编译成的UserMapper.class和UserMapper.xml文件已经处于同一目录下。

最后需要注意修改mybatis核心配置文件mybatis-config.xml文件中mappers块中指定的映射文件路径。

设置SQL映射文件的namespace属性

将其设置为Mapper接口全限定名。

在Mapper接口中定义方法

方法名就是SQL映射文件的sql语句id,参数类型和返回值类型需要保持一致。

编码
public class App2mapper {
    public static void main( String[] args ) throws Exception {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper接口的代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = userMapper.selectAll();

        System.out.println(users);
        //释放资源
        sqlSession.close();
    }
}

输入结果

包扫描的形式配置sql映射文件

在使用mapper代理的形式下,sql映射文件都放在mapper文件夹中,可以在核心文件中mappers的使用包扫描形式一次性加载包下所有映射文件。

<package name="org.example.mapper"/>

*别名映射

注意 该实体类的成员变量名需要和数据库表中列名保持一致,否则无法自动封装。

如果不一致,可以在sql语句查询的时候使用别名。如 

select user_Name as userName from tb_user;

或者在sql映射文件中使用resultMap映射

resultMap的子标签有两种:

  • id:完成主键字段的映射
  • result:完成一般字段的映射
<resultMap id="brandResultMap" tpye="brand">
    <result column="user_name" property="userName" />
</resultMap>

3.3 核心配置文件

官方文档很详细MyBatis 3 | Configuration – mybatis

3.3.1顶层结构

注意 配置各个标签的时,需要遵守前后顺序,顺序如上图。

3.3.2 environments

配置要连接的数据库。可以配置多个,在使用的时候修改environments标签中的default属性即可灵活切换要使用的数据源。

3.3.3 tpyeAliases 类型别名

在恰当的位置添加如下语句,在sql映射文件中使用org.example.pojo包下的类就不用带包名,直接使用类名即可。

mybatis-config.xml
mybatis-config.xml
​​​​

3.4 MyBatis案例

3.4.1 环境准备

该例子承接上面的例子。如果从0开始构建项目只需要导入必要的依赖、构建项目结构、配置核心文件的数据源,即environments即可。具体参考3.1快速入门的内容。

项目结构

项目结构
必要依赖
配置数据源

创建数据库以及对应表

-- 如果没有数据库先创建数据库
use mydb;
drop table if exists tb_brand;
-- 创建tb_brand表
create table tb_brand
(
    -- id 主键
    id           int primary key auto_increment,
    -- 品牌名称
    brand_name   varchar(20),
    -- 企业名称
    company_name varchar(20),
    -- 排序字段
    ordered      int,
    -- 描述信息
    description  varchar(100),
    -- 状态:0:禁用 1:启用
    status       int
);
-- 添加数据
insert into tb_brand (brand_name, company_name, ordered, description, status)
values ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
       ('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1),
       ('小米', '小米科技有限公司', 50, 'are you ok', 1);

SELECT * FROM tb_brand;

创建pojo包,在该包下面创建实体类。根据刚刚创建的表结构创建对应的实体类Brand

package org.example.pojo;

public class Brand {
    //主键
    private  Integer id          ;
    //品牌名称
    private  String brand_name  ;
    //公司名称
    private  String company_name;
    //排序字段
    private  Integer ordered     ;
    //描述信息
    private  String description ;
    //状态【1启用 0禁用】
    private  Integer status      ;
    
    //下面还包含所有属性的getter和setter方法,以及该类的toString方法
}

创建测试文件

文件结构如图

安装MyBatisX插件,该插件能够帮助我们更高效使用MyBatis框架。

主要功能:XML和接口方法相互跳转;根据接口生成statement(核心文件中的mapper映射)

假如在对应jopo类中创建一个方法,该方法对应sql语句在mapper配置中没有配置,将可以快捷生成对应的sql语句。

自动生成sql语句之后,补全select块内的内容

3.4.2 查询所有数据

这一步和上面的例子一致,不做过多赘述。

步骤:

  1. 编写接口方法:Mapper接口    List<Brand>  selectAll();
  2. 参数:无
  3. 结果:List<Brand>
  4. 编写SQL语句:SQL映射文件    <select id="selectAll" resultType="brand"> select * from tb_brand; </ select>
  5. 执行方法,测试

3.4.3 单条件查询

步骤:

  1. 编写接口方法:Mapper接口    List<Brand>  selectAll();
  2. 参数:int id
  3. 结果:List<Brand>
  4. 编写SQL语句:SQL映射文件    <select id="selectAll" resultType="brand"> select * from tb_brand; </ select>
  5. 执行方法,测试
*参数占位符

mybatis中有两种参数占位符:

  • #{}  :会将sql语句对应地方替换为占位符,防止sql注入
  • ${} :直接拼接成sql语句,存在sql注入问题
  • 使用时机:参数传递的时候使用#{} ;表名或者列名不确定的情况下才使用 ${}

参数类型:

  • parameterType:设置参数类型,该参数可以省略。

SQL语句中特殊字符处理(如小于号 <)

  • 使用转义字符,如<为 &lt;
  • CDATA区

对于sql语句select * from tb_brand where id < #{id};

<!--方法一:使用转义字符-->
<select id="selectById" resultType="org.example.pojo.Brand">
    select * from tb_brand where id &lt; #{id};
</select>


<!--方法二:使用CDATA区-->
<select id="selectById" resultType="org.example.pojo.Brand">
    select * from tb_brand where id 
    <![CDATA[
         <
    ]]>
    #{id};
</select>

【例子】

定义接口方法

编写映射文件

3.4.4 多条件查询的参数封装

首先配置sql映射文件

这里面使用 #{} 参数占位符的形式。

<select id="selectByCondition" resultType="org.example.pojo.Brand">
    select * from tb_brand
    where status = #{status}
        and company_name like #{companyName}
        and brand_name like #{brandName}
</select>

定义接口的时候,需要传递多个参数。参数的封装方式有三种:

法一:散装参数(使用@Param)

定义接口方法

//定义接口方法
public interface BrandMapper {
    //多条件查询
    //法1 散装参数:如果方法中有多个参数,需要使用@Param("参数名")注解
    public List<Brand> selectByCondition(
        @Param("status") int status,
        @Param("companyName") String companyName, 
        @Param("brandName") String brandName
    ); 
}

注意,上面@Param注解里的参数名应与sql映射文件中 #{} 里的参数名对应。

使用该方法

    public void  testSelectByCondition() throws Exception {
        //设置参数
        int status=1;
        String companyName="华为";
        String brandName="华为";
        //处理参数 模糊查询
        companyName="%"+companyName+"%";
        brandName="%"+brandName+"%";

        //获取SqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        //执行方法
        List<Brand> brands=brandMapper.selectByCondition(status, companyName, brandName);
        System.out.println(brands);

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

补充知识:

其实这种参数传递方法,MyBatis实际上是将这一个个需要传递的参数封装进一个Map容器。@Param注解里的参数将会称为键值对中的键名。Map中的键将会与映射文件里的#{}参数对应。如果不使用@Param注解,键名将会是默认值arg0,arg1……或者param1,param2……因此,如果不适用@Param注解,将映射文件中的#{}参数改成arg0,arg1……或者param1,param2……也能传递参数。

不过这种方式并不推荐。

法二:对象参数

获取参数之后,需要将参数封装成对应的实体类对象。

定义接口方法

//定义接口方法
public interface BrandMapper {
    //多条件查询
    //法2 对象参数
    public List<Brand> selectByCondition(Brand brand);
}

注意 对象的属性名称要和参数占位符名称一致。

如本例子中参数占位符#{}里面的参数名分别是status、companyName、brandName,需要保证实体类参数名是一致的。(为了一致我这里对实体类属性名稍作修改)

使用该方法

 public void  testSelectByCondition() throws Exception {
        //设置参数
        int status=1;
        String companyName="华为";
        String brandName="华为";
        companyName="%"+companyName+"%";
        brandName="%"+brandName+"%";

        //封装成实体类
        Brand brand=new Brand();
        brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);

        //获取SqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        //执行方法
        List<Brand> brands=brandMapper.selectByCondition(brand);
        System.out.println(brands);

        //释放资源
        sqlSession.close();
    }
法三:map参数

 定义接口方法

//定义接口方法
public interface BrandMapper {
    //多条件查询
    //法3 map参数
    public List<Brand> selectByConditionSingle(Map map); 
}

使用该方法

需要将参数封装成Map,其中封装好的Map注意键名需要和sql映射配置中的 #{} 内的参数名一致。

    public void  testSelectByCondition() throws Exception {
        //设置参数
        int status=1;
        String companyName="华为";
        String brandName="华为";
        companyName="%"+companyName+"%";
        brandName="%"+brandName+"%";

        //封装成Map
        Map map=new HashMap();
        map.put("status",status);
        map.put("companyName",companyName);
        map.put("brandName",brandName);

        //获取SqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取mapper接口对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        //执行方法
        List<Brand> brands=brandMapper.selectByCondition(map);
        System.out.println(brands);

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

参数传递底层原理

笔者认为这部分内容初学者无需理解,如果要详细学习建议看黑马课程p13.

黄色框里面红字的部分是参数被封装成Map时,键值对默认的键名。

在映射文件获取这些封装的参数,如果不使用@Param注解,直接使用默认键名(如“arg0”、“collection”)也能获取传递的参数。

3.4.5 动态条件查询

即查询的条件不固定。有时候可能需要对status进行筛选,有时候不对status进行筛选。

<if>标签

<if>标签是myBatis提供的标签,当<if>内test属性内的语句为正的时候才会将<if>标签包裹的语句添加到查询语句里面。下面是一个动态查询示例。

注意 为了满足语法,where后面跟着恒等式1=1。

<select id="selectByCondition" resultType="org.example.pojo.Brand">
    select * from tb_brand
    where 1=1
        <if test="status != null">
            and status = #{status}
        </if>
        <if test="companyName != null">
            and company_name like #{companyName}
        </if>
        <if test="brandName != null">
            and brand_name like #{brandName}
        </if>
</select>

<where>标签

myBatis提供的标签。当 <where> 标签内的 <if> 等条件标签有条件满足时,会自动添加 WHERE 关键字到 SQL 语句中;若 <where> 标签后的条件语句以 AND 或 OR 开头,它会自动剔除该开头的 AND 或 OR ,确保 WHERE 子句语法正确。

上面例子中,为了确保sql语句语法正确,必须在where后面跟恒等式。以下代码中,将where替换成<where>,即使没有恒等式也没有关系。myBatis会自动将关键字“and”进行保留或者删除以满足语法。

<select id="selectByCondition" resultType="org.example.pojo.Brand">
    select * from tb_brand
    <where>
        <if test="status != null">
            and status = #{status}
        </if>
        <if test="companyName != null">
            and company_name like #{companyName}
        </if>
        <if test="brandName != null">
            and brand_name like #{brandName}
        </if>
    </where>
</select>
<choose><when><otherwise> 标签

这三个标签配合使用类似于Java中的switch、case、default关键字。用于多个条件中选择一个条件进行查询。

  • <choose>:是父标签,作为容器,按顺序判断内部子标签条件。
  • <when> :<choose> 的子标签,可包含一个或多个。通过 test 属性设置条件表达式,当表达式值为 true 时,执行 <when> 内的 SQL 语句片段,且 <choose> 结束判断 ,不再检查后续 <when> 及 <otherwise> 。例如 <when test="status == 1"> AND status = #{status} </when> 。
  • <otherwise> :是 <choose> 的可选子标签,最多只能有一个,且须在所有 <when> 标签之后。当前面所有 <when> 的条件都不满足时,执行其内部 SQL 语句片段。如 <otherwise> AND is_deleted = 0 </otherwise> 。

以下例子中,只有一个<when>里面的sql语句会被使用。

<select id="selectByConditionSingle" resultMap="brandResultMap">
    select *
    from tb_brand
    where
    <choose> <!--类似于switch-->
        <when test="status != null"> <!--类似于case-->
            status = #{status}
        </when>
        <when test="companyName != null and companyName != """>
            company_name like #{companyName}
        </when>
        <when test="brandName != null and brandName != " ">
            brand_name like #{brandName}
        </when>
        <otherwise> <!--类似于default-->
            1 = 1
        </otherwise>
    </choose>
</select>

3.4.6 添加

定义方法接口

public interface BrandMapper {
    //添加
    public void add(Brand brand);
}

配置sql映射

注意 #{} 内参数名应该与实体类对应属性名保持一致

<insert id="add">
    insert into tb_brand (brand_name,company_name,ordered,description,status)
    values (#{brand_name},#{company_name},#{ordered},#{description},#{status});
</insert>

使用方法

将需要用到的参数封装成实体类。

注意 MyBatis 默认开启手动提交事务模式,执行INSERTUPDATEDELETE后需手动提交。即需要执行commit()

public void  testAdd() throws Exception {
        //设置参数
        int status=1;
        String companyName="小样手机";
        String brandName="小样";
        String description="这手机真好用";
        int ordered=100;

        //封装成实体类对象
        Brand brand=new Brand();
        brand.setStatus(status);
        brand.setCompany_name(companyName);
        brand.setBrand_name(brandName);
        brand.setDescription(description);
        brand.setOrdered(ordered);

        //获取SqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        //执行方法
        brandMapper.add(brand);
        //提交事务
        sqlSession.commit();

        //释放资源
        sqlSession.close();
    }
获取返回的id

如果插入数据后需要获取自增长的主键值,可以使用insert标签里面的useGenerateKeys和KeyProperty属性。

  • useGenerateKeys:取值true或者false。指示 MyBatis 是否使用 JDBC 的getGeneratedKeys方法来获取数据库表自动生成的主键值,比如常见的自增长主键 。
  • KeyProperty:指定将数据库表自动生成的主键值赋值给传入的 Java 对象的哪个属性 。
<insert id="add" useGeneratedKeys="true" keyProperty="id">
    insert into tb_brand (brand_name,company_name,ordered,description,status)
    values (#{brand_name},#{company_name},#{ordered},#{description},#{status});
</insert>

设置之后,如下代码执行完之后,封装的对象brand内id属性将会被填充。从而获取添加数据后,该行的id。

public void  testAdd() throws Exception {
        //设置参数
        int status=1;
        String companyName="小样手机";
        String brandName="小样";
        String description="这手机真好用";
        int ordered=100;

        //封装成实体类对象
        Brand brand=new Brand();
        brand.setStatus(status);
        brand.setCompany_name(companyName);
        brand.setBrand_name(brandName);
        brand.setDescription(description);
        brand.setOrdered(ordered);

        //获取SqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        //执行方法
        brandMapper.add(brand);
        //提交事务
        sqlSession.commit();
        //获取新增后的id
        System.out.println(brand.getId());
        //释放资源
        sqlSession.close();
}

3.4.7 修改字段

定义接口方法

public interface BrandMapper {
    //修改 返回值可以是void。如果返回值是int类型,将会返回受影响的行数
    public int update(Brand brand);
}

配置映射

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

执行方法

public void  testUpdate() throws Exception {
        //设置参数
        int status=1;
        String companyName="晓燕手机";
        String brandName="晓燕";
        String description="这晓燕手机真好用";
        int ordered=100;
        int id=8;

        //封装成实体类对象
        Brand brand=new Brand();
        brand.setStatus(status);
        brand.setCompany_name(companyName);
        brand.setBrand_name(brandName);
        brand.setDescription(description);
        brand.setOrdered(ordered);
        brand.setId(id);

        //获取SqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        //执行方法,获取受影响的行数
        int count=brandMapper.update(brand);
        //提交事务
        sqlSession.commit();

        //打印受影响的行数
        System.out.println(count);
        //释放资源
        sqlSession.close();
    }
修改动态字段

某些字段可能修改,也可能不修改,使用<set>和<if>实现动态修改。

<set> 标签

  • 专门用于动态生成 SQL 的 UPDATE 语句中的 SET 子句。
  • 自动处理逗号问题:会自动剔除多余的逗号(,),避免 SQL 语法错误。

<if> 标签

  • 实现条件判断,类似 Java 中的 if 语句。
  • 根据传入参数的值动态决定是否包含某段 SQL 片段。

sql映射文件

<update id="update">
    update tb_brand
    <set>
        <if test="brandName != null and brandName != ' '">
            brand_name = #{brandName},
        </if>
        <if test="companyName != null and companyName != ''">
            company_name = #{companyName},
        </if>
        <if test="ordered != null">
            ordered = #{ordered},
        </if>
        <if test="description != null and description != ''">
            description = #{description},
        </if>
        <if test="status != null">
            status = #{status},
        </if>
    </set>
    where id = #{id};
</update>

3.4.8 删除

删除单个

定义接口方法

public interface BrandMapper {
    //根据id删除
    public void deleteById(int id);
}

配置映射

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

执行方法

public void  testDeleteById() throws Exception {
        //设置参数
        int id=8;

        //获取SqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        //执行方法
        brandMapper.deleteById(id);
        //提交事务
        sqlSession.commit();

        //释放资源
        sqlSession.close();
    }
删除多个(使用<foreach>)

接受的参数是一个数组,在映射配置里面需要遍历数组获取需要删除的所有id。

在 MyBatis 中,<foreach>标签用于遍历集合参数,生成动态 SQL。

定义接口方法

public interface BrandMapper {
    //根据id批量删除
    public void deleteByIds(@Param("ids") int[] ids);
}

配置映射

<delete id="deleteByIds">
    delete from tb_brand where id in 
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    ;          
</delete>
  • @Param("ids"):将方法参数 ids 绑定到 SQL 映射中的 #{ids}。
    • 若不使用 @Param,MyBatis 会默认将数组参数命名为 "array",此时 <foreach> 的 collection 需改为 "array"。
  • separator 属性:定义每次迭代后添加的分隔符,用于连接集合中的多个元素。
  • open 属性:定义动态生成的 SQL 片段的起始符号,通常与 close 配合使用,用于包裹整个集合元素的拼接结果。
  • close 属性 :定义动态生成的 SQL 片段的结束符号,与 open 对应。

使用方法

 public void  testDeleteByIds() throws Exception {
        //设置参数
        int ids[]={3,9,10};

        //获取SqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        //执行方法
        brandMapper.deleteByIds(ids);
        //提交事务
        sqlSession.commit();

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

3.5 使用注解进行增删改查

对于简单的sql语句,使用注解的形式会比使用配置文件更加方便、简洁。

  • 查询 @Select
  • 添加 @Insert
  • 修改 @Update
  • 删除 @Delete

下面是一个简单的例子说明使用@Select注解如何配置。其他操作操作类似。

使用注解配置对应sql映射之后,就无需在映射文件中进行sql映射配置了。

public interface UserMapper {
    @Select("select * from tb_user")
    List<User> selectAll();
    @Select("select * from tb_user where id= #{id} ")
    User selectById(@Param("id") int id);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值