目录
第一节 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的用户的订单信息才去查询加载它(懒加载)