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>