Mybatis动态sql、if与where的使用、sql片段、foreach遍历、Mybatis的关联查询一对一、一对多、多对多、Mybatis的延时加载
程序员文章站
2022-03-10 15:25:55
目录第一节 Mybatis的动态SQL1.1 if 和where的使用1.2 SQL片断第一节 Mybatis的动态SQL1.1 if 和where的使用if标签:是作为判断参数来使用的,如果符合条件,就把if标签体内的SQL拼接上,不符合条件就不拼接注意:用if进行判断是否为空时,不仅要判断null,还要判断空字符串’'where标签:会去掉条件中的第一个and符号在UserMapper.java接口中写一个方法在UserMapper.xml中配置sql测试与效果packag...
目录
第一节 Mybatis的动态SQL
1.1 if 和where的使用
- if标签:是作为判断参数来使用的,如果符合条件,就把if标签体内的SQL拼接上,不符合条件就不拼接
- 注意:用if进行判断是否为空时,不仅要判断null,还要判断空字符串’'
- where标签:会去掉条件中的第一个and符号
-
在UserMapper.java接口中写一个方法
-
在UserMapper.xml中配置sql
- 测试与效果
package com.it.test; import com.it.mapper.UserMapper; import com.it.model.User; import com.it.vo.UserQueryVo; 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.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; /**
* @ClassName Demo02
* @Author shuyy
* @Date 2020/9/22
**/ public class Demo01 { SqlSession sqlSession; @Before public void before() throws IOException { System.out.println("before...获取session"); //1.读取配置文件 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.通过SqlSessionFactoryBuilder创建SqlSessionFactory会话工厂 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //3.通过SqlSessionFactory创建SqlSession sqlSession = sessionFactory.openSession(); } @After public void after(){ System.out.println("after...关闭session"); //5.关闭SqlSession sqlSession.close(); } @Test public void test1(){ UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //System.out.println(userMapper);//返回的是一个代理对象 //查询条件 UserQueryVo queryVo = new UserQueryVo(); User user = new User(); user.setSex("男"); user.setUsername("shu04"); queryVo.setUser(user); List<User> list = userMapper.findUserList(queryVo); System.out.println(list); //sqlSession.commit();//增删改记得提交事务 } }
-
如果注释了姓名设置,会查询所有符合性别条件的
-
如果注释了性别设置,虽然不会报错,但是查询不到结果,这是由于sql所致
-
为了正确的使用sql,使用if与where来拼接sql,如果要查询的字段值为空,就不把该字段的条件加到sql上,起到动态拼接sql的效果
<!--if与where的使用--><!--查询性别和名字--> <select id="findUserList" parameterType="UserQueryVo" resultType="user"> select * from user <where> <if test="user != null"> <if test="user.sex != null and user.sex != ''"> sex = #{user.sex} </if> <if test="user.username != null and user.username != ''"> and username like '%${user.username}%' </if> </if> </where> </select>
-
注释一些配置的效果,就算注释了设置user,user为空也不报错
-
where会去除条件中第一个and符号(上面的语句中写的是and username like ‘%${user.username}%’),拼接后的语句中去除了and,防止了SQL错误
-
注释了条件,就不拼接到对应的sql上
1.2 SQL片断
-
Mybatis提供了SQL片段的功能,可以提高SQL的可重用性。
<!--if与where的使用--><!--查询性别和名字--> <!--声明一个sql片段--> <sql id="select_user_where"> <if test="user != null"> <if test="user.sex != null and user.sex != ''"> sex = #{user.sex} </if> <if test="user.username != null and user.username != ''"> and username like '%${user.username}%' </if> </if> </sql> <select id="findUserList" parameterType="UserQueryVo" resultType="user"> select * from user <where> <!--引用sql片段--> <include refid="select_user_where"/> </where> </select>
-
成功执行
1.3 foreach遍历
-
先在包装类中提供一个List,并提供get/set
-
在UserMapper.java中提供一个方法
-
在UserMapper.xml中提供对应的sql
<!--foreach的使用--> <select id="findUserByIds" parameterType="userQueryVo" resultType="user"> select * from user <where> <if test="ids != null and ids.size > 0"> <!--
foreach标签:表示一个foreach循环
collection:集合参数名称,如果是直接传入集合参数,则该处的参数名称只能填 list
item:每次遍历出来的对象
open:开始遍历时拼接的串(sql)
close:结束遍历时拼接的串(sql)
separator:遍历出的每个对象之间需要拼接的字符
--> <foreach collection="ids" item="id" open="id in (" separator="," close=")"> ${id} </foreach> </if> </where> </select>
- 测试
package com.it.test; import com.it.mapper.UserMapper; import com.it.model.User; import com.it.vo.UserQueryVo; 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.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; /**
* @ClassName Demo02
* @Author shuyy
* @Date 2020/9/22
**/ public class Demo02 { SqlSession sqlSession; @Before public void before() throws IOException { System.out.println("before...获取session"); //1.读取配置文件 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.通过SqlSessionFactoryBuilder创建SqlSessionFactory会话工厂 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //3.通过SqlSessionFactory创建SqlSession sqlSession = sessionFactory.openSession(); } @After public void after(){ System.out.println("after...关闭session"); //5.关闭SqlSession sqlSession.close(); } @Test public void test1(){ //通过session获取代理【jdk实现的代理】 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //System.out.println(userMapper);//返回的是一个代理对象 //查询条件 UserQueryVo queryVo = new UserQueryVo(); List<Integer> ids = new ArrayList<Integer>(); ids.add(34); ids.add(35); ids.add(36); queryVo.setIds(ids); List<User> list = userMapper.findUserByIds(queryVo); for (User user : list) { System.out.println(user); } //sqlSession.commit();//增删改记得提交事务 } }
参数是数组的遍历
- 发现上面的使用有一些麻烦,直接在参数中传入数组
-
在UserMapper.java中提供方法
-
在UserMapper.xml中提供对应的sql
-
测试
第二节 Mybatis的关联查询
2.1 案例:用户与订单
-
在day01数据库表的基础上实现
- user和orders:
- User 与orders:一个用户可以创建多个订单,一对多
- Orders 与 user:多个订单可由一个用户创建,多对一
- orders和orderdetail:
- Orders 与 orderdetail:一个订单可以包括 多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多关系
- orderdetail 与orders:多个订单明细包括在一个订单中, 多对一
- orderdetail和items:
- Orderdetail 与 items:多个订单明细只对应一个商品信息,多对一
- Items 与 orderdetail:一个商品可以包括在多个订单明细 ,一对多
-
需求:根据用户id查找订单信息,包括用户名和地址
#查找id为1的所有订单 SELECT orders.id, orders.number, orders.createtime, orders.note, USER.username, USER.address FROM orders, USER WHERE orders.user_id = USER.id AND USER.id = 1;
2.2 一对一(resultType实现)
- 复杂查询时,单表对应的po类已不能满足输出结果集的映射,所以要根据需求建立一个扩展类来作为resultType的类型。
-
例如:查找某个订单id的信息,包括用户名和地址
#查找某个订单id的信息,包括用户名和地址 SELECT o.*, u.username, u.address FROM orders o, USER u WHERE o.user_id = u.id AND o.id = 3
- 单个orders表的类型已无法满足,所以创建一个扩展类来满足需求
第一步:写一个订单类的扩展类
-
原来的订单类,提供get/set,toString
-
订单扩展类OrdersExt,提供属性的get/set,toString(在toString中调用父类orders的toString)
第二步:声明订单接口
第三步:声明订单配置文件
<?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.it.mapper.OrderMapper"> <select id="findOrderById" parameterType="int" resultType="ordersExt"> select
o.*,u.username,u.address
from
orders o,user u
where
o.user_id = u.id
and o.id = #{id} </select> </mapper>
第四步:加载映射文件
第五步:测试
package com.it.test; import com.it.mapper.OrderMapper; import com.it.model.OrdersExt; 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.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; /**
* @ClassName Demo02
* @Author shuyy
* @Date 2020/9/22
**/ public class Demo03 { SqlSession sqlSession; @Before public void before() throws IOException { System.out.println("before...获取session"); //1.读取配置文件 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.通过SqlSessionFactoryBuilder创建SqlSessionFactory会话工厂 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //3.通过SqlSessionFactory创建SqlSession sqlSession = sessionFactory.openSession(); } @After public void after(){ System.out.println("after...关闭session"); //5.关闭SqlSession sqlSession.close(); } @Test public void test1(){ //通过session获取代理【jdk实现的代理】 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); //System.out.println(userMapper);//返回的是一个代理对象 //查询条件 OrdersExt orderById = orderMapper.findOrderById(3); System.out.println(orderById); //sqlSession.commit();//增删改记得提交事务 } }
2.3 一对一(resultMap实现)
- 其实就是在模型里使用模型,association的使用
第一步:在Orders类中添加User模型,并提供get/set
-
User 订单所属的用户
第二步:在订单Mapper接口中写一个方法
第三步:在订单配置文件中配置resultMap
<!--如果模型里有模型,使用resultMap--> <resultMap id="ordersResultMap" type="orders"> <!--与orders模型匹配数据--> <id column="id" property="id"></id> <id column="note" property="note"></id> <id column="number" property="number"></id> <id column="createtime" property="createtime"></id> <!--orders的user匹配数据
模型里有模型,使用association来配置
--> <association property="user" javaType="user"> <id column="user_id" property="id"></id> <id column="username" property="username"></id> <id column="address" property="address"></id> </association> </resultMap> <select id="findOrderByMap" parameterType="int" resultMap="ordersResultMap"> select
o.*,u.username,u.address
from
orders o,user u
where
o.user_id = u.id
and o.id = #{id} </select>
第四步:测试
@Test public void test2(){ //通过session获取代理【jdk实现的代理】 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); //System.out.println(userMapper);//返回的是一个代理对象 //查询条件 Orders order = orderMapper.findOrderByMap(3); System.out.println(order); System.out.println(order.getUser()); //sqlSession.commit();//增删改记得提交事务 }
总结
-
resultType:resultType的使用较为简单,如果pojo中没有包括查询出的列名,可以通过增加对应属性的列名来完成映射。如果对于查询结果没有特殊的要求,建议使用resultType。
-
resultMap:需要单独定义resultMap,实现起来有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以将关联查询映射到pojo对象的属性中。
-
resultMap可以实现延迟加载,resultType则无法实现延迟加载。
2.4 一对多(resultMap+collection)
- 其实就是在模型中使用集合,即collection的使用
-
例如:根据订单id查找订单信息、用户信息和订单明细信息
#根据订单id查找订单信息、用户信息和订单明细信息 SELECT o.*, u.username, u.address, od.id, od.items_id, od.items_num FROM orders o, USER u, orderdetail od WHERE o.user_id = u.id AND o.id = od.orders_id AND o.id = 3
第一步:写一个订单详情类
-
属性与数据库字段对应,并提供get/set,toString
package com.it.model; public class OrdersDetail { private Integer id;//订单id private Integer items_id;//商品id private Integer items_num;//商品购买数量 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getItems_id() { return items_id; } public void setItems_id(Integer items_id) { this.items_id = items_id; } public Integer getItems_num() { return items_num; } public void setItems_num(Integer items_num) { this.items_num = items_num; } @Override public String toString() { return "OrdersDetail{" + "id=" + id + ", items_id=" + items_id + ", items_num=" + items_num + '}'; } }
第二步:Orders中添加订单详情
-
并提供get/set
第三步:OrderMapper接口中写方法
第四步:OrderMapper.xml中添加配置
<!--一对多--> <resultMap id="ordersResultMap2" type="orders"> <!--与orders模型匹配数据--> <id column="id" property="id"></id> <id column="note" property="note"></id> <id column="number" property="number"></id> <id column="createtime" property="createtime"></id> <!--orders的user匹配数据
模型里有模型,使用association来配置
--> <association property="user" javaType="user"> <id column="user_id" property="id"></id> <id column="username" property="username"></id> <id column="address" property="address"></id> </association> <!--一对多匹配集合:往orders的orderDetails 匹配数据
注意:集合里类型使用ofType,而不是javaType
--> <collection property="ordersDetailList" ofType="ordersDetail"> <id property="id" column="id"></id> <id property="items_id" column="items_id"></id> <id property="items_num" column="items_num"></id> </collection> </resultMap> <select id="findOrderByIdsMap" parameterType="int" resultMap="ordersResultMap2"> select
o.*,
u.username,
u.address,
od.id,
od.items_id,
od.items_num
from
orders o,
user u,
orderdetail od
where
o.user_id = u.id
and o.id = od.orders_id
and o.id = #{id} </select>
第五步:测试
@Test public void test3(){ /*一对多:模型里有集合*/ //通过session获取代理【jdk实现的代理】 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); //System.out.println(userMapper);//返回的是一个代理对象 //查询条件 Orders order = orderMapper.findOrderByIdsMap(3); System.out.println(order); System.out.println(order.getUser()); System.out.println(order.getOrdersDetailList()); //sqlSession.commit();//增删改记得提交事务 }
2.5 多对多
-
例如:查询用户信息及用户购买的商品信息,要求将关联信息映射到主pojo的pojo属性中
- 映射思路
- 将用户信息映射到user中。
- 在user类中添加订单列表属性List<Orders> orderslist,将用户创建的订单映射到orderslist
- 在Orders中添加订单明细列表属性List<Orderdetail> detailList,将订单的明细映射到detailList
- 在Orderdetail中添加Items属性,将订单明细所对应的商品映射到Items
#查询用户信息及用户购买的商品信息 SELECT u.id, u.username, u.address, o.id, o.number, o.createtime, o.note, od.id detail_id, od.items_id, od.items_num, it.name, it.price, it.detail FROM orders o, USER u, orderdetail od, items it WHERE o.user_id = u.id AND o.id = od.orders_id AND od.items_id = it.id
第一步:UserMapper接口中添加方法
第二步:User/Orders/Orderdetail.java
-
User中添加订单,并提供get/set
-
Orders中还是保持之前添加的订单明细,提供get/set
-
写一个商品详情类Items,属性与数据库字段对应
-
这里就提供自己需要的属性,并提供get/set、toString
-
在订单详情(Orderdetail)中添加商品详情(Items),并提供get/set
第三步:配置UserMapper.xml
<!--查询用户信息及用户购买的商品信息--> <resultMap id="userResultMap2" type="user"> <!--1.匹配user属性--> <id column="id" property="id"></id> <result column="username" property="username"></result> <result column="address" property="address"></result> <!--2.匹配user的orderList属性--> <collection property="ordersList" ofType="orders"> <id property="id" column="order_id"></id> <result column="number" property="number"></result> <result column="createtime" property="createtime"></result> <result column="note" property="note"></result> <!--集合里嵌套集合--> <!--3.匹配Orders里的ordersDetail--> <collection property="ordersDetailList" ofType="ordersDetail"> <id property="id" column="detail_id"></id> <result property="items_id" column="items_id"></result> <result property="items_num" column="items_num"></result> <!--4.配置订单详细的商品信息--><!--集合中嵌套association--> <association property="items" javaType="items"> <id property="id" column="items_id"></id> <result property="name" column="name"></result> <result property="price" column="price"></result> <result property="detail" column="detail"></result> </association> </collection> </collection> </resultMap> <select id="findUserAndOrderInfo" resultMap="userResultMap2"> SELECT
u.id,
u.username,
u.address,
o.id order_id,
o.number,
o.createtime,
o.note,
od.id detail_id,
od.items_id,
od.items_num,
it.name,
it.price,
it.detail
FROM
orders o,
USER u,
orderdetail od,
items it
WHERE
o.user_id = u.id
AND o.id = od.orders_id
AND od.items_id = it.id </select>
第四步:测试
@Test public void test4(){ /*多对多*/ //通过session获取代理【jdk实现的代理】 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //System.out.println(userMapper);//返回的是一个代理对象 //查询条件 List<User> users = userMapper.findUserAndOrderInfo(); for (User user : users) { System.out.println("用户信息:"+user); for (Orders orders : user.getOrdersList()) { System.out.println("订单信息:"+orders); for (OrdersDetail ordersDetail : orders.getOrdersDetailList()) { System.out.println("订单详情:"+ordersDetail+":"+ordersDetail.getItems()); } System.out.println("---------------------------"); } } //sqlSession.commit();//增删改记得提交事务 }
第五步:测试单个用户
-
UserMapper接口中添加方法
-
修改UserMapper.xml,上面的resultMap复制一份改成userResultMap3即可
-
测试
@Test public void test5(){ /*多对多*/ //通过session获取代理【jdk实现的代理】 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //System.out.println(userMapper);//返回的是一个代理对象 //查询条件 User user = userMapper.findUserAndOrderInfo1(1); System.out.println("用户信息:"+user); for (Orders orders : user.getOrdersList()) { System.out.println("订单信息:" + orders); for (OrdersDetail ordersDetail : orders.getOrdersDetailList()) { System.out.println("订单详情:" + ordersDetail + ":" + ordersDetail.getItems()); } System.out.println("---------------------------"); } //sqlSession.commit();//增删改记得提交事务 }
总结
- resultType:将查询结果按照sql列名与pojo属性名的一致性映射到pojo中。
-
resultMap:使用association和collection完成一对一和一对多高级映射(对结果有特殊的映射要求)。
- association:将关联查询信息映射到一个pojo对象中。
- collection:将关联查询信息映射到一个list集合中。
第三节 延时加载
- 延迟加载又叫懒加载,也叫按需加载。也就是说先加载主信息,在需要的时候,再去加载从信息。
- 在mybatis中,resultMap标签的association标签和collection标签具有延迟加载的功能。
3.1 使用懒加载
第一步:在UserMapper中查看之前写的方法并配置
-
如果没有就添加上
-
在配置文件中配置
第二步:在OrderMapper中写一个方法并配置
-
在OrderMapper.xml中配置(通过id查询将数据映射到user中)
-
Orders中有user_id属性
<!--懒加载--> <resultMap id="lazyLoadingResultMap" type="orders"> <id column="id" property="id"></id> <id column="note" property="note"></id> <id column="number" property="number"></id> <id column="createtime" property="createtime"></id> <association property="user" select="com.it.mapper.UserMapper.findUserById" column="user_id"></association> </resultMap> <select id="findOrderAndByLazyLoading" resultMap="lazyLoadingResultMap"> SELECT * FROM orders </select>
第三步:测试(此时还不是懒加载)
-
注意观察sql语句的执行与执行结果,执行查询Orders后就立即查询用户user,也就是默认的即时加载
@Test public void test6() { /*懒加载*/ OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); List<Orders> list = orderMapper.findOrderAndByLazyLoading(); for (Orders orders : list) { System.out.println("订单信息:"+orders); System.out.println("订单所属用户:"+orders.getUser()); } }
第四步:在全局配置文件中配置懒加载
<!--配置懒加载--> <settings> <setting name="lazyLoadingEnabled" value="true"/> </settings>
-
配置完成后再执行一次,对比效果,第一次查询所有的订单,然后再查询id为1用户的订单信息(有2个订单),当使用到id为10的用户的订单信息才去查询加载它(懒加载)
本文地址:https://blog.csdn.net/qq_43414199/article/details/108784802