文章目录
1 MyBatis 延迟加载迟略
延迟加载:需要用到数据时才进行加载,不需要数据时就不加载。延迟加载也称懒加载。优点:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。缺点:只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
1.1 使用 association 实现一对一延迟加载
查询账户(Account)信息并且关联查询用户(User)信息。当我们需要查询用户(User)信息时再查询用户(User)信息。把对用户(User)信息的按需去查询就是延迟加载。
1.1.1 账户的持久层DAO接口
//在Account里添加User属性
public interface IAccountDao {
List<Account> findAllAccountUser();
}
1.1.2 账户的持久层映射配置
<resultMap id="accountMap" type="cn.pine.domain.Account">
<id column="id" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<association property="user" javaType="cn.pine.domain.User"
select="cn.pine.dao.IUserDao.findById"
column="uid">
</association>
</resultMap>
<select id="findAllAccountUser" resultMap="accountMap">
SELECT * FROM account
</select>
1.1.3 在 SqlMapConfig.xml 中开启延迟加载策略
<settings>
<!--注意标签顺序,在typeAliases前-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading"value="false"></setting>
</settings>
1.1.4 测试
@Test
public void testFindAllAccountUser(){
List<Account> allAccountUser = accountDao.findAllAccountUser();
}
由于只查询 Account 对象并没有取User对象,所以返回的结果中没有发出关联查询的语句
DEBUG IAccountDao.findAllAccountUser - ==> Preparing: SELECT * FROM account
IAccountDao.findAllAccountUser - ==> Parameters:
IAccountDao.findAllAccountUser - <== Total: 3
在测试中取 User 对象时,返回结果便发出关联查询。
DEBUG IAccountDao.findAllAccountUser - ==> Preparing: SELECT * FROM account
DEBUG IAccountDao.findAllAccountUser - ==> Parameters:
DEBUG IAccountDao.findAllAccountUser - <== Total: 3
DEBUG cn.pine.dao.IUserDao.findById - ==> Preparing: SELECT * FROM USER WHERE id = ?
DEBUG cn.pine.dao.IUserDao.findById - ==> Parameters: 46(Integer)
DEBUG cn.pine.dao.IUserDao.findById - <== Total: 1
User{id=46, username='赵六', birthday=Wed Mar 07 17:37:26 CST 2018, sex='男', address='杭州'}
DEBUG cn.pine.dao.IUserDao.findById - ==> Preparing: SELECT * FROM USER WHERE id = ?
DEBUG cn.pine.dao.IUserDao.findById - ==> Parameters: 45(Integer)
DEBUG cn.pine.dao.IUserDao.findById - <== Total: 1
User{id=45, username='李四', birthday=Sun Mar 04 12:04:06 CST 2018, sex='男', address='深圳'}
User{id=46, username='赵六', birthday=Wed Mar 07 17:37:26 CST 2018, sex='男', address='杭州'}
1.2 使用 Collection 实现一对多延迟加载
查询用户(User)信息并且关联查询账户(Account)信息。
1.2.1 在 User 中加入List<Account>
属性
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;
}
1.2.2 用户和账户持久层接口的方法
List<User> lazyFindAllUserAccount();
List<Account> findByUid(Integer uid);
1.2.3 用户持久层映射配置
<resultMap id="lazyUserMap" type="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<collection property="acccounts" ofType="cn.pine.domain.Account"
select="cn.pine.dao.IAccountDao.findById"
column="id">
</collection>
</resultMap>
<select id="lazyFindAllUserAccount" resultMap="lazyUserMap">
SELECT * FROM user
</select>
1.2.4 账户持久层映射配置
<select id="findById" resultType="cn.pine.domain.Account" parameterType="int">
SELECT * FROM account WHERE uid = #{uid}
</select>
测试
不取 User 中的 Account 集合时,返回结果中不发送关联查询
@Test
public void testLazyFindAllUserAccount(){
List<User> users = userDao.lazyFindAllUserAccount();
}
DEBUG UserDao.lazyFindAllUserAccount-=Preparing: SELECT* FROM user
DEBUG UserDao.lazyFindAllUserAccount -=> Parameters:
DEBUG UserDao.lazyFindAllUserAccount -<== Total: 6
2 MyBatis 缓存
MyBatis 通过缓存策略减少数据库的查询次数,提高性能,其中分为一级缓存与二级缓存。
2.1 一级缓存
一级缓存是SqlSession
范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()方法时,清空一级缓存。
设置useCache
为true
<?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="cn.pine.dao.IUserDao">
<select id="findById" resultType="user" parameterType="int" useCache="true">
SELECT * FROM USER WHERE id = #{id}
</select>
</mapper>
2.1.2 测试
@Test
public void testFindByID(){
User user1 = userDao.findById(41);
System.out.println("第一次查询:" + user1);
User user2 = userDao.findById(41);
System.out.println("第一次查询:" + user2);
System.out.println(user1==user2);
}
这里查询两次,但是返回结果只发出一次查询,并且两次查询结果,地址值相同。由此可见缓存存在。
第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 41 的用户信息,如果没有,从数据库查询用户信息。
得到用户信息,将用户信息存储到一级缓存中。如果sqlSession
去执行commit
操作(执行插入、更新、删除),清空SqlSession
中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 41 的用户信息,缓存中有,直接从缓存中获取用户信息。
-==> Preparing: SELECT*FROM USER WHERE id = ?
-> Parameters: 41(Integer)
-<== Total: 1
第一次查询:cn.pine.domain.User@4fcd19b3
第一次查询:cn.pine.domain.User@4fcd19b3
true
2.2 二级缓存
二级缓存是 mapper 映射级别的缓存,多个SqlSession
去操作同一个 Mapper
映射的SQL
语句,多个SqlSession
可以共用二级缓存,二级缓存是跨SqlSession
的。
首先开启二级缓存。
SqlSession1
去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
SqlSession2
去查询与SqlSession1
相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据
如果SqlSession3
去执行相同 mapper 映射下SQL
,执行commit
提交,将会清空该mapper
映射下的二级缓存区域的数据。
2.2.1 SqlMapConfig.xml 开启二级缓存
<settings>
<!-- 开启二级缓存,默认开启 -->
<setting name="cacheEnabled" value="true"/>
</settings>
2.2.2 配置相关 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="cn.pine.dao.IUserDao">
<!-- 开启二级缓存的支持 -->
<cache></cache>
<select id="findById" resultType="user" parameterType="int" useCache="true">
SELECT * FROM USER WHERE id = #{id}
</select>
</mapper>
2.2.3 测试
在执行第一次查询后关闭`SqlSession
public class MybatisTest {
private InputStream in;
private SqlSessionFactory factory;
@Before
public void init() throws Exception{
in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(in);
}
@After
public void destroy() throws Exception{
in.close();
}
@Test
public void testFindByID(){
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();
IUserDao dao1 = session1.getMapper(IUserDao.class);
IUserDao dao2 = session2.getMapper(IUserDao.class);
User user1 = dao1.findById(41);
System.out.println(user1);
session1.close();
User user2 = dao2.findById(41);
System.out.println(user2);
session2.close();
System.out.println(user1 == user2);
}
}
- ==> Preparing:SELECT* FROM USER WHERE id = ?
- ==> Parameters: 41(Integer)
- <== Total: 1
3 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 {
//与数据库列名不一致,省略getter,setter,toString
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
}
3.2.2 注解方式开发持久层接口
public interface IUserDao {
@Select("select * from user")
@Results(id = "userMap",
value = {
@Result(id=true, column = "id",property = "userId"),
@Result(column="username",property="userName"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress"),
@Result(column="birthday",property="userBirthday")
})
List<User> findAll();
@Select("select * from user where id = #{uid}")
@ResultMap("userMap")
User findById(Integer userId);
@Insert("insert into user(username,sex,birthday,address) values (#{userName},#{userSex},#{userBirthday},#{userAddress})")
@SelectKey(keyColumn = "id",keyProperty = "userId", resultType = Integer.class, before = false, statement = {"select last_insert_id()"})
int saveUser(User user);
@Update("update user set username=#{userName},address=#{userAddress},sex=#{userSex},birthday=#{userBirthday} where id =#{userId} ")
int updateUser(User user);
@Delete("delete from user where id = #{uid}")
int deleteUser(Integer userId);
@Select("select count(*) from user")
int findTotal();
@Select("select * from user where username like #{username}")
List<User> findByName(String name);
}
3.2.3 测试代码
public class MybatisTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
@Before
public void init()throws Exception{
in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(in);
session = factory.openSession();
userDao = session.getMapper(IUserDao.class);
}
@After
public void destroy()throws Exception {
session.commit();
session.close();
in.close();
}
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void testSave(){
User user = new User();
user.setUserName("Test_Save");
user.setUserSex("男");
user.setUserAddress("哈尔滨");
user.setUserBirthday(new Date());
int res = userDao.saveUser(user);
System.out.println("数据库影响行数---" + res);
System.out.println("插入的主键值---" + user.getUserId());
}
@Test
public void testUpdate() {
User user = userDao.findById(48);
user.setUserBirthday(new Date());
user.setUserSex("男");
int res = userDao.updateUser(user);
System.out.println(res);
}
@Test
public void testDelete() {
int res = userDao.deleteUser(51);
System.out.println(res);
}
@Test
public void testFindTotal(){
int res = userDao.findTotal();
System.out.println(res);
}
@Test
public void testFindByName() {
List<User> users = userDao.findByName("%四%");
for(User user : users) {
System.out.println(user);
}
}
}
3.3 使用注解实现复杂关系映射开发
@Results 注解
代替的是标签<resultMap>
该注解中可以使用单个@Result 注解,也可以使用@Result 集合
@Results({@Result(),@Result()})或@Results(@Result())
@Result 注解
代替了 <id>标签和<result>标签
@Result 属性
id 是否是主键字段
column 数据库的列名
property 需要装配的属性名
one 需要使用的@One 注解(@Result(one=@One)()))
many 需要使用的@Many 注解(@Result(many=@many)()))
@One 注解(一对一)
代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
@One 注解属性介绍:
select 指定用来多表查询的 sqlmapper
fetchType 会覆盖全局的配置参数 lazyLoadingEnabled。。
使用格式:
@Result(column=" ",property="",one=@One(select=""))
@Many 注解(多对一)
代替了<Collection>标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。
注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType
(一般为 ArrayList)但是注解中可以不定义;
使用格式:
@Result(property="",column="",many=@Many(select=""))
3.3.1 使用注解实现一对一复杂关系映射和延迟加载
加载账户信息延迟加载用户信息。
3.3.2.1 Account 实体类
Account 类中添加 User 属性
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
private User user;
}
3.3.2.2 账户的持久层接口使用注解配置
public interface IAccountDao {
@Select("select * from account")
@Results(id="acountMap", 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="cn.pine.dao.IUserDao.findById",
fetchType= FetchType.LAZY))
})
List<Account> findAll();
}
3.3.2.3 测试
@Test
public void testFindAll(){
List<Account> accounts = accountDao.findAll();
/* 开启注释,只发送3次查询语句
关闭后,多发两次查询
for (Account account : accounts) {
System.out.println(account);
System.out.println(account.getUser());
}
*/
}
3.3.2 使用注解实现一对多复杂关系映射和延迟加载
加载用户信息,延迟加载账户信息。
3.3.2.1 User 类加入List<Account>
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
private List<Account> accounts;
}
3.3.2.2 注解配置用户持久层
public interface IUserDao {
@Select("select * from user")
@Results(id = "userMap",
value = {
@Result(id=true, column = "id",property = "userId"),
@Result(column="username",property="userName"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress"),
@Result(column="birthday",property="userBirthday"),
@Result(column = "id",property = "accounts",
many = @Many(
select = "cn.pine.dao.IAccountDao.findByUid",
fetchType = FetchType.LAZY
))
})
List<User> findAll();
}
3.3.2.3 注解配置账户持久层
public interface IAccountDao {
@Select("select * from account where uid = #{uid}")
List<Account> findByUid(Integer userId);
}
3.3.2.4 测试
@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
public interface IUserDao {}