文章目录
Mybatis中的延迟加载
一对一进行延迟加载
一对一的情况下一般是采用立即加载,但为了好理解延迟加载在这里就进行一下演示
在之前 账户和用户的表中,我们一个账户只能是归一个用户所有,所以我们采用查询账户的方式来演示延时加载
首先进行准备工作:
- 实现根据id来查询用户的功能
<select id="findById" resultType="com.gegege.domain.User" parameterType="java.lang.Integer"> select * from user where id = #{uid}; </select>
- 在Account中添加私有成员变量 private User user2;并提供get和set方法
- Account的Test方法
首先我们在IAccountDao.xml添加一个resultMap
<resultMap id="accountusermap" type="com.gegege.domain.Account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user2" column="uid" javaType="com.gegege.domain.User" select="com.gegege.dao.IUserDao.findById"></association>
</resultMap>
这里要注意的是<association property="user2" column="uid" javaType="com.gegege.domain.User" select="com.gegege.dao.IUserDao.findById"></association>
其中property 对应的我们在准备工作第二步中创建的变量
column对应的是要传递给select映射的参数
select 对应要调用的select映射的ID
javaType对应延迟加载的javabean
这时更改findall对应的select语句
<select id="FindAll" resultMap="accountusermap">
select * from account
</select>
在这时运行会发现 语句还是一次性执行完
在https://mybatis.net.cn/configuration.html#settings网站中找到
要更改这两个属性值才可以实现延迟加载
于是我们更改sqlmapconfig.xml
在里添加以下内容即可实现延迟加载
<settings>
<!--开启mybatis全局进行延迟加载的开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
那么我们删掉遍历输出的语句试一下
@Test
public void FindAllTest() {
List<Account> list=dao.FindAll();
}
发现只执行了单表上的内容
此时一对一的延迟加载便完成了
一对多的延迟加载
user的javabean中要有private List<Account> list;
以便实现一对多
将IUserDao.xml改为
<resultMap id="selectUserAccount" type="com.gegege.domain.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<collection property="list" column="id" ofType="com.gegege.domain.Account" select="com.gegege.dao.IAccountDao.findByUid">
</collection>
</resultMap>
<sql id="select*"> select * from user</sql>
<select id="findAll" resultMap="selectUserAccount">
SELECT * FROM user
</select>
注意其中select="com.gegege.dao.IAccountDao.findByUid"
那么我们下一步要做的就是在IAccountDao.java和IAccountDao.xml添加findByUid方法
<mapper namespace="com.gegege.dao.IAccountDao">
<sql id="select_account"> select * from account</sql>
<resultMap id="accountusermap" type="com.gegege.domain.Account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user2" column="uid" javaType="com.gegege.domain.User" select="com.gegege.dao.IUserDao.findById"></association>
</resultMap>
<select id="FindAll" resultMap="accountusermap">
select * from account
</select>
<select id="findByUid" resultType="com.gegege.domain.Account">
select * from account where uid = #{uid}
</select>
</mapper>
List<Account> findByUid(int uid);
最后别忘了更改sqlmapconfig.xml
在里添加以下内容即可实现延迟加载
<settings>
<!--开启mybatis全局进行延迟加载的开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
缓存机制:
就是将用户经常查询的数据的结果的一个保存,保存到一个内存中(缓存就是内存中的一个对象),用户在查询的时候就不用到数据库文件中查询(磁盘),从而减少与数据库的交付次数提高了响应速度,解决了并发系统的西能问题
一级缓存
我们用以下代码测试一下一级缓存
/**
* 测试一级缓存
*/
@Test
public void testFirstLevelCache() {
User user1 = dao.findById(41);
System.out.println(user1);
User user2 = dao.findById(41);
System.out.println(user2);
System.out.println(user1==user2);
}
发现只执行了一次sql语句
还有user1和user2两个对象的地址是相同的,那么证明这个这个对象是从缓存中拿出来的
那么对于重要数据是不能保存在缓存里的
所以我们要清空缓存,清空缓存有两种方式
/**
* 测试一级缓存清空
* 已知在关闭session会清空一级缓存
*/
@Test
public void testFirstLevelCacheClean() {
User user1 = dao.findById(41);
System.out.println(user1);
session.close();
session=sqlSessionFactory.openSession();
dao=session.getMapper(IUserDao.class);
User user2 = dao.findById(41);
System.out.println(user2);
System.out.println(user1==user2);
}
/**
* 测试一级缓存清空
* 已知在关闭session会清空一级缓存
*/
@Test
public void testFirstLevelCacheClean() {
User user1 = dao.findById(41);
System.out.println(user1);
//清空cache
session.clearCache();
User user2 = dao.findById(41);
System.out.println(user2);
System.out.println(user1==user2);
}
这两个程序执行的结果是这样的
这样执行的就是两遍sql语句
/**
* 测试一级缓存清空
* 已知在关闭session会清空一级缓存
*/
@Test
public void testFirstLevelCacheClean() {
User user1 = dao.findById(41);
System.out.println(user1);
//清空cache
User user=new User();
int num=new Random().nextInt();
user.setId(42);
user.setUsername("dd"+ num);
user.setAddress("s" + num);
user.setBirthday(new Date());
user.setSex("s");
dao.upDateUser(user);
User user2 = dao.findById(41);
System.out.println(user2);
System.out.println(user1==user2);
}
在两个查询中间插入一个更新操作,这时执行程序就会有这样的结果
二级缓存
先写测试类
public class cache2_test {
private InputStream in;
private SqlSessionFactoryBuilder sessionFactoryBuilder;
private SqlSessionFactory factory1;
@Before
public void before() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建工具
sessionFactoryBuilder=new SqlSessionFactoryBuilder();
factory1 = sessionFactoryBuilder.build(in);
}
@After
public void after() throws IOException {
in.close();
}
/**
* 测试二级缓存
*/
@Test
public void testTwoLevelCache() {
SqlSession session1 = factory1.openSession();
IUserDao dao1 = session1.getMapper(IUserDao.class);
User user1 = dao1.findById(41);
System.out.println(user1);
session1.close();
SqlSession session2 = factory1.openSession();
IUserDao dao2 =session2.getMapper(IUserDao.class);
User user2 = dao2.findById(41);
System.out.println(user2);
session2.close();
System.out.println(session1==session2);
}
}
运行发现仍然执行了两遍sql语句,这表示两次数据都是从数据库里存取的
这是因为二级缓存默认是不开启的
首先在sqlmapconfig.xml配置一下setting (因为此配置默认是true此配置可以省略)
<setting name="cacheEnabled" value="true"/>
然后在IUserDao.xml里添加这两个标签
此时运行时结果:
我们可以发现这时只执行了一次sql语句,证明二级缓存生效了
但我们还可以发现,两个对象的地址不一样,所以对比结果为false,这是因为mybatis中二级缓存的存取方式是吧数据散装进行存储,而不是直接存储对象
注解开发
注解开发比较方便简单,在公司项目中也用的越来越多了,所以有必要学习注解开发
注解开发——单表的增删改查
创建maven项目
创建javabean的类和Iuserdao的接口
创建SqlMapConfig.xml,jdbcConfig.properties,log4j.properties
要注意对应dao接口的资源库路径下不能有对应的xml文件,否则会报错
在SqlMapConfig.xml添加如下内容
<?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>
<properties resource="jdbcConfig.properties"></properties>
<!--配置别名-->
<typeAliases>
<package name="com.mybatis.domain"/>
</typeAliases>
<!--配置环境-->
<environments default="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>
<!--指定带有注解的接口所在位置-->
<mappers>
<package name="com.mybatis.dao"/>
</mappers>
</configuration>
在IUserDao中插入以下内容:
package com.mybatis.dao;
import com.mybatis.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* mybatis的四种注解
* @Select @Install @Update @Delete
*/
public interface IUserDao {
/**
*查询所有用户
* @return
*/
@Select(value = "select * from user")
List<User> selectAll();
/**
* 插入用户
* @param user
*/
@Insert(value = "insert into user (username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday})")
void install(User user);
/**
* 修改用户
* @param user
*/
@Update(value = "update user set username=#{username}, sex=#{sex}, address=#{sex}, birthday=#{birthday} where id=#{id}")
void updateUser(User user);
/**
* 删除用户
* @param id
*/
@Delete(value = "delete from user where id=#{id}")
void deleteUser(int id);
/**
* 获取数据个数
* @return
*/
@Select(value = "select count(*) from user ")
int getNumber();
/**
* 根据ID查询
* @return
*/
@Select(value = "select * from user where id=#{id}")
User selectById(int id);
/**
* 模糊查询
* @param username
* @return
*/
@Select(value = "select * from user where username Like #{username}")
List<User> findUserByName(String username);
}
创建测试类进行测试
public class mybatisAnno_test {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private IUserDao dao;
@Before
public void init() throws IOException {
//获取字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//根据字节构建sqlsessionfactory
factory = new SqlSessionFactoryBuilder().build(in);
//根据sqlsessionfactory生产session
sqlSession = factory.openSession(true);
//根据sqlsession获取Dao代理对象
dao = sqlSession.getMapper(IUserDao.class);
}
@After
public void end() throws IOException {
//释放资源
in.close();
sqlSession.close();
}
/**
* 测试查询
*/
@Test
public void testfind() {
//用代理对象执行方法
List<User> users = dao.selectAll();
users.forEach(System.out::println);
}
/**
* 测试插入
*/
@Test
public void testinsert() {
User user=new User();
user.setAddress("jijijijij");
user.setBirthday(new Date());
user.setUsername("吉良吉影");
user.setSex("男");
dao.install(user);
}
/**
* 测试更新
*/
@Test
public void testUpdate() {
User user=new User();
user.setId(55);
user.setAddress("aaaaa");
user.setBirthday(new Date());
user.setUsername("空条承太郎");
user.setSex("男");
dao.updateUser(user);
}
/**
* 测试删除
*/
@Test
public void testdelete() {
dao.deleteUser(56);
}
/**
* 测试获取个数
*/
@Test
public void testGetNumber() {
System.out.println(dao.getNumber());
}
/**
* 测试根据id查询
*/
@Test
public void testFindById() {
User user = dao.selectById(55);
System.out.println(user);
}
/**
* 测试模糊查询
*/
@Test
public void testFindByName() {
//用代理对象执行方法
List<User> users = dao.findUserByName("%王%");
users.forEach(System.out::println);
}
}
此时所有测试便都可以正常执行
注解开发——建立表与实体类的对应关系
先删除除了查询之外的函数及抽象类
重新更改javabean中的参数,使其与数据库表中不对应
public class User implements Serializable {
private int userid;
private String username;
private String useraddress;
private String usersex;
private Date userbirthday;
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUseraddress() {
return useraddress;
}
public void setUseraddress(String useraddress) {
this.useraddress = useraddress;
}
public String getUsersex() {
return usersex;
}
public void setUsersex(String usersex) {
this.usersex = usersex;
}
public Date getUserbirthday() {
return userbirthday;
}
public void setUserbirthday(Date userbirthday) {
this.userbirthday = userbirthday;
}
@Override
public String toString() {
return "User{" +
"userid=" + userid +
", username='" + username + '\'' +
", useraddress='" + useraddress + '\'' +
", usersex='" + usersex + '\'' +
", userbirthday=" + userbirthday +
'}';
}
}
在IUserDao中添加@Results, @Result,@ResultMap
/**
* mybatis的四种注解
* @Select @Install @Update @Delete
*/
public interface IUserDao {
/**
*查询所有用户
* @return
*/
@Select(value = "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")
})
List<User> selectAll();
/**
* 根据ID查询
* @return
*/
@Select(value = "select * from user where id=#{id}")
@ResultMap(value = { "userMap"})
User selectById(int id);
/**
* 模糊查询
* @param username
* @return
*/
@Select(value = "select * from user where username Like #{username}")
@ResultMap(value = { "userMap"})
List<User> findUserByName(String username);
}
注解开发——多表查询:
一对一查询或者叫多对一 (一个账户只属于一个用户)
创建account的javabean
注意其中有user变量
public class Account implements Serializable {
private int id;
private int uid;
private double money;
private User user;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
'}'+user;
}
}
再创建IAccountDao.java
public interface IAccountDao {
@Select(value = "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(column = "uid",property = "user",one = @One(select = "com.mybatis.dao.IUserDao.selectById",fetchType = FetchType.EAGER))
})
List<Account> findAll();
}
需要注意@Result(column = "uid",property = "user",one = @One(select = "com.mybatis.dao.IUserDao.selectById",fetchType = FetchType.EAGER))
这句代码:
property 为javabean里的容器名
column的参数为要传递的形参参数
one是指mybatis里一对一(多对一)的查找
one注解里的 select是要填写全限定类名及方法名
fetchType 里 EAGER代表立即加载,LAZY是延迟加载或者叫懒加载
这时创建一个test运行即可看见结果
public class MybatisAccount_test {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private IAccountDao dao;
@Before
public void init() throws IOException {
//获取字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//根据字节构建sqlsessionfactory
factory = new SqlSessionFactoryBuilder().build(in);
//根据sqlsessionfactory生产session
sqlSession = factory.openSession(true);
//根据sqlsession获取Dao代理对象
dao = sqlSession.getMapper(IAccountDao.class);
}
@After
public void end() throws IOException {
//释放资源
in.close();
sqlSession.close();
}
/**
* 测试查询
*/
@Test
public void testfind() {
//用代理对象执行方法
List<Account> users = dao.findAll();
users.forEach(System.out::println);
}
}
运行结果:
一对多查询——一个用户对应多个账户
为IAccountDao添加抽象类
/**
* 根据用户id查询账户
* @param uid
* @return
*/
@Select(value = "select * from Account where uid=#{uid}")
List<Account> findByUid(int uid);
更改User的javabean,增加账户的list
private List<Account> accounts = new ArrayList<Account>();
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
增加下面这句参数
/**
*查询所有用户
* @return
*/
@Select(value = "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.mybatis.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))
})
List<User> selectAll();
这句注解的意思与上面一对一的意思类似,只是把one换成了more(一对一换成了多对一)
立即加载换成了懒加载
然后运行,即可执行成功
注解开发——缓存配置
使用下面的代码进行测试:
public class mybatisAnno_test {
private InputStream in;
private SqlSessionFactory factory;
@Before
public void init() throws IOException {
//获取字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//根据字节构建sqlsessionfactory
factory = new SqlSessionFactoryBuilder().build(in);
//根据sqlsessionfactory生产session
}
@After
public void end() throws IOException {
//释放资源
in.close();
}
/**
* 测试根据id查询
*/
@Test
public void testFindById() {
SqlSession sqlSession = factory.openSession(true);
IUserDao dao = sqlSession.getMapper(IUserDao.class);
User user = dao.selectById(42);
System.out.println(user);
sqlSession.close();
SqlSession sqlSession2 = factory.openSession(true);
IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
User user2 = dao2.selectById(42);
System.out.println(user2);
sqlSession2.close();
System.out.println(user==user2);
}
}
可以看到他其实是进行了两次查询,这证明此时并没有用到二级缓存
这时我们修改sqlmapconfig.xml此配置可以省略,因为默认值也为true
<!--配置缓存-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
然后在IUserDao类中添加注解@CacheNamespace(blocking = true)
@CacheNamespace(blocking = true)
public interface IUserDao {
/**
*查询所有用户
* @return
*/
@Select(value = "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.mybatis.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))
})
List<User> selectAll();
}
此时再次运行原来的方法,就可以看到开启了二级缓存的效果: