mybatis中的延迟加载:
在查询用户时,用户下的账户信息应该是什么时候使用,什么时候查询的。
什么是延迟加载:
在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
什么是立即加载
不管用不用,只要一调用方法,马上发起查询。
一对多,多对多:通常情况下我们都是采用延迟加载。
一对一,多对一:通常情况下我们都是采用立即加载。
<!-- 定义封装account和user的resultMap --> <resultMap id="accountUserMap" type="account"> <id property="id" column="aid"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> <!-- 一对一的关系映射:配置封装user的内容--> <association property="user" column="uid" javaType="user"> <id property="id" column="id"></id> <result column="username" property="username"></result> <result column="address" property="address"></result> <result column="sex" property="sex"></result> <result column="birthday" property="birthday"></result> </association> </resultMap>
配置延迟加载再提供这些封装没有意义,因为没有查数据是不可能封装的。
这时候需要使用一个新的属性select。
<!-- 一对一的关系映射:配置封装user的内容--> <association property="user" column="uid" javaType="user"> <id property="id" column="id"></id> <result column="username" property="username"></result> <result column="address" property="address"></result> <result column="sex" property="sex"></result> <result column="birthday" property="birthday"></result> </association>
<!-- 定义封装account和user的resultMap --> <resultMap id="accountUserMap" type="account"> <id property="id" column="id"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> <!-- 一对一的关系映射:配置封装user的内容 select属性指定的内容:查询用户的唯一标识 column属性指定的内容:用户根据id查询时,所需要的参数的值。 --> <association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById"> </association>
从这张图片中可以看出并没有延迟的效果。
要想真正实现延迟的功能我们需要进行配置。
百度搜索mybatis官网
点击settings(设置)
如果我们此时把AccountTest.java中的testFindAll()方法的遍历给注释上
@Test
public void testFindAll(){
List<Account> accounts = accountDao.findAll();
// for(Account account : accounts){
// System.out.println("--------每个account的信息------------");
// System.out.println(account);
// System.out.println(account.getUser());
// }
} 此时发现其只执行了一条语句,没有查询user的操作了。
-----------------------------------------------------------------------------------------------------------
一对多延迟加载
实体类user中
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
//一对多关系映射:主表实体应该包含从表实体的***引用
private List<Account> accounts; 在IUserDao.xml中 <!-- 定义User的resultMap--> <resultMap id="userAccountMap" type="user"> <id property="id" column="id"></id> <result property="username" column="username"></result> <result property="address" column="address"></result> <result property="sex" column="sex"></result> <result property="birthday" column="birthday"></result> <!-- 配置user对象中accounts***的映射 --> <collection property="accounts" column="id" ofType="account" select="com.itheima.dao.IAccountDao.findAccountByUid"> </collection> </resultMap>在dao.IAccountDao中
List<Account> findAccountByUid(Integer uid);在IAccountDao.xml中
<select id="findAccountByUid" parameterType="Integer" resultType="account">
SELECT * FROM account WHERE uid = #{uid}
</select> 在test包下的UserTest.java /**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
// for(User user : users){
// System.out.println("-----每个用户的信息------");
// System.out.println(user);
// System.out.println(user.getAccounts());
// }
}
最后别忘了主配置文件的开启步骤
<configuration> <!-- 配置properties--> <properties resource="jdbcConfig.properties"></properties> <!--配置参数--> <settings> <!--开启Mybatis支持延迟加载--> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings> <!--使用typeAliases配置别名,它只能配置domain中类的别名 --> <typeAliases> <package name="com.itheima.domain"></package> </typeAliases>
这就是一对多情况下的延迟加载,其实他们的思想都是在用的时候调用对方配置文件
中的一个配置来实现查询的功能。
-----------------------------------------------------------------------------------------------------------
mybatis中的缓存
什么是缓存:
存在于内存中的临时数据
为什么使用缓存:
减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,什么样的数据不能使用。
适用于缓存:
经常查询并且不经常改变的。
数据的正确与否对最终结果影响不大。
不适用于缓存:
经常改变的数据
数据的正确性与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,股市的牌价。
mybatis中的一级缓存和二级缓存
一级缓存:它指的是mybatis中SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
查询是否有,有的话直接拿出来用。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。
在UserTest.java中
@Test
public void testFirstLevelCache() {
User user1 = userDao.findById(41);
System.out.println(user1);
User user2 = userDao.findById(41);
System.out.println(user2);
System.out.println(user1==user2);
} 可以看到只发起了一次查询,换句话说,第一次是查询,第二次是从缓存中取。
@Test
public void testFirstLevelCache() {
User user1 = userDao.findById(41);
System.out.println(user1);
sqlSession.close();
sqlSession=factory.openSession();
userDao=sqlSession.getMapper(IUserDao.class);
User user2 = userDao.findById(41);
System.out.println(user2);
System.out.println(user1==user2);
} @Test
public void testFirstLevelCache() {
User user1 = userDao.findById(41);
System.out.println(user1);
//sqlSession.close();
//sqlSession=factory.openSession();
sqlSession.clearCache();//此方法也可以清空缓存
userDao=sqlSession.getMapper(IUserDao.class);
User user2 = userDao.findById(41);
System.out.println(user2);
System.out.println(user1==user2);
} 可以看到在更新之后再次查询的时候并没有从缓存中取,而是直接发起了一次新的查询。原因是一级缓存是SqlSession范围
的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

-----------------------------------------------------------------------------------------------------------
二级缓存:
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象
创建的SqlSession共享其缓存。
二级缓存的使用步骤: 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
<settings> <setting name="cacheEnabled" value="true"/> </settings>
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml配置)
第三步:让当前的操作支持二级缓存(在select标签中配置 )
<?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.IUserDao"> <!--让当前的映射文件支持二级缓存开启user支持二级缓存--> <cache/> <!-- 查询所有 --> <select id="findAll" resultType="user"> select * from user </select> <!-- 根据id查询用户 第三步:让当前的操作支持二级缓存(在select标签中配置 )--> <select id="findById" parameterType="INT" resultType="user" useCache="true"> select * from user where id = #{uid} </select> </mapper>
配置好之后,我们启动test方法,发现第二次直接是从缓存中获取,但是结果是false。
这个原因是二级缓存中存放的是数据,而不是对象。
-----------------------------------------------------------------------------------------------------------
Mybatis注解开发
Mybatis注解开发可以减少编写Mapper映射文件。
环境搭建
单表CRUD(***Dao方式)
多表查询操作
缓存的配置
只要使用注解开发,但是在配置路径文件下同时包含了IUserDao.xml,
此时不管用不用这个IUserDao.xml都会报错。
如果实体类User的属性名称和user表中的列名称不相同
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
...... IUserDao.java package com.itheima.dao;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 在mybatis中,针对CRUD一共有四个注解。
* @Select @Insert @Update @Delete
*/
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
@Results(value = {
@Result(id=true,column = "id",property = "userId"),
@Result(column = "username",property = "userName"),
@Result(column = "birthday",property = "userBirthday"),
@Result(column = "sex",property = "userSex"),
@Result(column = "address",property = "userAddress")
})
List<User> findAll();
@Insert("insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})")
void saveUser(User user);
} mybatis的@Results注解, 跟进去@Results,
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Results {
/**
* The name of the result map.
*/
String id() default "";
Result[] value() default {};
} 所以就是 @Result(value={
@Result
})
跟进去@Result,
public @interface Result {
boolean id() default false;
String column() default "";
String property() default "";
...... 发现有column和property,发现和之前用xml的很像,之前在xml还有一个唯一标志, 在这里有一个boolean类型的id,默认值是false,如果是id字段的话,把它设置为true。
这时我们就可以配置了
@Results(value={
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="birthday",property="userBirthday"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress")
})
但是此时这样用不到别的方法上,所以此时我们需要给这个@Results加上一个名称id,
@Results(id="userMap",value={
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="birthday",property="userBirthday"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress")
})
在想用的地方,直接@ResultMap
@Select("select * from user where id = #{id}") @ResultMap("userMap") User findById(Integer userId);点进去@ResultMap,
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ResultMap {
String[] value();
} 有一个value属性, 所以标准的写法应该是
@ResultMap(value={"userMap"}) 如果只有一个属性且是value的时候,可以省略,如果数组中只有一个值是大括号也可以省略。 在@Result中
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Result {
boolean id() default false;
String column() default "";
String property() default "";
Class<?> javaType() default void.class;
JdbcType jdbcType() default JdbcType.UNDEFINED;
Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;
One one() default @One;
Many many() default @Many;
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface One {
String select() default "";
FetchType fetchType() default FetchType.DEFAULT;
} select表示的是指向的是如何查询用户的标志, 点进FetchType,
public enum FetchType {
LAZY, EAGER, DEFAULT
} LAZY:延迟加载 EAGER:立即加载
IAccountDao.java
import java.util.List;
public interface IAccountDao {
/**
* 查询所有账户,并返回每个账户所属的用户信息
* @return
*/
@Select("select * from account")
@Results(id = "accountMap",value = {
@Result(id = true,column = "id",property = "id"),
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
@Result(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER))
})
List<Account> findAll();
}
----------------------------------------------------------------------------------------------------------- Mybatis注解开发一对多的查询配置
查询用户,并且带有其所有的账户信息
实体类user
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
} 在IUserDao.java下 public interface IUserDao {
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
@Results(id="userMap",value = {
@Result(id=true,column = "id",property = "userId"),
@Result(column = "username",property = "userName"),
@Result(column = "birthday",property = "userBirthday"),
@Result(column = "sex",property = "userSex"),
@Result(column = "address",property = "userAddress"),
@Result(column = "id",property = "accounts",
many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
fetchType = FetchType.LAZY))
})
List<User> findAll();
} 在IAccountDao.java下 @Select("select * from account where uid=#{userId}")
List<Account> findAccountByUid(Integer userId); 测试 @Test
public void testFindAll() {
List<User> users=userDao.findAll();
for (User user:users) {
System.out.println(user);
System.out.println(user.getAccounts());
}
} 我们采用的是延迟加载的方式加载的,如果想看到延迟加载,把打印给注释上
@Test
public void testFindAll() {
List<User> users=userDao.findAll();
// for (User user:users) {
// System.out.println(user);
// System.out.println(user.getAccounts());
// }
} mybatis注解开发使用二级缓存
IUseDao.java
没有配置二级缓存之前
CacheNamespace(blocking = true)
CacheNamespace(blocking = true)
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
@Results(id="userMap",value = {
@Result(id=true,column = "id",property = "userId"),
@Result(column = "username",property = "userName"),
@Result(column = "birthday",property = "userBirthday"),
@Result(column = "sex",property = "userSex"),
@Result(column = "address",property = "userAddress"),
@Result(column = "id",property = "accounts",
many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
fetchType = FetchType.LAZY))
})
List<User> findAll(); 发现并没有发起查询
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--配置开启二级缓存(不配置默认也是开启的)--> <settings> <setting name="cacheEnabled" value="true"/> </settings>

京公网安备 11010502036488号