Mybatis练习:

本文详细介绍了Mybatis在查询操作中的应用,包括解决属性名与列名不一致的问题,参数占位符的使用,多条件动态SQL的编写,以及如何处理特殊字段和批量操作。还探讨了在接口中使用@Param注解的原因及其工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给出指定数据表,完成查询所有

1.1 环境准备

  • 数据库表(tb_brand)及数据准备

sql -- 删除tb_brand表

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);
  • 实体类 Brand

在 com.itheima.pojo 包下创建 Brand 实体类。

package com.pl.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Brand {
    private Integer id;
    private String brandName;
    private String companyName;
    private Integer ordered;
    private String description;
    private Integer status;
}

dao层接口以及映射文件:

 BrandMapper接口

package com.pl.dao;

import com.pl.pojo.Brand;

import java.util.List;

public interface BrandMapper {
    List<Brand> selectAll();
}

BrandMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD MApper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pl.dao.BrandMapper">
<!--    这种方法查出出来,有些封装失败,查不出来,因为Brand中与数据库中名字不对应-->
    <select id="selectAll" resultType="brand">
        select *
        from tb_brand;
    </select>

</mapper>

Brand实体类:

package com.pl.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Brand {
    private Integer id;
    private String brandName;
    private String companyName;
    private Integer ordered;
    private String description;
    private Integer status;
}

工具类:

package com.pl.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 {
    private static SqlSessionFactory sqlSessionFactory;//提升作用域
    static {
        try {
            //获取SQLSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //获取SqlSession连接


    public static SqlSession getSession(){
        return sqlSessionFactory.openSession();
    }//commit不需要再写,自动提交事务

}

Mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--    <properties resource="jdbc.properties"/>-->

    <typeAliases>
        <package name="com.pl.pojo"/>
    </typeAliases>

    <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://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
<!--    加载映射文件-->
    <mappers>
        <mapper resource="com/pl/dao/BrandMapper.xml"/>
    </mappers>
</configura

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.pl</groupId>
    <artifactId>ExerciseMybatis</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.10</version>
    </dependency>
<!--    <dependency>-->
<!--        <groupId>com.github.dongfg.plugin</groupId>-->
<!--        <artifactId>mybatis-generator-lombok-plugin</artifactId>-->
<!--        <version>1.0.0</version>-->
<!--    </dependency>-->
<!--    <dependency>-->
<!--        <groupId>org.junit.jupiter</groupId>-->
<!--        <artifactId>junit-jupiter</artifactId>-->
<!--        <version>RELEASE</version>-->
<!--        <scope>compile</scope>-->
<!--    </dependency>-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>

</dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>

测试结果:

 部分查询结果为空

解决办法1:我们可以在写sql语句时给这两个字段起别名,将别名定义成和属性名一致即可。

 解决办法2:使用resultMap解决

起别名 + sql片段的方式可以解决上述问题,但是它也存在问题。如果还有功能只需要查询部分字段,而不是查询所有字段,那么我们就需要再定义一个 SQL 片段,这就显得不是那么灵活。

通过更改代码可以实现查询到所有数据

  <select id="selectAll" resultMap="brandResultMap">
           select * from tb_brand;
       </select>
    <resultMap id="brandResultMap" type="brand">
          <result column="brand_name" property="brandName"/>
        <result column="company_name" property="companyName"/>
    </resultMap>

小结

实体类属性名 和 数据库表列名 不一致,不能自动封装数据

  • ==起别名:==在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样
  • 可以定义 片段,提升复用性
  • ==resultMap:==定义 完成不一致的属性名和列名的映射

练习2:根据id查询用户详情

接口类定义方法:

Brand selectById(int id);

Mapper文件中配置方法:

   <select id="selectById" resultType="brand" parameterType="int">
        select * from tb_brand where id = #{id};
    </select>

测试方法:

  @Test
  public void testSelectById() throws IOException {
     int id  = 1;
    SqlSession session = MybatisUtils.getSession();
    BrandMapper mapper = session.getMapper(BrandMapper.class);
    Brand brand = mapper.selectById(id);
    System.out.println(brand);
    session.close();
  }

结果:

 参数占位符详解:

mybatis提供了两种参数占位符:

  • #{} :执行SQL时,会将 #{} 占位符替换为?,将来自动设置参数值。从上述例子可以看出使用#{} 底层使用的是 PreparedStatement
  • ${} :拼接SQL。底层使用的是 Statement,会存在SQL注入问题。如下图将 映射配置文件中的 #{} 替换成 ${} 来看效果

 

  练习: SQL语句中特殊字段处理

parameterType使用

 

可以看出报错了,因为映射配置文件是xml类型的问题,而 > < 等这些字符在xml中有特殊含义,所以此时我们需要将这些符号进行转义,可以使用以下两种方式进行转义

  • 转义字符

下图的 &lt; 就是 < 的转义字符。

多条件查询:(模糊查询)

编写接口方法

在 BrandMapper 接口中定义多条件查询的方法。

而该功能有三个参数,我们就需要考虑定义接口时,参数应该如何定义。Mybatis针对多参数有多种实现

  • 使用 @Param("参数名称") 标记每一个参数,在映射配置文件中就需要使用 #{参数名称} 进行占位

 List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName,@Param("brandName") String brandName);

  • 将多个参数封装成一个 实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和实体类属性名保持一致。

 List<Brand> selectByCondition(Brand brand);

  • 将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和map集合中键的名称一致。

List<Brand> selectByCondition(Map map);

 编写SQL语句

在 BrandMapper.xml 映射配置文件中编写 statement,使用 resultMap 而不是使用 resultType

    <select id="selectByCondition" resultMap="brandResultMap">
        select * from tb_brand where status = #{status}
        and company_name like #{companyName}
        and brand_name like #{brandName}
    </select>

编写测试方法

 @Test
  public void testSelectByCondition() {
    //接收参数
    int status = 1;
    String companyName = "华为";
    String brandName = "华为";
    // 处理参数
    companyName = "%" + companyName + "%";
    brandName = "%" + brandName + "%";
    SqlSession session = MybatisUtils.getSession();
    BrandMapper mapper = session.getMapper(BrandMapper.class);
//方式一 :接口方法参数使用 @Param 方式调用的方法
    List<Brand> brands = mapper.selectByCondition(status, companyName, brandName);
 //方式二 :接口方法参数是 实体类对象 方式调用的方法
     //封装对象
    /* Brand brand = new Brand();
        brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);*/

    //List<Brand> brands = brandMapper.selectByCondition(brand);

    //方式三 :接口方法参数是 map集合对象 方式调用的方法
    Map map = new HashMap();
    map.put("status" , status);
    map.put("companyName", companyName);
    map.put("brandName" , brandName);
    List<Brand> brands = brandMapper.selectByCondition(map);
    System.out.println(brands);


    session.close();

动态SQL

上述功能实现存在很大的问题。用户在输入条件时,肯定不会所有的条件都填写,这个时候我们的SQL语句就不能那样写的

例如用户只输入 当前状态 时,SQL语句就是

select * from tb_brand where status = #{status}

而用户如果只输入企业名称时,SQL语句就是

select * from tb_brand where company_name like #{companName}

而用户如果输入了 当前状态 和 企业名称 时,SQL语句又不一样

select * from tb_brand where status = #{status} and company_name like #{companName}

针对上述的需要,Mybatis对动态SQL有很强大的支撑:

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

  • if 标签:条件判断
  • test 属性:逻辑表达式

改写selectbycondition:

    <select id="selectByCondition" resultMap="brandResultMap">
        select * from tb_brand where
        <if test="status != null">                           and status = #{status}</if>
        <if test="companyName != null and companyName != '' "> and company_name like #{companyName} </if>
        <if test="brandName != null and brandName != '' "> and brand_name like #{brandName} </if>
    </select>

如上的这种SQL语句就会根据传递的参数值进行动态的拼接。如果此时status和companyName有值那么就会值拼接这两个条件。

但是它也存在问题,如果此时给的参数值是

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

拼接的SQL语句就变成了

sql select * from tb_brand where and company_name like ? and brand_name like ?

而上面的语句中 where 关键后直接跟 and 关键字,这就是一条错误的SQL语句。这个就可以使用 where 标签解决

    <select id="selectByCondition" resultMap="brandResultMap">
        select * from tb_brand
        <where>
        <if test="status != null">                           and status = #{status}</if>
        <if test="companyName != null and companyName != '' "> and company_name like #{companyName} </if>
        <if test="brandName != null and brandName != '' "> and brand_name like #{brandName} </if>
        </where>
    </select>

单个条件(动态SQL):

如上图所示,在查询时只能选择 品牌名称当前状态企业名称 这三个条件中的一个,但是用户到底选择哪儿一个,我们并不能确定。这种就属于单个条件的动态SQL语句。

这种需求需要使用到 choose(when,otherwise)标签 实现, 而 choose 标签类似于Java 中的switch语句。

通过一个案例来使用这些标签

编写接口方法

在 BrandMapper 接口中定义单条件查询的方法。

 List<Brand> selectByConditionSingle(Brand brand);

配置方法:

在 BrandMapper.xml 映射配置文件中编写 statement,使用 resultMap 而不是使用 resultType

  <select id="selectByConditionSingle" resultMap="brandResultMap">
        select * from tb_brand
        <where>
            <choose>
                <when test="status != null and companyName != '' "></when>
                <when test="brandName != null and brandName != ''"><!--相当于case-->
                    brand_name like #{brandName}
                </when>
            </choose>

        </where>
    </select>

测试方法:

  @Test
  public void testSelectByConditionSingle(){
    int status = 1;
    String companyName = "华为";
    String brandName = "小米";
    companyName = "%" +companyName + "%";
    brandName = "%" +brandName +"%";
    //封装对象
    Brand brand = new Brand();
//    brand.setCompanyName(companyName);
//    brand.setStatus(status);
     brand.setBrandName(brandName);
    SqlSession session = MybatisUtils.getSession();
    BrandMapper mapper = session.getMapper(BrandMapper.class);
    List<Brand> brands = mapper.selectByConditionSingle(brand);
    System.out.println(brands);
    session.close();
  }

 添加数据

如上图是我们平时在添加数据时展示的页面,而我们在该页面输入想要的数据后添加 提交 按钮,就会将这些数据添加到数据库中。接下来我们就来实现添加数据的操作。

  • 编写接口方法
  • void add(Brand brand);

    编写SQL语句:

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

    测试方法:

  •   @Test
      public void testAdd() {
        //接收参数
        int status = 1;
        String companyName = "波导手机";
        String brandName = "波导";
        String description = "手机中的战斗机";
        int ordered = 100;
    
        //封装对象
        Brand brand = new Brand();
        brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);
        brand.setDescription(description);
        brand.setOrdered(ordered);
        SqlSession session = MybatisUtils.getSession();
        BrandMapper mapper = session.getMapper(BrandMapper.class);
        mapper.add(brand);
        session.close();
      }

    添加-主键返回

  • 在数据添加成功后,有时候需要获取插入数据库数据的主键(主键是自增长)。

    比如:添加订单和订单项,如下图就是京东上的订单

 添加订单数据

在 insert 标签上添加如下属性:
  • useGeneratedKeys:是够获取自动增长的主键值。true表示获取
  • keyProperty :指定将获取到的主键值封装到哪儿个属性里

 批量删除:

接口:

void deleteByIds(int[] ids);

实现方法:

在 BrandMapper.xml 映射配置文件中编写删除多条数据的 statement

编写SQL时需要遍历数组来拼接SQL语句。Mybatis 提供了 foreach 标签供我们使用

foreach 标签

用来迭代任何可迭代的对象(如数组,集合)。

  • collection 属性:
  • mybatis会将数组参数,封装为一个Map集合。
    • 默认:array = 数组
    • 使用@Param注解改变map集合的默认key的名称

  • item 属性:本次迭代获取到的元素。
  • separator 属性:集合项迭代之间的分隔符。foreach 标签不会错误地添加多余的分隔符。也就是最后一次迭代不会加分隔符。
  • open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次
  • close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次
<delete id="deleteByIds">
    delete from tb_brand where id
    in
    <foreach collection="array" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
    ;
</delete>

测试方法:

public void testDeleteByIds() {
    int[] ids = {1,2,3};

    SqlSession sqlSession = MybatisUtils.getSession();
    BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
    //4. 执行方法
    brandMapper.deleteByIds(ids);
    //5. 释放资源
    sqlSession.close();
  }

多个参数:

如下面的代码,就是接收两个参数,而接收多个参数需要使用 @Param 注解,那么为什么要加该注解呢?这个问题要弄明白就必须来研究Mybatis 底层对于这些参数是如何处理的。

User select(@Param("username") String username,@Param("password") String password);
<select id="select" resultType="user">
    select *
    from tb_user
    where 
        username=#{username}
        and password=#{password}
</select>

我们在接口方法中定义多个参数,Mybatis 会将这些参数封装成 Map 集合对象,值就是参数值,而键在没有使用 @Param 注解时有以下命名规则:

  • 以 arg 开头 :第一个参数就叫 arg0,第二个参数就叫 arg1,以此类推。如:
map.put("arg0",参数值1);
map.put("arg1",参数值2);
  • 以 param 开头 : 第一个参数就叫 param1,第二个参数就叫 param2,依次类推。如:
map.put("param1",参数值1);
map.put("param2",参数值2);

 

代码验证:

  • 在 UserMapper 接口中定义如下方法

java User select(String username,String password);

  • 在 UserMapper.xml 映射配置文件中定义SQL

xml <select id="select" resultType="user"> select * from tb_user where username=#{arg0} and password=#{arg1} </select>

或者

xml <select id="select" resultType="user"> select * from tb_user where username=#{param1} and password=#{param2} </select>

在映射配合文件的SQL语句中使用用 arg 开头的和 param 书写,代码的可读性会变的特别差,此时可以使用 @Param 注解。

在接口方法参数上使用 @Param 注解,Mybatis 会将 arg 开头的键名替换为对应注解的属性值。

代码验证:

  • 在 UserMapper 接口中定义如下方法,在 username 参数前加上 @Param 注解

java User select(@Param("username") String username, String password);

Mybatis 在封装 Map 集合时,键名就会变成如下:

map.put("username",参数值1);
map.put("arg1",参数值2);
map.put("param1",参数值1);
map.put("param2",参数值2);
  • 在 UserMapper.xml 映射配置文件中定义SQL

xml <select id="select" resultType="user"> select * from tb_user where username=#{username} and password=#{param2} </select>

  • 运行程序结果没有报错。而如果将 #{} 中的 username 还是写成 arg0

xml <select id="select" resultType="user"> select * from tb_user where username=#{arg0} and password=#{param2} </select>

 

代码验证:

  • 在 UserMapper 接口中定义如下方法,在 username 参数前加上 @Param 注解

java User select(@Param("username") String username, String password);

Mybatis 在封装 Map 集合时,键名就会变成如下:

map.put("username",参数值1);
map.put("arg1",参数值2);
map.put("param1",参数值1);
map.put("param2",参数值2);
  • 在 UserMapper.xml 映射配置文件中定义SQL

xml <select id="select" resultType="user"> select * from tb_user where username=#{username} and password=#{param2} </select>

  • 运行程序结果没有报错。而如果将 #{} 中的 username 还是写成 arg0

xml <select id="select" resultType="user"> select * from tb_user where username=#{arg0} and password=#{param2} </select>

 运行程序则可以看到错误

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值