Mybatis 任务二:配置文件深入
课程任务主要内容:
* Mybatis 高级查询
* 映射配置文件深入
* 核心配置文件深入
* Mybatis 多表查询
* Mybatis 嵌套查询
一 Mybatis 高级查询
1.1 ResultMap 属性
建立对象关系映射
- resultType:如果实体的属性名与表中字段名一致(前提),将查询结果自动封装到实体类中
- ResultMap:如果实体的属性名与表中字段名不一致,可以使用ResutlMap 实现手动封装到实体类中
- 编写UserMapper 接口
public interface UserMapper {
public List<User> findAllResultMap();
}
- 编写UserMapper.xml
<!--id:唯一标识此resultMap
type:封装后实体的类型,要使用全类名,当在mybatis核心配置类中起了别名之后,可以使用别名-->
<resultMap id="userResultMap" type="com.myLagou.entity.User">
<!--手动配置映射关系-->
<!--property用来配置实体类中的属性名,column用来配置数据库表中的字段名 -->
<!--id:用来配置主键-->
<id property="id" column="id"></id>
<!--配置其他属性-->
<result property="usernameabc" column="username"></result>
<result property="birthdayabc" column="birthday"></result>
<result property="sexabc" column="sex"></result>
<result property="addressabc" column="address"></result>
补充:如果有查询结果有 字段与属性是对应的,可以省略手动封装 【了解】
</resultMap>
<!--查询所有用户-->
<!--ResultMap:如果实体的属性名与表中字段名不一致,可以使用ResultMap 实现手动封装到实体类中-->
<select id="findAllResultMap" resultMap="userResultMap">
select * from user
</select>
3)代码测试
//测试resultMap
@Test
public void test2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//当前返回的其实就是基于UserMapper所产生的代理对象,底层就是JDK的动态代理, 实际类型就是一个Proxy
List<User> userList = mapper.findAllResultMap();
sqlSession.close();
for (User user : userList) {
System.out.println(user);
}
}
1.2 多条件查询(三种)
需求
根据id 和username 查询 user 表
User实体类
package com.myLagou.entity;
import java.util.Date;
/**
* @author zhy
* @create 2022-08-07 14:11
*/
public class User {
private Integer id;
private String usernameabc;
private Date birthdayabc;
private String sexabc;
private String addressabc;
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsernameabc() {
return usernameabc;
}
public void setUsernameabc(String usernameabc) {
this.usernameabc = usernameabc;
}
public Date getBirthdayabc() {
return birthdayabc;
}
public void setBirthdayabc(Date birthdayabc) {
this.birthdayabc = birthdayabc;
}
public String getSexabc() {
return sexabc;
}
public void setSexabc(String sexabc) {
this.sexabc = sexabc;
}
public String getAddressabc() {
return addressabc;
}
public void setAddressabc(String addressabc) {
this.addressabc = addressabc;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", usernameabc='" + usernameabc + '\'' +
", birthdayabc=" + birthdayabc +
", sexabc='" + sexabc + '\'' +
", addressabc='" + addressabc + '\'' +
'}';
}
}
1)方式一
使用 #{arg0}-#{argn} 或者 #{param1}-#{paramn} 获取参数
UserMapper 接口
public interface UserMapper {
public List<User> findByIdAndUsername1(Integer id, String username);
}
UserMapper.xml
<!--多条件查询方式1-->
<!--当有多个参数时,占位符#{}与参数之间的匹配关系为:#{arge0}对应第一个参数,#{arge1}对应第二个参数,依此类推-->
<!-- <select id="findByIdAndUsername1" resultMap="userResultMap">-->
<!-- select * from user where id = #{arg0} and username = #{arg1}-->
<!-- </select>-->
<!--当有多个参数时,占位符#{}与参数之间的匹配关系为:#{param1}对应第一个参数,#{param2}对应第二个参数,依此类推-->
<select id="findByIdAndUsername1" resultMap="userResultMap">
select * from user where id = #{param1} and username = #{param2}
</select>
测试
//测试多条件查询方式1
@Test
public void test3() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//当前返回的其实就是基于UserMapper所产生的代理对象,底层就是JDK的动态代理, 实际类型就是一个Proxy
List<User> username1 = mapper.findByIdAndUsername1(1, "子慕");
sqlSession.close();
for (User user : username1) {
System.out.println(user);
}
}
2)方式二
使用注解,引入 @Param() 注解获取参数
UserMapper 接口
/*多条件查询方式2*/
public List<User> findByIdAndUsername2(@Param("id") Integer id, @Param("username")String username);
UserMapper.xml
<!--多条件查询方式2
在接口中的方法参数位置使用@Param("xxx"),
在select语句中的占位符#{}中使用param声明的xxx来匹配参数
-->
<select id="findByIdAndUsername2" resultMap="userResultMap">
select * from user where id = #{id} and username = #{username}
</select>
测试
//测试多条件查询方式2
@Test
public void test4() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//当前返回的其实就是基于UserMapper所产生的代理对象,底层就是JDK的动态代理, 实际类型就是一个Proxy
List<User> username1 = mapper.findByIdAndUsername2(1, "子慕");
sqlSession.close();
for (User user : username1) {
System.out.println(user);
}
}
3)方式三(推荐)
使用pojo 对象传递参数
UserMapper 接口
/*多条件查询方式3*/
public List<User> findByIdAndUsername3(User user);
UserMapper.xml
<!--多条件查询方式3
使用整个实体对象来做参数
占位符#{}中所写的参数要与实体类中的属性名一致
-->
<select id="findByIdAndUsername3" resultMap="userResultMap" parameterType="User">
select * from user where id = #{id} and username = #{usernameabc}
</select>
测试
//测试多条件查询方式3
@Test
public void test5() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//当前返回的其实就是基于UserMapper所产生的代理对象,底层就是JDK的动态代理, 实际类型就是一个Proxy
User user1 = new User();
user1.setId(1);
user1.setUsernameabc("子慕");
List<User> username1 = mapper.findByIdAndUsername3(user1);
sqlSession.close();
for (User user : username1) {
System.out.println(user);
}
}
1.3 模糊查询
需求
根据username 模糊查询user 表
1)方式一
使用#{}占位符
UserMapper 接口
/*模糊查询方式1*/
public List<User> findByUsername1(String username);
UserMapper.xml
<!--模糊查询方式1-->
<!--当所传递参数parameterType为基本数据类型或者是String类型,且传递参数只有一个时,那么#{}中的参数随便写,但是一般遵循见名知意原则-->
<select id="findByUsername1" resultMap="userResultMap" parameterType="String">
select * from user where username like #{username}
</select>
<!--#{}在进行实际参数的转换的时候,替换真正的参数值时会自动的帮我们添加上单引号-->
测试
//测试多模糊查询方式1
@Test
public void test6() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//findByUsername1("子慕")如果只写成这样,那么就相对于等值查询,只会查出username=子慕的数据,而不是模糊查询
//要想是模糊查询,就需要添加上通配符%,如下所示:
List<User> username1 = mapper.findByUsername1("%子慕%");
sqlSession.close();
for (User user : username1) {
System.out.println(user);
}
}
2)方式二
使用${},进行sql原样拼接
UserMapper 接口
public interface UserMapper {
/*模糊查询方式2*/
public List<User> findByUsername2(String username);
}
UserMapper.xml
<!--模糊查询方式2 使用${}占位符-->
<!--当所传递参数parameterType为基本数据类型或者是String类型,且传递参数只有一个时,那么${}中的值只能写value-->
<select id="findByUsername2" resultMap="userResultMap" parameterType="String">
select * from user where username like '${value }'
</select>
<!--${}实际上是进行sql原样拼接,不会替换真正的参数值时不会自动的加上单引号,所以需要我们自己手动添加-->
测试
//测试多模糊查询方式2
@Test
public void test7() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> username1 = mapper.findByUsername2("%子慕%");
sqlSession.close();
for (User user : username1) {
System.out.println(user);
}
}
3)方式三
UserMapper 接口
public interface UserMapper {
public List<User> findByUsername3(String username);
}
UserMapper.xml
<mapper namespace="com.lagou.mapper.UserMapper">
<!--不推荐使用,因为会出现sql 注入问题-->
<select id="findByUsername3" parameterType="string" resultType="user">
select * from user where username like '%${value}%'
</select>
</mapper>
测试
//测试 模糊查询方式3
@Test
public void testFindByUsername3() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//在sql语句中添加了通配符后再下面的代码中就不需要了,如下所示:
List<User> username1 = mapper.findByUsername3("子慕");
sqlSession.close();
for (User user : username1) {
System.out.println(user);
}
}
4)方式四(推荐)
UserMapper 接口
public interface UserMapper {
public List<User> findByUsername4(String username);
}
UserMapper.xml
<!--/*模糊查询方式4*/-->
<select id="findByUsername4" resultMap="userResultMap" parameterType="String">
<!--推荐使用,concat() 字符串拼接函数
注意:在 Oracle 中,concat() 函数只能传递二次参数,我们解决方案是嵌套拼接-->
select * from user where username like concat(concat('%',#{username}),'%');
</select>
测试
//测试 模糊查询方式4
@Test
public void testFindByUsername4() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//在sql语句中添加了通配符后再下面的代码中就不需要了,如下所示:
List<User> username1 = mapper.findByUsername4("子慕");
sqlSession.close();
for (User user : username1) {
System.out.println(user);
}
}
5)${} 与 #{} 区别【面试题】
#{} :表示一个占位符号
-
通过#{} 可以实现 preparedStatement(预编译对象) 向占位符中设置值,自动进行 java 类型(实体中属性的类型)和 jdbc 类型(表中字段类型)转换,#{}可以有效防止sql 注入。
-
#{} 可以接收简单类型值或 pojo 属性值。
-
如果 parameterType 传输单个简单类型值, #{} 括号中可以是value 或其它名称。
${} :表示拼接sql 串
-
通过${} 可以将 parameterType 传入的内容拼接在 sql 中且不进行jdbc 类型转换,会出现 sql 注入问题。
-
${} 可以接收简单类型值或 pojo 属性值。
-
如果 parameterType 传输单个简单类型值, ${} 括号中只能是value。
- 补充:TextSqlNode.java 源码可以证明
二 Mybatis 映射文件深入
2.1 返回主键
应用场景
我们很多时候有这种需求,向数据库插入一条记录后,希望能立即拿到这条记录在数据库中的主键值。
2.1.1 useGeneratedKeys
UserMapper接口
public interface UserMapper {
// 返回主键
public void save(User user);
}
UserMapper.xml
<!--添加用户并且获取返回主键的值,方式1-->
<!--
useGeneratedKeys:声明返回主键,值为true表示返回,值为false表示不返回
keyProperty:把返回的主键的值封装到实体中的指定的属性上
-->
<insert id="saveUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address})
</insert>
注意:只适用于主键自增的数据库,mysql 和sqlserver 支持,oracle 不行。
测试
//测试添加用户并且获取返回主键的值,方式1
@Test
public void test8() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = new User();
user1.setUsername("杨洋");
user1.setBirthday(new Date());
user1.setSex("男");
user1.setAddress("我心里");
System.out.println(user1);//user1的id为null
mapper.saveUser(user1);
sqlSession.commit();
sqlSession.close();
System.out.println(user1);//user1的id有值
}
2.1.2 selectKey
UserMapper接口
public interface UserMapper {
// 返回主键
public void save(User user);
}
UserMapper.xml
<!--添加用户并且获取返回主键的值,方式1-->
<!--
selectKey:获取返回指定的主键值,适用范围广,支持所有类型数据库
order="AFTER":表示selectKey中的语句在执行sql语句之后执行(在MySQL、SQL Server数据库中是使用after,
但是在oracle数据库中使用的是before
keyColumn:指定主键对应数据库表中对应的列名
keyProperty:指定主键封装到实体的 指定 属性中
resultType:指定主键类型
-->
<insert id="saveUser2" parameterType="user">
<selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="int">
SELECT LAST_INSERT_ID();
</selectKey>
insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address})
</insert>
测试
//测试添加用户并且获取返回主键的值,方式2
@Test
public void test9() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = new User();
user1.setUsername("于途");
user1.setBirthday(new Date());
user1.setSex("男");
user1.setAddress("我心里");
System.out.println(user1);//user1的id为null
mapper.saveUser2(user1);
sqlSession.commit();
sqlSession.close();
System.out.println(user1);//user1的id有值
}
2.2 动态 SQL
应用场景
当我们要根据不同的条件,来执行不同的 sql 语句的时候,需要用到动态sql。
2.2.1 动态 SQL 之<if>标签
需求
根据id 和username 查询,但是不确定两个都有值。
a)UserMapper 接口
public List<User> findByIdAndUsernameIf(User user);
b)UserMapper.xml 映射
<!--动态sql的if标签:多条件查询-->
<!-- /*test里面写的就是表达式*/
/*#{}中的值也需要跟传递过来的parameterType实体类中的属性名一样*/-->
<!-- <select id="findByIdAndUsernameIf" parameterType="user" resultMap="userResultMap">-->
<!-- select * from user where 1=1-->
<!-- <if test="id != null">-->
<!-- and id = #{id}-->
<!-- </if>-->
<!-- <if test="username != null">-->
<!-- and username = #{username}-->
<!-- </if>-->
<!-- </select>-->
<!--
根据上述select查询语句中的where 1=1 可以直接改写为如下代码
下述代码中的<where>标签就相对于where 1=1,但是如果没有条件的话,不会拼接上where关键字,只有有条件的时候才会自动拼接上
-->
<select id="findByIdAndUsernameIf" parameterType="user" resultMap="userResultMap">
select * from user
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="username != null">
and username = #{username}
</if>
</where>
</select>
c)测试代码
//测试 动态sql的if标签:多条件查询
@Test
public void tes10() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = new User();
//user1.setUsername("杨洋");//只有username时可以查询出
// user1.setId(1);//只有id值的时候也可以查出
user1.setId(1);
user1.setUsername("子慕");//id和username都有的时候也可以查出
List<User> userList = mapper.findByIdAndUsernameIf(user1);
sqlSession.close();
for (User user : userList) {
System.out.println(user);
}
}
2.2.2 动态 SQL 之标签
需求
如果有id 只使用id 做查询,没有 id 的话看是否有username,有username 就根据username 做查询,如果都没有,就不带条件。
a)UserMapper 接口
public List<User> findByIdAndUsernameChoose(User user);
b)UserMapper.xml 映射
c)测试代码
2.2.3 动态 SQL 之<set>标签
需求
动态更新user 表数据,如果该属性有值就更新,没有值不做处理。
a)UserMapper 接口
//动态sql的set标签:动态更新
public void updateIf(User user);
b)UserMapper.xml 映射
<!--//动态sql的set标签:动态更新-->
<update id="updateIf" parameterType="user">
update user
<!--<set>标签:在更新的时候,会自动的添加set关键字,还会去掉最后一个条件的逗号,
代替了之前直接硬编码的update语句
-->
<set>
<if test="username != null">
username = #{username},
</if>
<if test="birthday != null">
birthday = #{birthday},
</if>
<if test="sex != null">
sex = #{sex},
</if>
<if test="address != null">
address = #{address},
</if>
</set>
where id = #{id}
</update>
c)测试代码
//测试 动态sql的set标签:动态更新
@Test
public void tesUpdateIf() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = new User();
user1.setId(10);
user1.setUsername("杨洋内娱第一帅哥");
user1.setAddress("住在我心坎里!!!");
mapper.updateIf(user1);
sqlSession.commit();
sqlSession.close();
System.out.println(user1);
}
2.2.4 动态 SQL 之<foreach>标签
foreach 主要是用来做数据的循环遍历
例如:select * from user where id in (1,2,3) 在这样的语句中,传入的参数部分必须依靠foreach 遍历才能实现。
- <foreach>标签用于遍历集合,它的属性:
- collection:代表要遍历的集合元素
- open:代表语句的开始部分
- close:代表结束部分
- item:代表遍历集合的每个元素,生成的变量名
- sperator:代表分隔符
a)当传递参数的容器为集合类型
UserMapper 接口
/*动态sql的foreach标签:多值查询,*/
public List<User> findByList(List<Integer> ids);
UserMaper.xml 映射
<!--动态sql的foreach标签:多值查询,根据多个id值查询用户,多个id组成一个集合-->
<select id="findByList" parameterType="list" resultType="user">
<!--原始的sql语句:select * from user id in(1,2,3)-->
<!--使用动态sql语句如下-->
select * from user
<where>
<!--collection:代表要遍历的集合元素,当要遍历的集合为普通类型(如基本数据类型或者String)
collection 属性值就可以写:collection 或者 list
open:代表foreach中语句开始的部分
close:代表foreach中语句结束的部分
item:代表遍历集合中的每个元素生成的变量名
separator:分隔符,将item中的变量进行分隔
#{}中所写的值要与item所写的值一致
-->
<foreach collection="collection" open="id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
测试代码
//测试 动态sql的foreach标签:多值查询,根据多个id值查询用户,多个id组成一个集合
@Test
public void tesFindByList() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(10);
list.add(11);
List<User> userByList = mapper.findByList(list);
for (User user : userByList) {
System.out.println(user);
}
}
b)当传递参数的容器为数组类型
UserMapper 接口
/*动态sql的foreach标签:多值查询,多值组成一个数组时*/
public List<User> findByArray(Integer[] ids);
UserMaper.xml 映射
<!--动态sql的foreach标签:多值查询,根据多个id值查询用户,多个id组成一个数组-->
<select id="findByArray" parameterType="int" resultType="user">
select * from user
<where>
<!--collection:代表要遍历的数组元素,collection 属性值就可以写:array
open:代表foreach中语句开始的部分
close:代表foreach中语句结束的部分
item:代表遍历集合中的每个元素生成的变量名
separator:分隔符,将item中的变量进行分隔
#{}中所写的值要与item所写的值一致
-->
<foreach collection="array" open="id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
测试代码
//测试 动态sql的foreach标签:多值查询,根据多个id值查询用户,多个id组成一个数组
@Test
public void tesFindByArray() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Integer[] ids = {1,10,11};
List<User> userArray = mapper.findByArray(ids);
for (User user : userArray) {
System.out.println(user);
}
}
c)pojo
QueryVo
public class QueryVo {
private List<Integer> ids;
}
核心配置文件
<!--设置实体别名-->
<typeAliases>
<typeAlias type="com.lagou.domain.User" alias="user"></typeAlias>
<typeAlias type="com.lagou.domain.QueryVo" alias="queryVo"></typeAlias>
</typeAliases>
UserMapper 接口
public List<User> findByPojo(QueryVo queryVo);
UserMaper.xml 映射
<!--
如果查询条件为复杂类型pojo 对象,collection 属性值为:集合或数组的属性名
-->
<select id="findByPojo" parameterType="queryVo" resultType="user">
SELECT * FROM `user`
<where>
<foreach collection="ids" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
测试代码
// foreach 标签 pojo
@Test
public void testFindByPojo() throws Exception {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<Integer> ids = new ArrayList<>();
ids.add(46);
ids.add(48);
ids.add(51);
QueryVo queryVo = new QueryVo();
queryVo.setIds(ids);
List<User> list = userMapper.findByPojo(queryVo);
System.out.println(list);
}
2.3 SQL 片段
应用场景
映射文件中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
<!--在于CRUD操作的同级下,使用<sql>标签进行重复代码的抽取-->
<!--抽取重复的sql代码-->
<sql id="selectUser">
select * from user
</sql>
<!--动态sql的foreach标签:多值查询,根据多个id值查询用户,多个id组成一个集合-->
<select id="findByList" parameterType="list" resultType="User">
<!--引入被抽取的sql语句-->
<include refid="selectUser"></include>
<where>
<foreach collection="collection" open="id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
2.4 知识小结
MyBatis 映射文件配置
-
<select>:查询
-
<insert>:插入
-
<update>:修改
-
<delete>:删除
-
<selectKey>:返回主键
-
<where>:where 条件
-
<if>:if 判断
-
<foreach>:for 循环
-
<set>:set 设置
-
<sql>:sql 片段抽取
三 Mybatis 核心配置文件深入
3.1 plugins 标签
MyBatis 可以使用第三方的插件来对功能进行扩展,分页助手PageHelper 是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤:
①导入通用PageHelper 的坐标
②在mybatis 核心配置文件中配置PageHelper 插件
③测试分页数据获取
①导入通用 PageHelper 坐标
<!-- 分页助手 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
②在 mybatis 核心配置文件中配置PageHelper 插件
<plugins>
<!--配置分页-->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!--dialect:指定方言,也就是说明整个分页需要支持指定数据库的特有语法-->
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
③测试分页代码实现
//测试 分页助手
//核心配置文件深入,plugin标签配置pageHelper
@Test
public void tesPageHelper() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//要想把查询结果分页展示,就需要在查询之前,先设置分页参数
//设置分页参数
/*startPage(参数1(表示当前页),参数2(表示每页显示的条数)) */
PageHelper.startPage(2, 2);
List<User> userList = mapper.findAllResultMap();//获取所有用户
for (User user : userList) {
System.out.println(user);
}
//获取分页相关的其他数据
PageInfo<User> userPageInfo = new PageInfo<User>(userList);//将查询出来的结果集作为参数传入
//PageInfo就会根据传入的参数去计算分页的总条数,分页数等等
System.out.println("总条数:" + userPageInfo.getTotal());
System.out.println("总页数:" + userPageInfo.getPages());
System.out.println("当前页:" + userPageInfo.getPageNum());
System.out.println("是否是第一页:" + userPageInfo.isIsFirstPage());
sqlSession.close();
}
3.2 知识小结
MyBatis 核心配置文件常用标签:
1、properties 标签:该标签可以加载外部的 properties 文件
2、typeAliases 标签:设置类型别名
3、environments 标签:数据源环境配置标签
4、plugins 标签:配置MyBatis 的插件(可以自定义插件,也可以整合其他第三方插件)
四 Mybatis 多表查询
4.1 数据库表关系介绍
关系型数据库表关系分为
- 一对一
-
一对多
-
多对多
举例
-
人和身份证号就是一对一
- 一个人只能有一个身份证号
- 一个身份证号只能属于一个人
-
用户和订单就是一对多,订单和用户就是多对一
- 一个用户可以下多个订单
- 多个订单属于同一个用户
-
学生和课程就是多对多
- 一个学生可以选修多门课程
- 一个课程可以被多个学生选修
-
特例
- 一个订单只从属于一个用户,所以mybatis 将多对一看成了一对一
案例数据库环境准备
- 创建orders表
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ordertime` varchar(255) DEFAULT NULL,
`total` DOUBLE DEFAULT NULL,
`uid` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-
往rders表中插入数据
INSERT INTO `orders` VALUES ('1', '2020-12-12', '3000', '1'); INSERT INTO `orders` VALUES ('2', '2020-12-12', '4000', '1'); INSERT INTO `orders` VALUES ('3', '2020-12-12', '5000', '2');
-
创建sys_role表
DROP TABLE IF EXISTS `sys_role`; CREATE TABLE `sys_role` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `rolename` VARCHAR(255) DEFAULT NULL, `roleDesc` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-
往sys_role表中插入数据
INSERT INTO `sys_role` VALUES ('1', 'CTO', 'CTO'); INSERT INTO `sys_role` VALUES ('2', 'CEO', 'CEO');
-
创建sys_user_role表
DROP TABLE IF EXISTS `sys_user_role`; CREATE TABLE `sys_user_role` ( `userid` INT(11) NOT NULL, `roleid` INT(11) NOT NULL, PRIMARY KEY (`userid`,`roleid`), KEY `roleid` (`roleid`), CONSTRAINT `sys_user_role_ibfk_1` FOREIGN KEY (`userid`) REFERENCES `user` (`id`), CONSTRAINT `sys_user_role_ibfk_2` FOREIGN KEY (`roleid`) REFERENCES `sys_role` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
往 sys_user_role表中插入数据
INSERT INTO `sys_user_role` VALUES ('1', '1'); INSERT INTO `sys_user_role` VALUES ('2', '1'); INSERT INTO `sys_user_role` VALUES ('1', '2'); INSERT INTO `sys_user_role` VALUES ('2', '2');
4.2 一对一(多对一)multitable
4.2.1 介绍
一对一查询模型
-
用户表和订单表的关系为,一个用户有多个订单(一对多的关系),一个订单只从属于一个用户(一对一的关系)
-
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
-
一对一查询语句
SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`;
-
查询结果
- 结果的封装 需要自己手动配置resultMap
4.2.2 代码实现
1 )Order 实体
package com.myLagou.entity;
import java.util.Date;
/**
* @author zhy
* @create 2022-08-09 13:40
*/
public class Orders {
private Integer id;
private Date ordertime;
private double total;
private Integer uid;
// 表示当前订单属于哪个用户
private User user;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getOrdertime() {
return ordertime;
}
public void setOrdertime(Date ordertime) {
this.ordertime = ordertime;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
@Override
public String toString() {
return "Orders{" +
"id=" + id +
", ordertime=" + ordertime +
", total=" + total +
", uid=" + uid +
", user=" + user +
'}';
}
}
2 )OrderMapper 接口
public interface OrderMapper {
/*一对一关联查询:查询所有的订单,并且查出每个订单所属的用户信息*/
public List<Orders> findAllWithUser();
}
3 )OrderMapper.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.myLagou.mapper.OrderMapper">
<resultMap id="orderMap" type="com.myLagou.entity.Orders">
<!--配置Orders类真的主键-->
<id property="id" column="id"></id>
<!--配置其他属性-->
<result property="ordertime" column="ordertime"></result>
<result property="total" column="total"></result>
<result property="uid" column="uid"></result>
<!--配置Orders类中的User对象属性-->
<!--
association:在进行一对一关联查询配置时进行类中的对象属性的配置
property配置Orders类中要封装的关联对象user属性
javaType配置要封装的user的类型 -->
<association property="user" javaType="com.myLagou.entity.User">
<!--在配置user类的column时要注意避免与Orders类中的column混淆,
因为orders表中的外键uid就是拿user表的主键id来设置的,所以为了避免id混淆,可以直接拿uid来使用-->
<id property="id" column="uid"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
</association>
</resultMap>
<!-- /*一对一关联查询:查询所有的订单,并且查出每个订单所属的用户信息*/-->
<!--因为这查询返回的结果为Orders的一个集合,且Orders类中包含了User的信息,mybatis不能直接自动映射封装,所有需要自己手动配置resultMap-->
<select id="findAllWithUser" resultMap="orderMap">
SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`;
</select>
</mapper>
4 )测试代码
package com.myLagou.test;
import com.myLagou.entity.Orders;
import com.myLagou.entity.User;
import com.myLagou.mapper.OrderMapper;
import com.myLagou.mapper.UserMapper;
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.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @author zhy
* @create 2022-08-09 14:21
*/
public class mybatisTest {
/*一对一关联查询:查询所有的订单,并且查出每个订单所属的用户信息*/
@Test
public void testFindAllWithUser() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Orders> ordersList = mapper.findAllWithUser();
for (Orders orders : ordersList) {
System.out.println(orders);
}
sqlSession.close();
}
}
4.3 一对多
4.3.1 介绍
一对多查询模型
-
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
-
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单(从用户的角度来看是一对多)
一对多查询语句
SELECT u.*, o.id oid, o.ordertime,o.total, o.uid FROM orders o RIGHT JOIN user u ON u.`id` = o.`uid`;
查询结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p62ktvoc-1660055717105)(D:\blog\Notes\lagou\SSM\Mybatis\任务二\Mybatis 任务二:配置文件深入.assets\查询结果2.png)]
4.3.2 代码实现
1 )User 实体
package com.myLagou.entity;
import java.util.Date;
import java.util.List;
/**
* @author zhy
* @create 2022-08-07 14:11
*/
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//表示一对多中多的那一方的关系使用集合
// 代表当前用户具备的订单列表
private List<Orders> orderList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public List<Orders> getOrderList() {
return orderList;
}
public void setOrderList(List<Orders> orderList) {
this.orderList = orderList;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
", orderList=" + orderList +
'}';
}
}
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// 代表当前用户具备的订单列表
private List orderList;
}
2 )UserMapper 接口
public interface UserMapper {
/*一对多查询的需求:查询所有用户,与此同时查询出该用户具有的订单信息*/
public List<User> findAllWithOrder();
}
3 )UserMapper.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.myLagou.mapper.UserMapper">
<resultMap id="userMapper" type="com.myLagou.entity.User">
<!--为了避免出现id混淆,就sql语句执行的时候给orders表的id起别名,所以此处就使用id即可-->
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<!--
collection:一对多的查询时,使用 collection 标签关联
property="orderList" 封装到集合的属性名
ofType="order" 封装集合的泛型类型
-->
<collection property="orderList" ofType="com.myLagou.entity.Orders">
<!--配置Orders类的主键-->
<id property="id" column="oid"></id>
<!--配置其他属性-->
<result property="ordertime" column="ordertime"></result>
<result property="total" column="total"></result>
<result property="uid" column="uid"></result>
</collection>
</resultMap>
<!--/*一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单信息*/-->
<select id="findAllWithOrder" resultMap="userMapper">
SELECT u.*, o.id oid, o.ordertime,o.total, o.uid FROM orders o RIGHT JOIN user u ON u.`id` = o.`uid`;
</select>
</mapper>
4 )测试代码
/*一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单信息*/
@Test
public void testFindAllWithOrder() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findAllWithOrder();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
4.4 多对多
4.4.1 介绍
多对多查询的模型
-
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
-
多对多查询的需求:查询用户同时查询出该用户的所有角色
-
多对多查询语句
- 先分开查询 先查出所有的用户的id对应的roleid
- 将上述查询结果当作一张虚拟表再去与用户角色role表进行关联查询
- 最终的sql语句为:
select u.*, r.id rid, r.rolename, r.roleDesc from user u
left join sys_user_role ur on ur.userid = u.id
left join sys_role r on ur.roleid = r.id;
4.4.2 代码实现
1 )User 和Role 实体
User实体类
package com.myLagou.entity;
import java.util.Date;
import java.util.List;
/**
* @author zhy
* @create 2022-08-07 14:11
*/
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//表示一对多中多的那一方的关系使用集合
// 代表当前用户具备的订单列表
private List<Orders> orderList;
// 代表当前用户关联的角色列表
private List<Role> roleList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public List<Orders> getOrderList() {
return orderList;
}
public void setOrderList(List<Orders> orderList) {
this.orderList = orderList;
}
public List<Role> getRoleList() {
return roleList;
}
public void setRoleList(List<Role> roleList) {
this.roleList = roleList;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
", orderList=" + orderList +
", roleList=" + roleList +
'}';
}
}
Role实体类
package com.myLagou.entity;
/**
* @author zhy
* @create 2022-08-09 15:38
*/
public class Role {
private Integer id;
private String rolename;
private String roleDesc;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRolename() {
return rolename;
}
public void setRolename(String rolename) {
this.rolename = rolename;
}
public String getRoleDesc() {
return roleDesc;
}
public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}
@Override
public String toString() {
return "Role{" +
"id=" + id +
", rolename='" + rolename + '\'' +
", roleDesc='" + roleDesc + '\'' +
'}';
}
}
2 )UserMapper 接口
public interface UserMapper {
/*多对多关联查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/
public List<User> findAllWithRole();
}
3 )UserMapper.xml 映射
<!--多对多的resultMap配置-->
<!--要封装到user实体类上,所以type为user-->
<resultMap id="userRoleMap" type="com.myLagou.entity.User">
<!--设置User表中的主键id-->
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<collection property="roleList" ofType="com.myLagou.entity.Role">
<!--配置Role类的主键-->
<id property="id" column="rid"></id>
<!--配置其他属性-->
<result property="rolename" column="rolename"></result>
<result property="roleDesc" column="roleDesc"></result>
</collection>
</resultMap>
<!--/*多对多关联查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/-->
<select id="findAllWithRole" resultMap="userRoleMap">
select u.*, r.id rid, r.rolename, r.roleDesc from user u
left join sys_user_role ur on ur.userid = u.id
left join sys_role r on ur.roleid = r.id;
</select>
4 )测试代码
/*多对多关联查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/
@Test
public void testFindAllWithRole() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
/*mapper要调用的方法在哪个XxxMapper中,就使用哪个类,所以使用UserMapper.class*/
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> allWithRole = mapper.findAllWithRole();
for (User user : allWithRole) {
System.out.println(user);
}
sqlSession.close();
}
public void testUserWithRole() throws Exception {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List list = userMapper.findAllWithRole();
for (User user : list) {
System.out.println(user);
}
}
4.5 小结
MyBatis 多表关联配置方式
* 一对一配置:使用<resultMap>+<association>做配置
* 一对多配置:使用<resultMap>+<collection>做配置
* 多对多配置:使用<resultMap>+<collection>做配置
* 多对多的配置跟一对多很相似,难度在于SQL 语句的编写。
五 MyBatis 嵌套查询
5.1 什么是嵌套查询
嵌套查询就是将原来多表查询中的联合查询语句拆成单个表的查询,再使用 mybatis 的语法嵌套在一起。
举个栗子
* 需求:查询一个订单,与此同时查询出该订单所属的用户
1. 联合查询
SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`;
2. 嵌套查询
2.1 先查询订单
SELECT * FROM orders
2.2 再根据订单uid 外键,查询用户
SELECT * FROM `user` WHERE id = #{根据订单查询的 uid}
2.3 最后使用mybatis,将以上二步嵌套起来
...
5.2 一对一嵌套查询
5.2.1 介绍
需求:查询一个订单,与此同时查询出该订单所属的用户
一对一查询语句
-- 先查询订单
SELECT * FROM orders;
-- 再根据订单 uid 外键,查询用户
SELECT * FROM `user` WHERE id = #{订单的uid};
5.2.2 代码实现
1) OrderMapper 接口
public interface OrderMapper {
/*一对一嵌套查询:查询所有的订单,并且查出每个订单所属的用户信息*/
public List<Orders> findAllWithUser2();
}
2) OrderMapper.xml 映射
<resultMap id="orderMap2" type="com.myLagou.entity.Orders">
<!--配置Orders类真的主键-->
<id property="id" column="id"></id>
<!--配置其他属性-->
<result property="ordertime" column="ordertime"></result>
<result property="total" column="total"></result>
<result property="uid" column="uid"></result>
<!--存在问题
问题1:怎么去执行嵌套的第二条sql? ————通过select属性
问题2:在执行第二条sql的时候,如何把第一条sql查询的结果uid作为参数传递给第二条sql ————通过column
select属性表示引入UserMapper.xml中的findById的查询语句
column表示把第一条sql查询出来的uid作为参数传递给第二条sql语句,也就是传给select属性中的sql语句
-->
<association property="user" javaType="com.myLagou.entity.User"
select="com.myLagou.mapper.UserMapper.findById" column="uid">
</association>
</resultMap>
<!-- /*一对一嵌套查询:查询所有的订单,并且查出每个订单所属的用户信息*/-->
<select id="findAllWithUser2" resultMap="orderMap2">
select * from orders
</select>
编写完OrderMapper这部分内容后,就把需要嵌套的第二条sql语句需要的代码编写到UserMapper部分中去,在OrderMapper.xml文件中通过association标签中的属性select来从UserMapper.xml中引入sql,使用column将第一条sql查询出来的uid当作结果传递给select属性
3) UserMapper 接口
public interface UserMapper {
//为了实现一对一嵌套查询
/*根据id查询用户*/
public User findById(Integer id);
}
4) UserMapper.xml 映射
<!--/*根据id查询用户*/-->
<select id="findById" resultType="user" parameterType="int">
select * from user where id = #{id}
</select>
5)测试代码
/*一对一嵌套查询:查询所有的订单,并且查出每个订单所属的用户信息*/
@Test
public void testFindAllWithUser2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
/*mapper要调用的方法在哪个XxxMapper中,就使用哪个类,所以使用UserMapper.class*/
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Orders> ordersList = mapper.findAllWithUser2();
for (Orders orders : ordersList) {
System.out.println(orders);
}
sqlSession.close();
}
5.3 一对多嵌套查询
5.3.1 介绍
需求:查询一个用户,与此同时查询出该用户具有的订单
一对多查询语句
-- 先查询用户
SELECT * FROM `user`;
-- 再根据用户 id 主键,查询订单列表
SELECT * FROM orders where uid = #{用户 id};
5.3.2 代码实现
a)UserMapper 接口
public interface UserMapper {
/*一对多嵌套查询的需求:查询所有用户,与此同时查询出该用户具有的订单信息*/
public List<User> findAllWithOrder2();
}
b)UserMapper.xml 映射
<!--一对多的嵌套查询-->
<resultMap id="userOrderMap" type="com.myLagou.entity.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<!--配置的select与column与之前一对一嵌套查询的作用一样-->
<collection property="orderList" ofType="com.myLagou.entity.Orders" column="id"
select="com.myLagou.mapper.OrderMapper.findAllByUid">
</collection>
</resultMap>
<!-- /*一对多嵌套查询的需求:查询所有用户,与此同时查询出该用户具有的订单信息*/-->
<select id="findAllWithOrder2" resultMap="userOrderMap">
select * from user ;
</select>
c)OrderMapper*接口
public interface OrderMapper {
/*根据uid查询该用户对应的所有订单*/
public List<Orders> findAllByUid(Integer id);
}
d)OrderMapper.xml映射
<!-- /*根据uid查询该用户对应的所有订单*/-->
<select id="findAllByUid" parameterType="int" resultType="com.myLagou.entity.Orders">
select * from orders where uid = #{uid}
</select>
e)测试代码
/*一对多嵌套查询的需求:查询所有用户,与此同时查询出该用户具有的订单信息*/
@Test
public void testFindAllWithOrder2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
/*mapper要调用的方法在哪个XxxMapper中,就使用哪个类,所以使用UserMapper.class*/
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findAllWithOrder2();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
5.4 多对多嵌套查询
5.4.1 介绍
需求:查询用户同时查询出该用户的所有角色
多对多查询语句
-- 先查询用户
SELECT * FROM `user`;
-- 再根据用户 id 主键,查询角色列表
SELECT * FROM role r INNER JOIN user_role ur ON r.`id` = ur.`rid`
WHERE ur.`uid` = #{用户 id};
5.4.2 代码实现
a) UserMapper 接口
public interface UserMapper {
/*多对多嵌套查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/
public List<User> findAllWithRole2();
}
b)UserMapper.xml 映射
<!--多对多嵌套查询-->
<resultMap id="userRole" type="com.myLagou.entity.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<collection property="roleList" ofType="com.myLagou.entity.Role" column="id"
select="com.myLagou.mapper.RoleMapper.findByUid">
</collection>
</resultMap>
<!-- /*多对多嵌套查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/-->
<select id="findAllWithRole2" resultMap="userRole">
select * from user
</select>
c) RoleMapper 接口
public interface RoleMapper {
/*根据用户id来查询对应的角色*/
public List<Role> findByUid(Integer uid);
}
d) RoleMapper.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.myLagou.mapper.RoleMapper">
<!--/*根据用户id来查询对应的角色*/
public List<Role> findByUid(Integer uid);当方法传递的参数为基本数据类型或者为String,且只有一个参数时,parameterType可以省略不写
-->
<select id="findByUid" resultType="role" >
select * from sys_role r inner join sys_user_role ur on ur.roleid = r.id
where ur.userid = #{uid}
</select>
</mapper>
e)测试代码
/*多对多嵌套查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/
@Test
public void testFindAllWithRole2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
/*mapper要调用的方法在哪个XxxMapper中,就使用哪个类,所以使用UserMapper.class*/
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findAllWithRole2();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
5.5 小结
-
一对一配置:使用<resultMap>+<association>做配置,通过column 条件,执行 select 查询
-
一对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询
-
多对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询
-
优点:简化多表查询操作
有的角色信息*/
public List findAllWithRole2();
}
**b)UserMapper.xml** **映射**
```xml
<!--多对多嵌套查询-->
<resultMap id="userRole" type="com.myLagou.entity.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<collection property="roleList" ofType="com.myLagou.entity.Role" column="id"
select="com.myLagou.mapper.RoleMapper.findByUid">
</collection>
</resultMap>
<!-- /*多对多嵌套查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/-->
<select id="findAllWithRole2" resultMap="userRole">
select * from user
</select>
c) RoleMapper 接口
public interface RoleMapper {
/*根据用户id来查询对应的角色*/
public List<Role> findByUid(Integer uid);
}
d) RoleMapper.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.myLagou.mapper.RoleMapper">
<!--/*根据用户id来查询对应的角色*/
public List<Role> findByUid(Integer uid);当方法传递的参数为基本数据类型或者为String,且只有一个参数时,parameterType可以省略不写
-->
<select id="findByUid" resultType="role" >
select * from sys_role r inner join sys_user_role ur on ur.roleid = r.id
where ur.userid = #{uid}
</select>
</mapper>
e)测试代码
/*多对多嵌套查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/
@Test
public void testFindAllWithRole2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
/*mapper要调用的方法在哪个XxxMapper中,就使用哪个类,所以使用UserMapper.class*/
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findAllWithRole2();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
5.5 小结
-
一对一配置:使用<resultMap>+<association>做配置,通过column 条件,执行 select 查询
-
一对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询
-
多对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询
-
优点:简化多表查询操作
-
缺点:执行多次sql 语句,浪费数据库性能