MyBatis框架安排
4. 第四天:
1. MyBatis的延迟加载
-
引入问题:在一对多中,我们有一个用户,他有100个账户
- 问题1:在查询用户的时候,要不要把账户一起关联出来?
- 问题2:在查询账户时,要不要把用户一起关联出来?
答
问题1:在查询用户时,应该是什么时候使用它的账户,就什么时候查。
问题2:查询账户时,用户应该是随着账户一起被查询出来,这样才知道账户对应着谁。
什么是延迟加载:在真正使用数据时才发起查询,不用时不查询,又称按需查询(懒加载)
立即加载:不管用不用数据,只要一调用查询方法,就立即查询。
在对应的四种表关系中:一对多,多对一,一对一,多对多中
通常情况下:
一对多、多对多采用的是延迟加载。
一对一、多对一采用的是立即加载。
简单记忆:右边多的采用延迟加载,否则采用立即加载。
<mark>演示1:账户和用户的关系,一对一(账户 | 用户)</mark>
账户和用户关系:一个账户只属于一个用户(特定时候一对一),但一个用户可以有多个账户(一对多)
需求:查询账户表信息,账户所属的用户信息不查询出来(延迟加载)
-
准备好两个实体类,Account,User
package com.liuzeyu.domin; import java.io.Serializable; public class Account implements Serializable{ private Integer id; private Integer uid; private Double money; private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String toString() { return "Account{" + "id=" + id + ", uid=" + uid + ", money=" + money + ", user=" + user + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getUid() { return uid; } public void setUid(Integer uid) { this.uid = uid; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } }
package com.liuzeyu.domin; import java.util.Date; import java.util.List; public class User { private Integer id; private String address; private String username; private String sex; private Date birthday; private List<Account> accounts; public List<Account> getAccounts() { return accounts; } public void setAccounts(List<Account> accounts) { this.accounts = accounts; } @Override public String toString() { return "User{" + "id=" + id + ", address='" + address + '\'' + ", username='" + username + '\'' + ", sex='" + sex + '\'' + ", birthday=" + birthday + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
- 两个映射配置文件IUserDao.xml,IAccountDao.xml
<?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.liuzeyu.dao.IUserDao"> <!-- 根据id查找一个 --> <select id="findById" resultType="user" parameterType="int"> select * from user where id=#{uid} </select> </mapper>
如果要配置延迟加载,association标签则不能封装User对象,如下
<?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.liuzeyu.dao.IAccountDao"> <resultMap id="rstAc" type="account"> <id property="id" column="id"/> <result property="uid" column="uid"/> <result property="money" column="money"/> <!--javaType="user"映射后封装的结果集 外键:uid select :属性所指的内容就是查询用户的唯一标识 column="uid",其中的uid就是select标签要查询的那个id ,不可以删掉 --> <association property="user" column="uid" javaType="user" select="com.liuzeyu.dao.IUserDao.findById"> </association> </resultMap> <!-- 打印user表数据 --> <select id="findAll" resultMap="rstAc"> SELECT * from account </select> </mapper>
- 准备主配置文件
<?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"> <!-- mybatis主配置文件 --> <configuration> <properties resource="jdbcConfig.properties"> </properties> <!--配置懒加载--> <!--<settings>--> <!--<!–开启mybatis支持延迟加载–>--> <!--<setting name="lazyLoadingEnabled" value="true"/>--> <!--<!–关闭mybatis支持立即加载,默认true:mybatis < 3.4.1–>--> <!--<setting name="aggressiveLazyLoading" value="false"/>--> <!--</settings>--> <!--自定义为配置文件取别名--> <typeAliases> <package name="com.liuzeyu.domin"/> </typeAliases> <!-- mysql配置文件 --> <environments default="mysql"> <environment id="mysql"> <!-- JDBC事物--> <transactionManager type="JDBC"></transactionManager> <!-- JDBC连接池 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <package name="com.liuzeyu.dao"></package> </mappers> </configuration>
- 测试函数
public class Test { private InputStream is = null; private IAccountDao accountDao = null; private SqlSession session = null; /** * 初始化 * @throws IOException */ @Before //测试开始前执行 public void init() throws IOException { //1.解析xml配置文件 is = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.构建工厂 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); //3.工厂生产SqlSession对象 session = factory.openSession(); //session = factory.openSession(true); //设置事物的自动提交,可以不用//session.commit(); //4.创建代理Dao的对象 accountDao = session.getMapper(IAccountDao.class); } @After //测试结束后执行 public void destory(){ //session.commit(); //7.释放资源 if( session != null){ session.close(); } if( is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } @org.junit.Test public void testSelect() throws IOException { //5.使用代理对象执行方法 List<Account> accounts = accountDao.findAll(); } }
- 测试结果
- 如果在主配置文件中不配置懒加载
- 如果在主配置文件中配置懒加载
由以上日志信息可知,如果不配置懒加载,就会去根据id查询用户表的信息,配置了懒加载,则不会去查询用户表。
- 如果在主配置文件中不配置懒加载
<mark>演示2:用户和账户的关系,一对多(用户 | 账户)</mark>
在演示1的基础上添加
-
映射配置IUserDao.xml文件添加
<resultMap id="userMap" type="user"> <id property="id" column="id"/> <result property="address" column="address"/> <result property="sex" column="sex"/> <result property="birthday" column="birthday"/> <result property="username" column="username"/> <collection property="accounts" column="id" select="com.liuzeyu.dao.IAccountDao.findByUid"/> </resultMap> <!-- 根据id查找一个 --> <select id="findAll" resultMap="userMap"> select * from user </select>
-
映射配置IAccountDao.xml文件添加
<!-- 打印uid查询账户 --> <select id="findByUid" resultType="account" parameterType="int"> SELECT * from account where uid=#{uid} </select>
-
测试函数
public class UserTest { private InputStream is = null; private IUserDao userDao = null; private SqlSession session = null; /** * 初始化 * @throws IOException */ @Before //测试开始前执行 public void init() throws IOException { //1.解析xml配置文件 is = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.构建工厂 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); //3.工厂生产SqlSession对象 session = factory.openSession(); //session = factory.openSession(true); //设置事物的自动提交,可以不用//session.commit(); //4.创建代理Dao的对象 userDao = session.getMapper(IUserDao.class); } @After //测试结束后执行 public void destory(){ //session.commit(); //7.释放资源 if( session != null){ session.close(); } if( is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } @org.junit.Test public void testSelect() throws IOException { //5.使用代理对象执行方法 List<User> users = userDao.findAll(); } }
-
测试结果
- 在主配置文件中不设置懒加载(默认立即加载)
- 在主配置文件中设置懒加载
- 在主配置文件中不设置懒加载(默认立即加载)
2. MyBatis的缓存
-
什么是缓存?
答:存在于内存当中的临时数据。 -
为什么使用缓存?
答:减少与数据库直接打交道,提高执行效率。 -
什么样的数据适用缓存,什么样的数据不适用?
- 适用缓存:
- 不经常改变的数据
- 并且缓存数据和数据库不同步时最终影响不大
- 不适用缓存:
- 经常改变的数据
- 并且缓存数据和数据库不同步时最终影响很大,如商品的库存,银行的汇率,股市的牌价等等
- 适用缓存:
-
MyBatis的一级缓存:一级缓存SqlSession对象范围的缓存,当我们执行查询之后,查询结果会同时存入SqlSession为我们提供的一块区域中,该区域的结构是Map,当我们再次查询到同样的数据时mybatis会先从sqlSession中获取。当调用SqlSession增删改,commit(),close()时就会清空一级缓存。
示例:- 准备User实体类
package com.liuzeyu.domin; import java.util.Date; import java.util.List; public class User { private Integer id; private String address; private String username; private String sex; private Date birthday; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
- 准备映射配置文件
<?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.liuzeyu.dao.IUserDao"> <!-- 根据id查找一个 --> <select id="findById" resultType="user" parameterType="int"> select * from user where id=#{uid} </select> <!-- 根据id查找一个 --> <select id="updateByUser" parameterType="user"> update user set username=#{username} where id=#{id} </select> </mapper>
- dao层接口
public interface IUserDao { //根据id查找一个 public User findById(Integer id); //根据id查找一个 public void updateByUser(User user); }
- 测试函数
public class UserTest { private InputStream is = null; private IUserDao userDao = null; private SqlSession session = null; SqlSessionFactory factory = null; /** * 初始化 * @throws IOException */ @Before //测试开始前执行 public void init() throws IOException { //1.解析xml配置文件 is = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.构建工厂 factory = new SqlSessionFactoryBuilder().build(is); //3.工厂生产SqlSession对象 session = factory.openSession(); //session = factory.openSession(true); //设置事物的自动提交,可以不用//session.commit(); //4.创建代理Dao的对象 userDao = session.getMapper(IUserDao.class); } @After //测试结束后执行 public void destory(){ //session.commit(); //7.释放资源 if( session != null){ session.close(); } if( is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } // 测试一级缓存session.clearCache(); @org.junit.Test public void testSelect() throws IOException { //5.使用代理对象执行方法 User users1 = userDao.findById(41); System.out.println(users1); session.clearCache(); User users2 = userDao.findById(41); System.out.println(users2); System.out.println(users1 == users2); } //测试一级缓存:sqlSession的更新操作 @org.junit.Test public void testUpdate() throws IOException { //5.使用代理对象执行方法 User users1 = userDao.findById(41); System.out.println(users1); users1.setUsername("SqlSession update"); userDao.updateByUser(users1); User users2 = userDao.findById(41); System.out.println(users2); System.out.println(users1 == users2); //false } //测试一级缓存:sqlSession.close(); @org.junit.Test public void testClose() throws IOException { //5.使用代理对象执行方法 User users1 = userDao.findById(41); System.out.println(users1); session.close(); SqlSession session = factory.openSession(); IUserDao userDao = session.getMapper(IUserDao.class); //true userDao.updateByUser(users1); User users2 = userDao.findById(41); System.out.println(users2); System.out.println(users1 == users2); //false } }
<mark>留下疑问:为什么SqlSession的更新和删除操作,并没有清空缓存?</mark>
-
Mybatis的二级缓存:它是指Mybatis的SqlSessionFactory对象缓存,在同一个SqlSessionFactory创建的SqlSession对象内共享缓存。
二级缓存的使用步骤:
- 让MyBatis框架支持二级缓存(在SqlMapConfig.xml中配置)
<settings> <setting name="cacheEnabled" value="true"/> </settings>
- 让接口配置文件支持二级缓存,在IUserDao.xml中配置
<!--开启配置文件二级缓存--> <cache/>
- 让当前操作系统支持二级缓存(在接口配置文件的select 标签中配置)
<!-- useCache="true" --> <select id="findById" resultType="user" parameterType="int" useCache="true"> select * from user where id=#{uid} </select>
示例:
在一级缓存的基础项目上,新建测试函数
package com.liuzeyu.test;
import com.liuzeyu.dao.IUserDao;
import com.liuzeyu.domin.User;
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 java.io.IOException;
import java.io.InputStream;
public class UserTest2 {
private InputStream is = null;
SqlSessionFactory factory = null;
/**
* 初始化
* @throws IOException
*/
@Before //测试开始前执行
public void init() throws IOException {
//1.解析xml配置文件
is = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.构建工厂
factory = new SqlSessionFactoryBuilder().build(is);
}
@After //测试结束后执行
public void destory(){
if( is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 测试一级缓存session.clearCache();
@org.junit.Test
public void testSecondCash() throws IOException {
SqlSession session1 = factory.openSession();
IUserDao userDao1 = session1.getMapper(IUserDao.class);
User user1 = userDao1.findById(41);
System.out.println(user1);
session1.close();
SqlSession session2 = factory.openSession();
IUserDao userDao2 = session2.getMapper(IUserDao.class);
User user2 = userDao2.findById(41);
System.out.println(user2);
session2.close();
System.out.println(user1 == user2);
}
}
未配置二级缓存:
配置二级缓存:
可见两个不同的Sqlsession对象共享二级缓存区域,session1将查询的数据放到了二级缓存中,第二次查询就无需重新连接数据库查询了。
3. MyBatis注解开发
-
环境搭建
- 准备实体类
package com.liuzeyu.domain; import java.io.Serializable; import java.util.Date; public class User implements Serializable { private Integer id; private String username; private Date birthday; private String sex; private String address; @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", birthday=" + birthday + ", sex='" + sex + '\'' + ", adress='" + address + '\'' + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String adress) { this.address = adress; } }
- 主配置文件
<?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"> <!-- mybatis主配置文件 --> <configuration> <!--加载配置文件--> <properties resource="jdbcConfig.properties"/> <!--取别名--> <typeAliases> <package name="com.liuzeyu.domain"/> </typeAliases> <!-- 配置环境 --> <environments default="mysql"> <!-- 配置mysql环境 --> <environment id="mysql"> <!-- 配置事物类型 --> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据库源(连接池)--> <dataSource type="POOLED"> <!-- 配置连接数据库的四个基本信息 --> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> <!-- 指定映射配置文件的位置,映射配置文件是指每一个独立的dao接口配置--> <mappers> <!--<mapper class="com.liuzeyu.dao.IUserDao"/>--> <package name="com.liuzeyu.dao"/> </mappers> </configuration>
- dao层接口
public interface IUserDao { @Select("select * from user") public List<User> findAll(); }
- 测试函数
public class AnnotationTest { public static void main(String[] args) throws Exception{ //1.加载配置文件 InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.创建SqlSessiionFactory SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); //3.创建SqlSession对象 SqlSession sqlSession = factory.openSession(); //4.创建dao接口的代理dao IUserDao userDao = sqlSession.getMapper(IUserDao.class); //5.代理dao执行 List<User> users = userDao.findAll(); //6.遍历 for (User user : users) { System.out.println(user); } //7.释放资源 sqlSession.close(); is.close(); } }
- 查询结果
- <mark>注意事项1:</mark>
如果使用注解配置,只要是在resource存在,如下目录结构的映射文件
无论是否在主配置文件中配置mapper映射文件的class属性,mybatis都会认为这是错误的,因为他不认识你是否要用注解开发
<mark>注意事项2:</mark>
注解开发与映射配置文件的区别,为什么可以使用注解开发?
由此配置文件有的,注解也都能提供。
-
单表CRUD操作
- 在1的基础上操作,为dao层接口添加如下GRUD操作
public interface IUserDao { @Select("select * from user") public List<User> findAll(); //插入一个用户 @Select("insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})") public List<User> insertUser(User user); //根据id查找某一用户 @Select("select * from user where id=#{id}") public User findById(Integer id); // 根据id删除某一用户 @Delete("delete from user where id=#{id}") public void deleteById(Integer id); // 根据id更新某一用户的用户名 @Update("update user set username=#{username} where id=#{id}") public void Update(User user); // 根据name删除某一用户集合 //@Select("select * from user where username like '%${username}%'") 不推荐使用 @Select("select * from user where username like #{username}") public List<User> findByName(User user); }
- 测试函数
public class AnnotationTestCRUD { private InputStream is = null; private SqlSession sqlSession = null; private SqlSessionFactory factory = null; private IUserDao dao = null; @Before public void init() throws IOException { //1.加载配置文件 is = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.创建工厂 factory = new SqlSessionFactoryBuilder().build(is); //3.创建SqlSession对象 sqlSession = factory.openSession(true); //4.创建代理dao对象 dao = sqlSession.getMapper(IUserDao.class); } @After public void destory() throws IOException { if(sqlSession != null){ sqlSession.close(); } if(is != null){ is.close(); } } /** * 插入一个用户 */ @Test public void testInsert(){ User user = new User(); user.setUsername("JAY"); user.setAddress("莆田"); user.setSex("男"); user.setBirthday(new Date()); dao.insertUser(user); } /** * 删除一个用户 */ @Test public void testDelete(){ dao.deleteById(50); } /** * 根据id查找某一用户 */ @Test public void testFindById(){ User user = dao.findById(41); System.out.println(user); } /** * 更新某一用户 */ @Test public void testUpdate(){ User user = dao.findById(51); user.setUsername("Jack"); dao.Update(user); } /** * 根据id查找某一用户 */ @Test public void testFindByNames(){ User user = new User(); //user.setUsername("%王%"); user.setUsername("王"); List<User> users = dao.findByName(user); for (User u : users) { System.out.println(u); } } }
-
解决实体类属性名和数据库字段名不一致问题
- 在1的基础上修改字段名,并生成对应的setXXX,getXXX方法
public class User implements Serializable { private Integer userId; private String userName; private Date userBirthday; private String userSex; private String userAddress; @Override public String toString() { return "User{" + "userId=" + userId + ", userName='" + userName + '\'' + ", userBirthday=" + userBirthday + ", userSex='" + userSex + '\'' + ", userAddress='" + userAddress + '\'' + '}'; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public Date getUserBirthday() { return userBirthday; } public void setUserBirthday(Date userBirthday) { this.userBirthday = userBirthday; } public String getUserSex() { return userSex; } public void setUserSex(String userSex) { this.userSex = userSex; } public String getUserAddress() { return userAddress; } public void setUserAddress(String userAddress) { this.userAddress = userAddress; } }
- 删除不需要的dao方法
public interface IUserDao { @Select("select * from user") public List<User> findAll(); @Select("insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})") public List<User> insertUser(User user); @Select("select * from user where id=#{id}") public User findById(Integer id); }
- 测试findAll方法,可见并没有被封装进去
- 解决实体类和数据库字段对应关系
在dao层函数上添加,与接口配置文件的resultMap标签作用相同
@Select("select * from user") @Results(id = "userMap",value = { @Result(id = true,property = "userId" ,column = "id"), @Result(property = "userName",column = "username"), @Result(property = "userBirthday",column = "birthday"), @Result(property = "userSex",column = "sex"), @Result(property = "userAddress",column = "address") }) public List<User> findAll();
- 测试findAll
- 如果需要在其它地方引用则个Map,可以使用注解ResultMap的value字段引用即可
//@ResultMap(value = {"userMap"}) 简写如下 @ResultMap("userMap") @Select("select * from user where id=#{id}") public User findById(Integer id);
- 测试findById
-
多表查询操作
-
账户和用户的关系(账户 | 用户,多对一) mybatis称之为:一对一的关系
- 在4的基础上新增Account类
public class Account implements Serializable{ private Integer id; private Integer uid; private Double money; private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String toString() { return "Account{" + "id=" + id + ", uid=" + uid + ", money=" + money + ", user=" + user + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getUid() { return uid; } public void setUid(Integer uid) { this.uid = uid; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } }
- 新增IAccountDao接口及方法
import java.util.List; public interface IAccountDao { @Select("select * from account") @Results(id = "accountMap",value = { @Result(id = true,property = "id" ,column = "id"), @Result(property = "uid",column = "uid"), @Result(property = "money",column = "money"), @Result(property = "user",column = "uid", //账户和用户对应关系是:一对一,使用@One one = @One(select = "com.liuzeyu.dao.IUserDao.findById",fetchType = FetchType.LAZY)) }) public List<Account> findAll(); }
- 测试方法
public class AnnotationTestAccount { private InputStream is = null; private SqlSession sqlSession = null; private SqlSessionFactory factory = null; private IAccountDao dao = null; @Before public void init() throws IOException { //1.加载配置文件 is = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.创建工厂 factory = new SqlSessionFactoryBuilder().build(is); //3.创建SqlSession对象 sqlSession = factory.openSession(true); //4.创建代理dao对象 dao = sqlSession.getMapper(IAccountDao.class); } @After public void destory() throws IOException { if(sqlSession != null){ sqlSession.close(); } if(is != null){ is.close(); } } /** * 查找所有 */ @Test public void testFindAll(){ List<Account> accounts = dao.findAll(); for (Account account : accounts) { System.out.println(account); } }
- 测试结果
-
用户和账户的关系(用户 | 账户 ,一对多)
- User实体类添加属性Account集合
private List<Account> accounts; public List<Account> getAccounts() { return accounts; } public void setAccounts(List<Account> accounts) { this.accounts = accounts; }
- 在5.1的基础上新增dao层IAccount接口方法
userId 属性值 <!-- 通过uid查询账户--> @Select("select * from account where uid=#{userId}") public Account findByUid(Integer id);
- 修改IUserDao的接口方法
public interface IUserDao { @Select("select * from user") @Results(id = "userMap",value = { @Result(id = true,property = "userId" ,column = "id"), @Result(property = "userName",column = "username"), @Result(property = "userBirthday",column = "birthday"), @Result(property = "userSex",column = "sex"), @Result(property = "userAddress",column = "address"), @Result(property = "accounts",column = "id", many = @Many(select = "com.liuzeyu.dao.IAccountDao.findByUid",fetchType= FetchType.LAZY)) }) public List<User> findAll(); }
- 新增测试方法
public class AnnotationTestCRUD { private InputStream is = null; private SqlSession sqlSession = null; private SqlSessionFactory factory = null; private IUserDao dao = null; @Before public void init() throws IOException { //1.加载配置文件 is = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.创建工厂 factory = new SqlSessionFactoryBuilder().build(is); //3.创建SqlSession对象 sqlSession = factory.openSession(true); //4.创建代理dao对象 dao = sqlSession.getMapper(IUserDao.class); } @After public void destory() throws IOException { if(sqlSession != null){ sqlSession.close(); } if(is != null){ is.close(); } } /** * 查找所有 */ @Test public void testFindAll(){ List<User> users = dao.findAll(); for (User user : users) { System.out.println(user); } } }
- 测试结果
-
-
缓存配置
- 一级缓存默认开启,共享SqlSession
@Test public void testFindById(){ User user1 = dao.findById(41); //sqlSession.clearCache(); 开启后 user1 == user2为false User user2 = dao.findById(41); System.out.println(user1 == user2); //true }
- 二级缓存的配置
- 新增类测试二级缓存
这里需注意的是必须是同一个工厂对象factory才可以共享二级缓存。public class AnnotationTestSecondLevel { private InputStream is = null; private SqlSessionFactory factory = null; @Before public void init() throws IOException { //1.加载配置文件 is = Resources.getResourceAsStream("SqlMapConfig.xml"); factory = new SqlSessionFactoryBuilder().build(is); } @After public void destory() throws IOException { if(is != null){ is.close(); } } /** * 测试二级缓存 */ @Test public void testSecondLevel(){ SqlSession sqlSession1 = factory.openSession(true); IUserDao dao1 = sqlSession1.getMapper(IUserDao.class); User user1 = dao1.findById(41); System.out.println(user1); sqlSession1.close(); SqlSession sqlSession2 = factory.openSession(true); IUserDao dao2 = sqlSession2.getMapper(IUserDao.class); User user2 = dao2.findById(41); System.out.println(user2); sqlSession2.close(); } }
- 还未配置二级缓存前,测试结果
- 配置二级缓存
- 主配置文件开启二级缓存,注意要写到加载配置文件后
<!--开启二级缓存--> <settings> <setting name="cacheEnabled" value="true"/> </settings>
- dao层接口
- 重新运行测试函数,此时只打开一次数据库连接
- 主配置文件开启二级缓存,注意要写到加载配置文件后
- 一级缓存默认开启,共享SqlSession