《动态SQL与多表关联》笔记
学习目标
- 能够使用动态SQL完成SQL拼接
- 能够使用resultMap完成多表查询
- 能够使用一对一查询
- 能够使用一对多查询 (注:多对多其实就是两个一个多)
映射文件:为什么要resultMap
目标
-
定义结果映射
-
使用结果映射
回顾
在mybatis中有2种配置文件:
- 核心配置文件,如:sqlMapConfig.xml
- 实体类映射文件,如:UserMapper.xml
实体类映射文件2个功能:
- 编写SQL语句
- 指定实体类与表之间映射关系
第2个功能还没用到,如果一张表中所有的列名与实体类中属性名全部相同,就不需要映射。
如果表中列名与实体类中属性名有不同,必须要映射,否则这列的数据就无法封装。
步骤
修改表结构
-- 复制user为user2新表,新复制的表没有主键约束,没有自增
create table user2 select * from user;
-- 添加主键约束
alter table user2 add primary key (id);
-- 增加自增功能
alter table user2 modify id int auto_increment;
-- 从100开始自增
alter table user2 auto_increment = 100;
-- 修改用户表结构,将用户名字段由username改成user_name
alter table user2 change username user_name varchar(20);
select * from user2;
查询结果如下
复制idea原来项目
-
在windows下复制原来的项目整个目录,并且改名。
-
修改项目中的文件名,项目的描述文件
-
在idea中导入项目
- 选择上面修改过的文件
-
如果使用idea插件,每次只能开一个mybatis项目,不然有时会发现DAO接口和XML映射文件有报错
-
删除UserMapper接口中所有的方法,删除UserMapper.xml中所有的配置,删除测试中所有测试的方法
数据库名字要修改
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/day26
jdbc.username=root
jdbc.password=root
用户实体类不变
package com.itheima.entity;
import java.sql.Date;
/**
用户实体类对象 */
public class User {
private Integer id; //注:使用大写
private String username;
private Date birthday;
private String sex;
private String address;
public User() {
}
public User(Integer id, String username, Date birthday, String sex, String address) {
this.id = id;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
//get和set省略
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
UserMapper接口
User findUser2ById(Integer id);
映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.itheima.dao.UserMapper">
<!--查询user2的表-->
<select id="findUser2ById" resultType="user">
select id,user_name as username,birthday,sex,address from user2 where id=#{id}
</select>
</mapper>
结果如下
配置前:
解决方法:
-
使用别名,给user_name定义别名,别名名字与username相同
select id,user_name as username,birthday,sex,address from user2 where id=#{id}
-
resultMap来实现实体类与表中列映射
映射文件:resultMap输出类型
使用resultMap实现
说明:resultMap用于配置sql语句中字段(列)的名称,与java对象中属性名称的对应关系。本质上还是要把执行结果映射到java对象上。分两步:
-
使用resultMap来定义一个映射关系,并且给这个映射id的属性,这个id在同一个配置文件中要唯一
-
在查询的时候,将查询的结果设置为上面的映射,通过id来引用
配置mapper映射文件
- 定义映射,指定id和type,type为实体类的别名
- id标签:映射主键字段,如果列名与属性名相同可以省略
- result标签:映射普通字段,指定哪个属性对应哪个列,这里只需映射username即可
- 在查询的结果中使用resultMap,为上面的映射id
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.itheima.dao.UserMapper">
<!--
1. 使用resultMap来定义一个映射关系
id:给这个映射唯一标识
type:对应的实体类的类型
-->
<resultMap id="userMap2" type="user">
<!--
id子元素:用于指定主键映射(如果列名与属性名相同,可以不映射)
属性:
column 指定表中列名
property 指定实体体中属性名
javaType 指定属性名的数据类型
result:子元素用于定义其它列的映射,两个属性与上面含义相同,只映射不同的列
-->
<id column="id" property="id"/>
<result column="user_name" property="username"/>
</resultMap>
<!--
2. 查询user2的表的时候,通过id引用上面的映射关系
resultType 直接指定实体类的别名
resultMap 指定定义的映射关系
-->
<select id="findUser2ById" resultMap="userMap2">
select id,user_name,birthday,sex,address from user2 where id=#{id}
</select>
</mapper>
测试
查询id为1的用户
@Test
public void testFindUser2ById() {
User user = userMapper.findUser2ById(1);
System.out.println(user);
}
执行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m8GVqcaQ-1592726289127)(assets/image-20200516094550680.png)]
映射流程
映射文件的标签小结
标签 | 子标签 | 说明 |
---|---|---|
<resultMap> | 定义表的字段名与实体类的属性名之间映射关系 | |
<id> | 定义主键列 | |
<result> | 定义其它列 |
创建会话的工具类
目标
创建会话的工具类
需求
因为以后每天都需要使用会话对象,如果每次都完整的写一次,比较麻烦。创建一个工具类,调用一个方法获取会话对象。
步骤
- 在静态代码块中创建会话工厂
- 编写静态方法得到会话
代码
package com.itheima.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 SqlSessionUtils {
private static SqlSessionFactory factory;
/**
* 在静态代码块中创建会话工厂
*/
static {
//0.创建核心配置文件的输入流
try (InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml")) {
//1.创建工厂建造类
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
//2.创建会话工厂,读取核心配置文件
factory = factoryBuilder.build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 编写一个静态方法获取会话
*/
public static SqlSession getSession() {
return factory.openSession();
}
}
动态SQL:环境搭建
目标
- 学习哪些动态SQL标签
- 复制项目
MyBatis动态SQL
以前:我们的SQL语句都是直接写死在配置文件中,一但写好了就固定了。
概念:在代码的执行过程中,根据代码的执行情况动态拼接成不同的SQL语句
将要学习的标签:类似于JSTL中标签
- if 在XML中进行判断,如果符合条件就进行拼接
- where 类似于sql where语句
- set 用于更新的操作
- foreach 对集合或数组进行遍历
- sql 定义一个SQL代码片段
- include 引用上面代码片段
需求
- 根据用户名称和性别查询用户数据
- 姓名是模糊查询
- 查询User表
声明UserMapper接口方法
- 根据用户名称和性别查询用户
- 参数是User对象
/**
* 根据用户名称和性别查询用户
*/
List<User> findUserByNameAndSex(User user);
配置mappr映射文件
<!--根据用户名称和性别查询用户-->
<select id="findUserByNameAndSex" resultType="user">
select * from user where username like "%"#{username}"%" and sex = #{sex}
</select>
执行效果
测试
@Test测试方法中通过用户名和性别查询多个用户,查询条件是"精"和"女"
@Test
public void testFindUserByNameAndSex() {
//封装查询条件
User user = new User();
user.setUsername("精");
user.setSex("女");
List<User> users = userMapper.findUserByNameAndSex(user);
System.out.println(users);
}
动态SQL:if和where标签
目标
- 动态SQL语句if标签的使用
- 动态SQL语句where标签的使用
应用场景
如果使用一个实体类来封装所有的查询条件,如果这个实体类属性有值,则参与查询,否则就不做为查询条件。
if标签
作用
判断某个条件是否为真,如果为真则拼接标签体中的SQL语句
UserMapper.xml
- 根据用户名称和性别查询用户
- if:判断用户名称不为空,且不为空字符串,则作为查询条件
- if:判断用户性别不为空,且不为空字符串,则作为查询条件
<!--
根据用户名称和性别查询用户
if 标签的作用:判断某个条件是否为真,如果为真则拼接标签体中的SQL语句
-->
<select id="findUserByNameAndSex" resultType="user">
select * from user where
<if test="username!=null and username!=''">
username like "%"#{username}"%"
</if>
<if test="sex!=null and sex!=''">
and sex = #{sex}
</i