一、延迟加载策略
1.1 什么是延迟加载(懒加载)
- 在需要用到数据时才进行加载,不需要用到数据时就不加载数据。也称为懒加载
- 好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表比关联查询多张表速度要快。
- 坏处:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
1.2 实现懒加载
- 使用“账户Account - 用户User”,表示一对一,一个账户只能被一个用户拥有。
- 使用“用户User - 账户Account”,表示一对多,一个用户可以拥有多个账户。
Account
中含有private User user;
User
中含有private List<Account> accounts;
1.2.1 Account持久层DAO接口
public interface IAccountDao {
/**
* 查询所有账户
* @return
*/
List<Account> findAll();
/**
* 根据用户id查询账户信息
* @param uid
* @return
*/
List<Account> findAccountByUid(Integer uid);
}
1.2.2 Account持久层映射文件
association
:表示一对一关系property
:实体类中的属性名javaType
:查询结构以user
对象表示select
:需要用到对方的查询方法。<font color="red">全限定类名+方法名</font>column
:用户根据id查询时,所需要的参数值
<?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.ruki.dao.IAccountDao">
<!-- 定义封装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" javaType="user" select="com.ruki.dao.IUserDao.findById" column="uid"></association>
</resultMap>
<!-- 查询所有用户 -->
<!--<select id="findAll" resultType="com.ruki.domain.User">-->
<select id="findAll" resultMap="accountUserMap">
select * from account
</select>
<select id="findAccountByUid" parameterType="integer" resultType="account">
select * from account where uid=#{uid}
</select>
</mapper>
1.2.3 User持久层DAO接口
public interface IUserDao {
/**
* 查找所有
* @return
*/
List<User> findAll();
/**
* 根据ID查用户
* @param userId
* @return
*/
User findById(Integer userId);
}
1.2.4 User持久层映射文件
collection
:于建立一对多中集合属性的对应关系property
:包含的属性名ofType
:指定集合元素的数据类型。(<font color="red">泛型</font>)select
:用于指定查询账户的唯一标识(账户的dao全限定类名加上方法名称)。调用对方的查询方法column
:指定使用哪个字段的值作为条件查询
<?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.ruki.dao.IUserDao">
<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>
<collection property="accounts" ofType="account" select="com.ruki.dao.IAccountDao.findAccountByUid" column="id"></collection>
</resultMap>
<!-- 查询所有用户 -->
<!--<select id="findAll" resultType="com.ruki.domain.User">-->
<select id="findAll" resultMap="userAccountMap">
select * from user
</select>
<!-- 根据ID查用户-->
<select id="findById" parameterType="int" resultType="com.ruki.domain.User">
select * from user where id=#{id};
</select>
</mapper>
1.2.5 开启MyBatis的延迟加载策略
SqlMapConfig.xml
lazyLoadingEnabled
:懒加载,默认为true
aggressiveLazyLoading
:立即加载,需要关掉
<!-- 配置参数 -->
<settings>
<!-- 开启MyBatis支持延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 关闭MyBatis立即加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
1.2.6 测试
/**
* 测试查询所有账户
*/
@Test
public void testFindAll() throws IOException {
List<Account> accounts = accountDao.findAll();
for(Account account : accounts){
System.out.println("---每一个account的信息");
System.out.println(account);
System.out.println(account.getUser());
}
}
//----------------------------------------------------------------------------
/**
* 测试查询所有账户
*/
@Test
public void testFindAll() throws IOException {
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
System.out.println(user.getAccounts());
}
}
二、MyBatis缓存
2.1 一级缓存
- 一级缓存是
SqlSession
级别的缓存,只要SqlSession
没有flush
或close
,它就存在。 因为一级缓存的存在,导致第二次查询id相同的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。
- 此时查询出来的
System.out.println(user1 == user2);
结果是<font color="red">true</font>
- 此时查询出来的
- 当调用
SqlSession
的<font color="red">修改,添加,删除,commit(),close()</font>等方法时,就会<font color="red">清空</font>一级缓存,避免脏读。
2.2 二级缓存
二级缓存是
mapper
映射级别的缓存,多个SqlSession
去操作同一个Mapper
映射的sql语句,多个SqlSession
可以共用二级缓存,二级缓存是跨SqlSession
的。sqlSession1
去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。- 如果
SqlSession3
去执行相同 mapper映射下sql,执行commit
提交,将会清空该 mapper映射下的二级缓存区域的数据。 sqlSession2
去查询与sqlSession1
相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据,并包装成<font color="red">新的对象</font>返回- 此时
System.out.println(user1 == user2);
结果是<font color="red">false</font>
- 此时
2.2.1 二级缓存的开启与关闭
2.2.1.1 在SqlMapConfig.xml中开启二级缓存
- 因为
cacheEnabled
的取值默认就为true
,所以这一步可以省略不配置。为true
代表开启二级缓存;为false
代表不开启二级缓存。
<settings>
<!-- 开启二级缓存支持-->
<setting name="cacheEnabled" value="true"/>
</settings>
2.2.1.2 配置相关的Mapper映射文件
<cache>
标签表示当前这个mapper映射将使用二级缓存
<?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.ruki.dao.IUserDao">
<!-- 开启user支持二级缓存-->
<cache />
<!-- 查询所有用户 -->
<select id="findAll" resultType="User">
select * from user
</select>
<!-- 根据ID查用户-->
<select id="findById" parameterType="int" resultType="com.ruki.domain.User" useCache="true">
select * from user where id=#{id};
</select>
<!-- 更新用户信息-->
<update id="updateUser" parameterType="user">
update user set username=#{username}, address=#{address} where id=#{id}
</update>
</mapper>
2.2.1.3 配置select语句上的userCache属性
useCache
:true
表示使用二级缓存
<!-- 根据ID查用户-->
<select id="findById" parameterType="int" resultType="com.ruki.domain.User" useCache="true">
select * from user where id=#{id};
</select>
2.2.2 二级缓存使用注意事项
- 使用二级缓存时,所缓存的类一定要实现
java.io.Serializable
接口,这种就可以使用序列化方式来保存对象。
三、MyBatis注解开发
3.1 常用注解总结
@Insert
:实现新增@Update
:实现更新@Delete
:实现删除@Select
:实现查询@Result
:实现结果集封装@Results
:可以与@Result
一起使用,封装多个结果集@ResultMap
:实现引用@Results
定义的封装@One
:实现一对一结果集封装@Many
:实现一对多结果集封装@SelectProvider
: 实现动态SQL映射@CacheNamespace
:实现注解二级缓存的使用
3.2 基于注解实现基本的CRUD
3.2.1 实体类
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
//getter setter toString
}
3.2.2 注解形式的DAO持久层接口
package com.ruki.dao;
import com.ruki.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;
public interface UserDao {
/**
* 查找所有
* @return
*/
@Select("select * from user")
List<User> findAll();
/**
* 添加用户
*/
@Insert("insert into user(username, address, sex, birthday) values(#{username}, #{address}, #{sex}, #{birthday})")
void saveUser(User user);
/**
* 修改用户
* @param user
*/
@Update("update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id}")
void updateUser(User user);
/**
* 删除用户
* @param id
*/
@Delete("delete from user where id=#{id}")
void deleteUser(Integer id);
/**
* 根据ID查询用户
* @param id
* @return
*/
@Select("select * from user where id=#{id}")
User findById(Integer id);
/**
* 根据用户名模糊查询
* @param name
* @return
*/
@Select("select * from user where username like #{name}")
List<User> findByName(String name);
/**
* 查询全部记录数
* @return
*/
@Select("select count(*) from user")
int findTotal();
}
3.2.3 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.ruki.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>
<!-- 引入带有注解的dao接口的所在位置-->
<package name="com.ruki.dao"/>
</mappers>
</configuration>
3.2.4 测试
public class MyBatisAnnoTest {
private InputStream in;
private SqlSessionFactoryBuilder builder;
private SqlSessionFactory sessionFactory;
private SqlSession sqlSession;
private UserDao userDao;
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
builder = new SqlSessionFactoryBuilder();
sessionFactory = builder.build(in);
sqlSession = sessionFactory.openSession(true);
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.close();
in.close();
}
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
}
}
@Test
public void testSaveUser(){
User user = new User();
user.setUsername("Annotation user");
user.setAddress("上海");
user.setSex("女");
user.setBirthday(new Date());
userDao.saveUser(user);
}
@Test
public void testUpdateUser(){
User user = new User();
user.setId(53);
user.setUsername("Annotation user update");
user.setAddress("上海");
user.setSex("女");
user.setBirthday(new Date());
userDao.updateUser(user);
}
@Test
public void testDeleteUser(){
userDao.deleteUser(52);
}
@Test
public void testFindById(){
User user = userDao.findById(53);
System.out.println(user);
}
@Test
public void testFindByName(){
List<User> users = userDao.findByName("%王%");
for(User user : users){
System.out.println(user);
}
}
@Test
public void testFindTotal(){
int total = userDao.findTotal();
System.out.println(total);
}
}
3.3 基于注解的复杂关系映射
@Results
注解代替的是标签
<resultMap>
该注解中可以使用单个@Result
注解,也可以使用@Result
集合@Results({@Result(),@Result()})
@Results(@Result())
@Result
注解- 代替了
<id>
标签和<result>
标签 @Result
中属性介绍:id
是否是主键字段column
数据库的列名property
需要装配的属性名one
需要使用的@One
注解many
需要使用的@Many
注解
- 代替了
@One
注解(一对一)- 代替了
<assocation>
标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。 @One
注解属性介绍:select
指定用来多表查询的sqlmapperfetchType
会覆盖全局的配置参数lazyLoadingEnabled
使用格式:
@Result(column=" ",property="",one=@One(select=""))
- 代替了
@Many
注解(多对一)- 代替了
<Collection>
标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。 - 注意:聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性,属性的
javaType
(一般为ArrayList
)但是注解中可以不定义; 使用格式:
@Result(property="",column="",many=@Many(select=""))
- 代替了
3.3.1 实体类
User
public class User implements Serializable {
private Integer userId;
private String userName;
private String userAddress;
private String userSex;
private Date userBirthday;
//一对多关系映射,一个用户对应多个张华
private List<Account> accounts;
//getter setter toString
}
- Account
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
//多对一(mybatis中称之为一对一)的映射,一个账户只能属于一个用户
private User user;
//getter setter toString
}
3.3.2 基于注解持久层DAO接口
UserDao
@CacheNamespace(blocking = true)
public interface UserDao {
/**
* 查找所有
* @return
*/
@Select("select * from user")
@Results(id = "userMap",value = {
@Result(id = true, property = "userId", column = "id"),
@Result(property = "userName", column = "username"),
@Result(property = "userAddress", column = "address"),
@Result(property = "userSex", column = "sex"),
@Result(property = "userBirthday", column = "birthday"),
@Result(property = "accounts", column = "id",
many = @Many(select = "com.ruki.dao.AccountDao.findAccountByUid",
fetchType = FetchType.LAZY))
})
List<User> findAll();
/**
* 根据ID查询用户
* @param id
* @return
*/
@Select("select * from user where id=#{id}")
@ResultMap(value = "userMap")
User findById(Integer id);
/**
* 根据用户名模糊查询
* @param name
* @return
*/
@Select("select * from user where username like #{name}")
@ResultMap(value = "userMap")
List<User> findByName(String name);
}
AccountDao
public interface AccountDao {
/**
* 查找所有
*/
@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(select = "com.ruki.dao.UserDao.findById", fetchType = FetchType.EAGER))
})
List<Account> findAll();
/**
* 根据用户ID查询账户信息
* @param userId
* @return
*/
@Select("select * from account where uid=#{userId}")
List<Account> findAccountByUid(Integer userId);
}
3.2.3 测试
@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());
}
}
3.4 基于注解的二级缓存
3.4.1 SqlMapConfig.xml中开启
<!-- 配置二级缓存 -->
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
3.4.2 持久层接口中使用注解配置二级缓存
@CacheNamespace(blocking = true)
public interface UserDao {}
One comment
文章的确不错啊https://www.cscnn.com/