文章目录
1 一对一延迟加载
1.1 概述
订单与用户的关系:1个订单记录只能对应一个用户
- 针对的是关联对象
用户和订单从面相对象的角度来说就是关联对象,当只需要订单数据,尚不需要用户数据的时候,就不应该去查询用户表,啥时候用到用户数据,啥时候查询
- 一对一延迟加载
关联的sql语句肯定要拆分了
- association标签
- column属性:关联查询条件的属性
- select属性:指定另一个查询语句
1.2 延迟加载
一起查的sql语句:
SELECT u.id uid,u.username,u.sex,u.birthday,u.address,o.id,o.user_id,o.number,o.createtime,o.note
FROM orders o LEFT OUTER JOIN USER u ON u.id = o.user_id;
实现延迟加载,拆分SQL语句
拆分的是订单表的查询
<select id="queryOrdersUser" resultMap="queryOrdersUserResultMap">
SELECT o.id,o.user_id,o.number,o.createtime,o.note FROM orders o
</select>
手动配置映射:
<!--手动映射配置,数据表的列和pojo对象属性对应-->
<resultMap id="queryOrdersUserResultMap" type="orders">
<!--配置orders-->
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!--
配置用户数据,orders对象中包含user
association 配置对应关系
property 属性: 和orders对象的哪个属性关联
javaType 属性: 属性数据类型
column 属性: 列的意思,使用那个列作为条件进行第二次查询
select 属性: 封装的第二次查询SQL语句
-->
<association property="user" javaType="user" column="user_id" select="queryUserByUserId">
<!-- <id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>-->
</association>
</resultMap>
第二次查询的SQL语句
<!--拆分后的第二次SQL语句,查询用户,条件是用户的主键 = 订单表的user_id-->
<select id="queryUserByUserId" parameterType="Integer" resultType="user">
SELECT u.id,u.username,u.sex,u.birthday,u.address from user u where id = #{id}
</select>
@Test
/**
* 查询语句,SQL查询了订单和用户
* 运行时期,输出订单数据,没有用户数据
* 查询用户数据是浪费资源
* 延迟查询,需要的就查,不需要不查,不用该代码
*/
public void testQueryOrdersUser(){
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
List<Orders> ordersList = mapper.queryOrdersUser();
for (int i = 0; i < ordersList.size(); i++) {
Orders orders = ordersList.get(i);
//输出对象,打印toString()
System.out.println(orders.getId()+"::"+orders.getUserId()+"::"+orders.getNumber()+"::"+orders.getUser());
// System.out.println(orders);
}
}
2 开启延迟加载配置
<settings>
<!-- 开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 关闭立即加载-->
<setting name="aggressiveLazyLoading" value="false" />
<!-- 设定tostring等方法延迟加载-->
<setting name="lazyLoadTriggerMethods" value="true" />
</settings>
3 一对多延迟加载
一个用户对应多个订单:
SELECT u.id uid,u.username,u.sex,u.birthday,u.address,o.id,o.user_id,o.number,o.createtime,o.note
FROM USER u LEFT OUTER JOIN orders o ON u.id = o.user_id;
以用户为基准,查询订单,一对多查询
SQL语句的拆分,拆分为查询用户
<select id="queryUserOrders" resultMap="queryUserOrdersResultMap">
select u.id uid,u.username,u.sex,u.birthday,u.address from user u
</select>
配置一对多映射:
<!--一对多查询手动映射-->
<resultMap id="queryUserOrdersResultMap" type="user">
<!--配置User表-->
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
<!--
配置的是User对象的属性 ,不是单一的对象,是集合容器
collection 配置的是一对多
属性property: 配置pojo对象的属性
属性ofType:集合泛型
属性column: 以上一个SQL的那个列的结果,进行第二次查询
-->
<collection property="ordersList" ofType="orders" column="uid" select="queryOrderByUserId">
<!--<id column="id" property="id"/>-->
<!--<result column="user_id" property="userId"/>-->
<!--<result column="number" property="number"/>-->
<!--<result column="createtime" property="createtime"/>-->
<!--<result column="note" property="note"/>-->
</collection>
</resultMap>
配置第二次查询语句:
<!--
配置第二次查询语句
queryUserByUserId
-->
<select id="queryOrderByUserId" parameterType="Integer" resultType="orders">
SELECT o.id,o.user_id,o.number,o.createtime,o.note FROM orders o where user_id=#{id}
</select>
@Test
/**
* 多表查询
* 用户对订单的一对多查询
*/
public void testQueryUserOrders(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.queryUserOrders();
for (int i = 0; i < userList.size(); i++) {
User user = userList.get(i);
System.out.println(user.getUsername()+"::"+user.getOrdersList());
}
}
4 MyBatis缓存
4.1 MyBatis一级缓存(同一个sqlsession)
一级缓存:是SqlSession级别的,也就是同一个SqlSession内执行相同select语句的时候,不再去查询数据库,而是从Mybatis内部的缓存内存结构去直接拿到数据。
- 缓存失效时机:
- sqlSession关闭
- sqlSession提交事务(意味着可能是一个增删改的动作,需要更新缓存,那么这个时候Mybatis就会把已有的一级缓存给清理掉)
一级缓存针对SQLSession对象
4.2 MyBatis二级缓存(不同的sqlsession)
二级缓存针对:xxxMapper.xml文件
二级缓存默认是关闭的。
有风险:第一次查完,缓存中就有了,第二次将要查,然后有人执行了update操作,这样第二次读取的是缓存中的未更新的。不让使用二级缓存(有风险)。
5 MyBatis注解开发
注解开发: 注解取消xml配置文件
5.1 注解实现添加数据
@Insert注解
- 注解属性value:写入SQL语句
@Options注解(功能一样:可替代SelectKey注解)
- 实现添加新数据的主键封装
- 注解属性
- useGeneratedKeys:使用生成的主键,配置为true
- keyProperty:主键封装的pojo对象属性
@SelectKey注解
- 实现添加新数据的主键封装
- 注解属性
- statement:要执行的SQL语句
- before:在添加SQL语句之前还是之后进行,配置为false
- keyProperty:主键封装的pojo对象属性
@Insert("insert into user values (null,#{username},#{sex},#{birthday},#{address})")
@Options(useGeneratedKeys = true,keyProperty="id")
//@SelectKey(statement = "SELECT LAST_INSERT_ID ()",before = false,keyProperty = "id",resultType = Integer.class)
int saveUser(User user);
5.2 注解实现更新数据
@Update注解
- 注解属性value:写入SQL语句
/**
* 更新数据 @Update注解
*/
@Update("update user set username=#{username},sex=#{sex},address=#{address} where id=#{id}")
int updateUser(User user);
5.3 注解实现删除数据
注解属性value:写入SQL语句
//删除用户
@Delete("delete from user where id = #{id}")
void deleteUser(int id);
动态sql:
/**
* 根据多个主键删除用户
* 标签 foreach
*/
@Delete("<script>delete from user where id in <foreach" +
" collection=\"list\" open=\"(\" close=\")\" separator=\",\" item=\"id\">" +
"#{id}" +
"</foreach></script>")
int deleteUserById(List<Integer> list);
5.4 注解实现主键查询用户数据
@Select注解
- 注解属性value:写入SQL语句
/**
* 定义方法,根据主键查询用户
*/
@Select("select id ,username,sex,birthday,address from user where id = #{id} ")
User queryUserById(Integer id);
动态sql:
/**
* 查询用户表,条件不确定,使用动态SQL
*
*/
@Select("<script>select id ,username,sex,birthday,address from user <where>" +
"<if test=\"username!='' and username != null\">" +
" and username like #{username}" +
"</if>" +
"<if test=\"sex!='' and sex !=null\">" +
" and sex = #{sex}" +
"</if>" +
"</where></script>")
List<User> queryUserByWhere(User user);
5.5 一对一的注解开发
@Results注解
- 配置手动映射,取代resultMap标签
@Result注解
- 配置手动映射,取代result标签
* 订单对用户的一对一查询
* 延迟加载
*
* 配置手动映射的注解
* @Results
* 注解属性: Result[] value() default {};
* 属性是另一个注解数组
*
* @Result注解
* 注解属性 boolean id() default false; 是不是主键
* 注解属性 column 数据表列名
* 注解属性 property pojo对象的属性名
* 注解属性 one 配置一对一的
* 注解属性 javaType 查询结果存储的数据类型
*
* @One注解:
* 属性 select 关联的第二次查询语句
* SQL2: 根据主键查询用户,写在了 UserMapper接口上
* 查询的条件 id = 订单表的user_id值
* 属性fetchType 加载类型
@Select(" select o.id,o.user_id,o.number,o.createtime,o.note from orders o")
@Results({
@Result(id = true, column = "id",property = "id"),
@Result(column = "userId",property = "user_id"),
@Result(column = "number",property = "number"),
@Result(column = "createtime",property = "createtime"),
@Result(column = "note",property = "note"),
@Result(column = "user_id" ,property = "user", javaType = User.class,
one = @One(select = "com.itheima.mapper.UserMapper.queryUserById",fetchType = FetchType.LAZY) )
})
List<Orders> queryOrdersUser();
5.6 一对多注解开发
* 定义方法,用户对订单一对多
* 延迟加载
* @Results配置映射
*
* 用户的主键,查询订单的外键 user_id
*
* select = "" 根据用户主键查询订单
@Select("select u.id ,u.username,u.sex,u.birthday,u.address from user u")
@Results({
@Result(id = true,column = "id",property = "id"),
@Result(column = "username" , property ="username" ),
@Result(column = "sex" , property ="sex" ),
@Result(column = "birthday" , property ="birthday" ),
@Result(column = "address" , property ="address" ),
@Result(column = "id",property ="ordersList", javaType = List.class,
many = @Many(select = "com.itheima.mapper.OrdersMapper.queryOrdersByUserId",fetchType = FetchType.LAZY))
})
List<User> queryUserOrders();