09_MYBATIS_DAY02_1

1. 使用XML文件配置SQL语句

首先下载http://doc.canglaoshi.org/config/Mapper.xml.zip,解压得到SomeMapper.xml

在项目的src/main/resources下创建mappers文件夹,并将SomeMapper.xml复制到该文件夹,并重命名为UserMapper.xml

此步骤中创建的文件夹的名称是自定义的,与后续的配置有关。

此步骤中XML文件的名称是自定义的,与其它任何配置都无关。

以上添加的UserMapper.xml就是用于配置SQL语句的文件,通常称之为映射文件!该文件中的以下代码是固定的,且不可缺少的:

<?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的属性,该属性的值是对应的MyBatis接口文件的全名,例如:

<mapper namespace="cn.tedu.mybatis.UserMapper">
</mapper>

接下来,根据需要执行的SQL语句的种类,选择使用<insert><delete><update><select>这4个节点中的某1个来配置SQL语句,这些节点都必须配置id属性,该属性的值就是接口中的抽象方法的名称,然后,在节点内部编写SQL语句即可:

<?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">
  
<!-- namespace属性:接口的全名 -->
<mapper namespace="cn.tedu.mybatis.UserMapper">

	<!-- id属性:抽象方法的名称 -->
	<insert id="insert">
		INSERT INTO t_user (
			username, password, age, phone, email
		) VALUES (
			#{username}, #{password}, #{age}, #{phone}, #{email}
		)
	</insert>

</mapper>

目前,MyBatis框架并不知道存在mappers文件夹,更加不知道哪个文件配置的SQL语句!可以将配置SQL语句的XML文件的位置配置在jdbc.properties中:

mybatis.mapper-locations=classpath:mappers/*.xml

以上代码就用于表示“在src/main/resouces下的mappers文件夹中的所有XML文件都是用于配置SQL语句的”,在后续使用时,也必须保证不会在mappers文件夹中添加其它作用的XML文件!

接下来,还应该读取以上配置信息:

@Value("${mybatis.mapper-locations}")
private Resource[] mapperLocations;

并应用于SqlSessionFactoryBean对象的属性中:

@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    bean.setDataSource(dataSource);
    bean.setMapperLocations(mapperLocations);
    return bean;
}

最后,在运行或测试之前,还需要注意:每个抽象方法只能通过1种方式配置SQL语句,要么使用注解,要么使用XML文件,不可以同时使用!否则将报错,错误提示的关键信息例如:

Caused by: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for cn.tedu.mybatis.UserMapper.insert. please check file [D:\eclipse-workspace-202003\mybatis\target\classes\mappers\UserMapper.xml] and cn/tedu/mybatis/UserMapper.java (best guess)

以上提示中的Mapped Statements collection already contains value for cn.tedu.mybatis.UserMapper.insert.就表示“cn.tedu.mybatis.UserMapper.insert方法被映射了多个SQL语句的配置”,并且还推荐了检查UserMapper.xmlUserMapper.java文件!

如果配置的是<insert>节点,且需要获取自动编号的ID值,则在<insert>节点中继续配置属性即可,例如:

<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO t_user (
        username, password, age, phone, email
    ) VALUES (
        #{username}, #{password}, #{age}, #{phone}, #{email}
    )
</insert>

当需要实现的数据访问是查询类型的,在<select>节点中必须配置resultTyperesultMap中的某1个属性(二选一),如果都没有指定,则会出现如下错误:

Caused by: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement 'cn.tedu.mybatis.UserMapper.count'.  It's likely that neither a Result Type nor a Result Map was specified.

其中,resultType指的就是“封装查询结果的数据的类型”,也可以理解为“抽象方法的返回值的类型”,例如可以配置为:

<select id="count" resultType="java.lang.Integer">
	SELECT COUNT(*) FROM t_user
</select>

如果某个查询的抽象方法的返回值是List集合类型的,例如:

List<User> findAll();

在配置<select>resultType属性时,该属性值必须是集合中的元素的类型,例如:

<select id="findAll" resultType="cn.tedu.mybatis.User">
    SELECT * FROM t_user ORDER BY id
</select>

至于resultMap属性,后面专门介绍。

注意:使用了这种做法后,就需要对抽象方法名称的定义增加一个要求“不允许重载”!

2. 关于多参数的问题

当抽象方法的参数列表中超过1个参数时,在配置SQL语句时直接使用#{参数名称}是无法访问到参数值的!

因为Java源文件在运行之前需要被编译成字节码文件(.class文件),编译时,会丢失所有局部的量的名称,所以,会导致运行时原有的“参数名称”无法使用的问题!

MyBatis允许使用arg作为前缀并添加从0开始编号的名称(例如arg0arg1等等)表示第?个参数的名称,后续,在配置SQL语句时,就可以通过例如#{arg0}来表示抽象方法的第1个参数的值,使用#{arg1}表示抽象方法的第2个参数的值……以此类推!另外,还可以使用param作为前缀并添加从1开始编号的名称(例如param1param2等等),在具体使用时,使用arg系列的名称和param系列的名称均可!

但是,使用argparam 系列的名称不便于表示语义,并且,当抽象方法的参数列表发生变化时,这些名称中的序号也可能需要调整!

MyBatis提供了@Param注解,这个注解是添加在抽象方法的各参数之前的,可以在该注解中指定名称,后续,在配置SQL语句时,占位符中就使用注解中配置的名称!

2. 练习

  • tedu_ums数据库中创建t_group表,用于存储“用户分组”的信息,该表需要有idname这2个字段,分别表示“组id”和“组名称”;

    • CREATE TABLE t_group (
      	id int AUTO_INCREMENT,
          name VARCHAR(10) NOT NULL UNIQUE,
          PRIMARY KEY (id)
      ) DEFAULT CHARSET=utf8mb4;
      
  • 在项目中,创建Group实体类,对应t_group表;

    • package cn.tedu.mybatis;
      
      public class Group {
      
      	private Integer id;
      	private String name;
      
      	public Integer getId() {
      		return id;
      	}
      
      	public void setId(Integer id) {
      		this.id = id;
      	}
      
      	public String getName() {
      		return name;
      	}
      
      	public void setName(String name) {
      		this.name = name;
      	}
      
      	@Override
      	public String toString() {
      		return "Group [id=" + id + ", name=" + name + "]";
      	}
      
      }
      
  • 通过MyBatis技术,实现:

    • 增加组;
    • 根据id删除组;
    • 根据id查询组;
    • 查询所有组。

3. 动态SQL – foreach

假设存在需求:批量删除用户数据(一次性删除若干条用户数据);

需要执行的SQL语句大致是:

DELETE FROM t_user WHERE id=? OR id=? OR id=?;
DELETE FROM t_user WHERE id IN (?,?,?);

作为开发人员,无法确定以上SQL语句中问号的数量,及问号对应的参数值!只能确定以上参数的数据类型及所表示的意义!

以上功能最终将由用户(软件的使用者)来决定需要删除的数据的数量(问号的数量),及删除的数据是哪几条(问号对应的参数值)!就会导致“当用户的操作不同时(选中需要删除的数据不同),最终需要执行的SQL语句是不同的”!MyBatis框架提供了“动态SQL”机制来解决这个问题!

动态SQL:根据用户提供的参数值不同,最终需要执行的SQL语句可以不同!

当需要实现以上批量删除的需求时,可以将抽象方法设计为:

Integer deleteByIds(List<Integer> ids);

或者,也可以设计为(本次案例就使用这个):

Integer deleteByIds(Integer[] ids);

甚至,还可以设计为:

Integer deleteByIds(Integer... ids);

可变参数在被处理时,本质上就是数据。

在配置SQL语句时,需要通过<foreach>节点来配置SQL语句中需要通过循环生成的部分:

<delete id="deleteByIds">
    DELETE FROM t_user WHERE id IN (
    	<foreach collection="array" item="id" separator=",">
            #{id}
    	</foreach>
    )
</delete>

关于<foreach>节点的配置:

  • collection:需要被遍历的对象,当抽象方法的参数只有1个且没有添加@Param注解时,如果参数类型是List集合,则取值为list,如果参数类型是数组,则取值为array;当抽象方法的参数超过1个,就一定添加了@Param注解,则取值为@Param注解配置的参数值;

  • item:遍历过程中的每一个元素数据,当前属性可以自定义值表示元素数据的名称,在<foreach>节点的子级,使用#{}占位符时,就可以使用这个名称来表示数据;

  • separator:遍历生成的代码片段中,各元素数据之间的分隔符号;

List<User> users = xx;
for (User u : users) {
    System.out.println(u);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值