文章目录
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(); }
此时再次运行原来的方法,就可以看到开启了二级缓存的效果: