抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

MyBatis框架的延迟加载、MyBatis缓存、MyBatis注解开发。

MyBatis延迟加载策略


一、延迟加载的基本概念

1、延迟加载

延迟加载是指在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称为懒加载(按需加载)。


2、优点

先从单表查询,需要时再从关联表查询,大大提高了数据库性能,因为查询单表要比关联查询多张表速度要快。


3、缺点

因为只有当用到数据库时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。


4、立即加载

不管使用不使用,只要一调用方法,马上发起查询。


5、延迟加载和立即加载的使用时机

在一对多、多对多、多对一、一对一的四种表关系中可将其分为具体的两类进行分析:

  • 一对多和多对多:通常在这种情况下都是使用延迟加载的形式进行查询操作。
  • 多对一和一对一:通常在这种情况下都是使用立即加载的形式进行查询操作。

延迟加载指得其实是先查询一张表,然后根据从这张表里获取的数据再去另外一张表进行查询。以前的查询方式是先将两张关联表查询出来,然后通过相互关联的外键和主键再进行查询,这样十分浪费资源。而延迟查询却可以少查一张表,只查询表中的一部分数据。



二、使用assocation实现延迟加载

1、编写账户表和用户表的Dao接口

1
2
3
4
5
6
7
8
9
10
11
12
13
//账户表的dao接口
/**
* 查询所有账户信息
* @return
*/
List<Account> findAll();

//用户表的dao接口
/**
* 根据id查询用户
* @return
*/
User findById(int id);

2、编写账户表的持久层映射文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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.itheima.dao.IAccountDao">
<!-- 定义封装了Account和User的resultMap -->
<resultMap id="accountUserMap" type="account">
<id property="id" column="id"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
<!-- 一对一的关系映射:配置封装user的内容 -->
<association property="user" javaType="user"
select="com.itheima.dao.IUserDao.findById"
column="uid">
</association>
</resultMap>


<!-- 查询所有并封装到一个List集合 -->
<select id="findAll" resultMap="accountUserMap">
select * from account
</select>

<!-- 根据id查询账户信息 -->
<select id="findById" resultType="com.itheima.domain.Account" parameterType="int">
select * from account where uid = #{uid}
</select>
</mapper>

association标签:

  • select属性:内容为关联表的全限定dao接口名和方法名组。
  • colum属性:内容为传递给select方法的参数。

3、开启MyBatis延迟加载策略

在SqlMapConfig.xml中添加延迟加载配置

1
2
3
4
5
<!-- 开启延迟加载的支持 -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

4、编写测试类进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
`package com.itheima.test;

import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
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 org.junit.Test;

import java.io.InputStream;
import java.util.List;

public class IAccountDaoTest {
private InputStream in = null;
private SqlSession session = null;
private IAccountDao accountDao;

@Before
public void init() throws Exception {
//读取配置文件,使用输入输出流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂(用于创建数据库连接对象)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//使用工厂生产SqlSession对象
session = factory.openSession(true);
//使用SqlSession创建Dao接口的代理对象(增强其中的方法)
accountDao = session.getMapper(IAccountDao.class);
}

@After
public void destroy() throws Exception {
//手动提交
// session.commit();
//释放资源
session.close();
in.close();
}

/**
* 查询所有
*/
@Test
public void findByAllTest(){
List<Account> accounts = accountDao.findAll();
for (Account account: accounts) {
System.out.println("---------------------------");
System.out.println(account);
System.out.println(account.getUser());
}
}
}

运行结果:

一对一延迟加载运行结果

三、使用Collection实现延迟加载

1、配置编写User实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.itheima.domain;

import java.util.Date;
import java.util.List;

public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;

//一对多关系映射:主表的关系映射应该包含从表实体的集合引用
private List<Account> accounts;

public User() {
}

public List<Account> getAccounts() {
return accounts;
}

public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}

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 address) {
this.address = address;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}

2、编写账户表和用户表的Dao接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//账户表的Dao接口
/**
* 根据用户id查找账户表信息
* @param id 账户表id
* @return
*/
List<Account> findById(int uid);

//用户表的Dao接口
/**
* 查询所有用户,同时获取到用户下的所有账户信息
* @return
*/
List<User> findByAll();

3、编写用户表的持久层映射文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?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.itheima.dao.IUserDao">
<!-- 定义User的resultMap -->
<resultMap id="userMap" 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>
<!-- 配置user对象中accounts集合的映射 -->
<collection property="accounts" ofType="account"
select="com.itheima.dao.IAccountDao.findById"
column="id">
</collection>

</resultMap>


<!-- 查询所有并封装到一个List集合 -->
<select id="findByAll" resultMap="userMap">
select * from user
</select>

<!-- 根据id查询User对象 -->
<select id="findById" resultType="com.itheima.domain.User" parameterType="int">
select * from user
where id = #{id}
</select>
</mapper>

4、编写测试类进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.domain.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 org.junit.Test;

import java.io.InputStream;
import java.util.List;

public class IUserDaoTest {
private InputStream in = null;
private SqlSession session = null;
private IUserDao userDao;

@Before
public void init() throws Exception {
//读取配置文件,使用输入输出流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂(用于创建数据库连接对象)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//使用工厂生产SqlSession对象
session = factory.openSession(true);
//使用SqlSession创建Dao接口的代理对象(增强其中的方法)
userDao = session.getMapper(IUserDao.class);
}

@After
public void destroy() throws Exception {
//手动提交
// session.commit();
//释放资源
session.close();
in.close();
}

/**
* 查询所有
*/
@Test
public void findAllTest(){
List<User> users = userDao.findByAll();
for (User user: users) {
System.out.println("----------------------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}

/**
* 根据id查询用户信息
*/
@Test
public void findByIdTest(){
User user = userDao.findById(45);
System.out.println(user);
}
}

运行结果:

执行的语句

一对多延迟加载运行结果 一对多延迟加载运行结果2


MyBatis缓存

一、MyBatis的一级缓存

1、缓存的概念

缓存是指存在于内存中的临时数据


2、缓存的优点

减少和数据库的交互次数,提高执行效率。


3、缓存数据限制

(1)适用于缓存的数据

  • 经常查询并且不经常改变的数据。
  • 数据的正确与否对最终结果影响不大的数据。

(2)不适用于缓存的数据

  • 经常改变的数据。

  • 数据的正确与否对最终结果影响很大的数据。

    例如:商品的库存(常变)、银行的汇率(对结果影响较大)、股市的牌价。


4、一级缓存

一级缓存指的是MyBatis中SqlSession对象的缓存。

当我们执行查询之后,查询的结果会同时存入到SqlSession提供的一块区域。

该区域是一个Map。当我们再次查询同样的数据,MyBatis会先去SqlSession中查询是否有,有的话直接拿来使用。

值得注意的是当SqlSession对象消失时,NyBatis的一级缓存也就消失了。


5、一级缓存的证明

(1)编写User实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.itheima.domain;

import java.util.Date;
import java.util.List;

public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;

public User() {
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + 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 address) {
this.address = address;
}
}

(2)编写持久层Dao接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.itheima.dao;

import com.itheima.domain.User;

import java.util.List;

public interface IUserDao {
/**
* 查询所有用户,同时获取到用户下的所有账户信息
* @return
*/
List<User> findByAll();

/**
* 根据id查询用户
* @return
*/
User findById(int id);
}

(3)编写持久层Dao的映射文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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.itheima.dao.IUserDao">
<!-- 查询所有并封装到一个List集合 -->
<select id="findByAll" resultType="user">
select * from user u left outer join account a on u.id = a.uid
</select>

<!-- 根据id查询User对象 -->
<select id="findById" resultType="com.itheima.domain.User" parameterType="int">
select * from user
where id = #{id}
</select>
</mapper>

(4)编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.domain.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 org.junit.Test;

import java.io.InputStream;
import java.util.List;

public class IUserDaoTest {
private InputStream in = null;
private SqlSession sqlSession = null;
private IUserDao userDao;
private SqlSessionFactory factory;

@Before
public void init() throws Exception {
//读取配置文件,使用输入输出流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂(用于创建数据库连接对象)
factory = new SqlSessionFactoryBuilder().build(in);
//使用工厂生产SqlSession对象
sqlSession = factory.openSession(true);
//使用SqlSession创建Dao接口的代理对象(增强其中的方法)
userDao = sqlSession.getMapper(IUserDao.class);
}

@After
public void destroy() throws Exception {
//手动提交
// session.commit();
//释放资源
sqlSession.close();
in.close();
}

/**
* 查询所有
*/
@Test
public void findByAll(){
List<User> users = userDao.findByAll();
for (User user: users) {
System.out.println("----------------------");
System.out.println(user);
}
}

/**
* 测试一级缓存
*/
@Test
public void testFirstLevelCache(){
User user1 = userDao.findById(41);
User user2 = userDao.findById(41);
System.out.println(user1 == user2);

}
}

运行结果

一级缓存运行结果

由上图可知只执行了一次Sql查询语句。而获取的两个User对象也是同一个对象。


(5)重新编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  @Test
public void testFirstLevelCache(){
User user1 = userDao.findById(41);

/*
//清除缓存
sqlSession.close();
//再次获取SqlSession对象
sqlSession = factory.openSession();*/
sqlSession.clearCache();//此方法也可以清空缓存区的内容
userDao = sqlSession.getMapper(IUserDao.class);

User user2 = userDao.findById(41);
System.out.println("------------------------");
System.out.println(user1 == user2);
System.out.println("------------------------");
}

此时的运行结果为

一级缓存运行结果1

由上图可知两个对象不一致,并且查询了两次。证明了缓存的存在


6、一级缓存的同步

(1)在用户表的Dao接口中新添加一个方法

1
2
3
4
5
/**
* 修改用户信息
* @param user 修改用户信息
*/
void updataUser(User user);

(2)编写Dao接口的映射文件

1
2
3
4
<!-- 修改用户信息 -->
<update id="updataUser" parameterType="user">
update user set username = #{username} , address = #{address} where id = #{id}
</update>

(3)再次修改之前的测试类

这一次此时缓存区的同步信息能力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 测试缓存同步
*/
@Test
public void testClearlCache(){
//根据id查询用户
User user1 = userDao.findById(41);
//更新用户信息
user1.setUsername("updata user clear cache");
user1.setAddress("陕西汉中");
userDao.updataUser(user1);

//再次查询id为41的对象
User user2 = userDao.findById(41);

System.out.println(user1 == user2);

}

运行结果

一级缓存同步运行结果

由上面的结果得知这一次并没有从缓存区获取原来的对象,而是在修改完后重新查找了一次第41号对象的结果。


7、一级缓存的分析

一级缓存是SqlSession范围的缓存,当调用SqlSession的修改、添加、删除、commit()、close()方法时会清空一级缓存区。

当第一次发起查询用户id为41的用户信息时,先去缓存区中查找缓存区中是否存在id为41的用户信息,如果没有再从数据库中进行查询。

当得到用户信息,将用户信息存储到一级缓存中。

当SqlSession区执行commit操作(增删改),会清空SqlSession中的一级缓存,这样做的目的是为了让SqlSession中的数据始终是最新的信息,避免脏读。

当第二次发起查询id为41的用户信息时,先去缓存区中查找id为41的用户信息是否存在,若存在则直接从缓存区中获取用户信息。



二、二级缓存

1、二级缓存

二级缓存指的是MyBatis中SqlSessionFactroy对象的缓存。由同一个SqlSessionFactroy对象创建的SqlSession对象。


2、结构图

结构图

在开启MyBatis的二级缓存之后

SqlSession1去查询用户信息,将查询到的用户数据存储到二级缓存中。

此时若SqlSession3去执行相同mapper映射下sql,执行commit提交,将会清空该mapper映射下的二级缓存区域数据。

SqlSessiion2查询与SqlSession1相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中读取数据。


3、二级缓存的开启与关闭

(1)第一步:在SqlMapConfig.xml开启二级缓存

1
2
3
4
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>

因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为false 代表不开启二级缓存。


(2)第二步:配置相关的Mapper映射文件

标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。

1
2
3
4
5
6
7
8
<?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.itheima.dao.IUserDao">
<!-- 开启二级缓存的支持 -->
<cache></cache>
</mapper>

(3)第三步:配置statement上面的useCache属性

1
2
3
4
<!-- 根据 id 查询 -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>

将 UserDao.xml 映射文件中的

注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。


4、二级缓存测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.domain.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 org.junit.Test;

import java.io.InputStream;
import java.util.List;

public class SecondLevelCacheTest {
private InputStream in = null;
private SqlSessionFactory factory;

@Before
public void init() throws Exception {
//读取配置文件,使用输入输出流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂(用于创建数据库连接对象)
factory = new SqlSessionFactoryBuilder().build(in);
}

@After
public void destroy() throws Exception {
in.close();
}

/**
* 测试一级缓存的存在
*/
@Test
public void testFirstLevelCache(){
SqlSession sqlSession1 = factory.openSession();
IUserDao userDao1 = sqlSession1.getMapper(IUserDao.class);
User user1 = userDao1.findById(41);
sqlSession1.close();

SqlSession sqlSession2 = factory.openSession();
IUserDao userDao2 = sqlSession2.getMapper(IUserDao.class);
User user2 = userDao2.findById(41);
sqlSession2.close();

System.out.println("------------------------");
System.out.println(user1 == user2);
System.out.println("------------------------");
}
}

运行结果

二级缓存运行结果

由上面的图可知只执行了一次,第二次执行是从缓存区中读取的。

这里的false是因为存入SqlSessionFactroy区的是数据而不是对象,所以是两个对象。


5、二级缓存的注意事项

在使用二级缓存时,所缓存的类必须要实现 java.io.Serializable 接口这种就可以使用序列化方式来保存对象。




MyBatis注解开发


一、MyBatis注解开发的环境搭建

1、常用注解说明

注解 注解说明
@Insert 实现新增
@Updata 实现更新
@Delete 实现删除
@Select 实现查询
@Result 实现结果集封装
@Results 可以与@Result一起使用,封装多个结果集
@ResultMap 实现引用@Results定义的封装
@One 实现一对一结果集封装
@Many 实现一对多结果集封装
@SelectProvider 实现动态SQL映射
@CacheNamespace 实现注解二级缓存的使用

2、配置POM.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>eesy_zhujiekaifa</groupId>
<artifactId>eesy_zhujiekaifa</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>

</project>

3、导入数据库信息文件——jdbcConfig.properties

1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy_mybatis
jdbc.username=root
jdbc.password=root

4、导入配置文件解析文件——log4j.properties


5、配置SqlMapConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?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"/>

<!-- 配置别名 -->
<typeAliases>
<package name="com.itheima.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>

<!-- 指定带有注解的dao接口的位置 -->
<mappers>
<package name="com.itheima.dao"/>
</mappers>
</configuration>

这样注解开放的环境就配置好了,其实和非注解开发的环境搭建是一样的。



二、单表的CRUD操作

1、配置实体类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package com.itheima.domain;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {
private Integer id;
private String username;
private String sex;
private String address;
private Date birthday;

public User() {
}

public User(Integer id, String username, String sex, String address, Date birthday) {
this.id = id;
this.username = username;
this.sex = sex;
this.address = address;
this.birthday = birthday;
}

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 String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public Date getBirthday() {
return birthday;
}

public void setBirthday(Date birthday) {
this.birthday = birthday;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
", birthday=" + birthday +
'}';
}
}

值得注意的是实体类需要实现序列化接口(Serializable)


2、编写实体类的Dao接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.itheima.dao;

import com.itheima.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中针对CRUD总共由四个注解
* @Select @Insert @Update @Delete
*/
public interface IUserDao {
/**
* 查询所有
* @return
*/
@Select("select * from user")
List<User> findAll();

/**
* 修改用户信息
* @param user 要修改的用户信息
*/
@Update("update user set username = #{username},sex = #{sex},address = #{address},birthday = #{birthday} where id = #{id}")
int updataUser(User user);

/**
* 添加用户
* @param user 要添加的用户实体对象
*/
@Insert("insert into user(username,sex,address,birthday) values(#{username},#{sex},#{address},#{birthday})")
int insertUser(User user);

/**
* 根据id删除用户信息
* @param id
*/
@Delete("delete from user where id = #{id}")
int deleteUser(int id);

/**
* 根据id查询用户的信息
* @param id 要查询的用户id
* @return
*/
@Select("select * from user where id = #{id}")
User findById(Integer id);

/**
* 根据用户名字进行模糊查询
* 第一种查询方式需要在传入的名字参数前后添加上"%"才能进行查询
* 第二种方式却不需要
* @param username 要进行模糊查询的用户名字
* @return
*/
// @Select("select * from user where username like #{username}")
@Select("select * from user where username like '%${value}%'")
List<User> findByName(String username);

/**
* 查询所有的记录总条数
* @return 返回总的条数
*/
@Select("select count(*) from user")
Integer findTotal();
}

3、编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.domain.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 org.junit.Test;

import java.io.InputStream;
import java.util.Date;
import java.util.List;

public class AnnoCRUDTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private IUserDao userDao;

@Before
public void init() throws Exception{
//使用输入流加载配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//根据输入流创建SqlSessionFactroy对象
factory = new SqlSessionFactoryBuilder().build(in);
//根据SqlSessionFactroy对象构建SqlSession对象
sqlSession = factory.openSession(true);
//使用SqlSession对象代理增强Dao接口的方法
userDao = sqlSession.getMapper(IUserDao.class);
}

@After
public void destroy() throws Exception{
//释放资源
sqlSession.close();
in.close();
}

/**
* 查询所有用户信息
*/
@Test
public void findAllTest(){
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}

/**
* 新增用户
*/
@Test
public void insertUserTest(){
System.out.println(userDao.insertUser(new User(null,"mybatis insert","男","西安",new Date())));
}

/**
* 修改用户信息
*/
@Test
public void updataUserTest(){
System.out.println(userDao.updataUser(new User(54,"mybatis updata","女","成都",new Date())));
}

/**
* 根据用户的id查询用户信息
*/
@Test
public void findByIdTest(){
System.out.println(userDao.findById(54));
}

/**
* 模糊查询
*/
@Test
public void findByNameTest(){
// List<User> users = userDao.findByName("%王%");
List<User> users = userDao.findByName("王");
for (User user: users) {
System.out.println(user);
}
}

/**
* 查询总记录数
*/
@Test
public void findTotalTest(){
System.out.println(userDao.findTotal());
}

/**
* 根据用户id删除用户信息
*/
@Test
public void deletdUserTest(){
System.out.println(userDao.deleteUser(54));
}

}


三、建立实体类属性和数据库表列的对应关系

1、修改实体类表的属性

1
2
3
4
5
private Integer userId;
private String username;
private String userSex;
private String userAddress;
private Date userBirthday;

数据库列的名称

数据库列名

2、重新编写Dao接口内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.itheima.dao;

import com.itheima.domain.User;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface IUserDao {
/**
* 查询所有
* @return
*/
@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();

/**
* 根据用户的id查询用户信息
* @param id 要查询的用户id
* @return
*/
@Select("select * from user where id = #{id}")
@ResultMap(value = {"userMap"})
User findById(Integer id);

/**
* 根据用户的姓名模糊查询
* @param username 要进行模糊查询的用户的名字
* @return
*/
@Select("select * from user where username like #{username}")
@ResultMap("userMap")
List<User> findByName(String username);
}

@Results注解

  • id属性和非注解时的resultMap一样,是该对应关系的唯一标识。
  • value属性用于配置实体类和数据库表列的对应关系。

@ResultMap注解:用于调用配置好的对应关系。


3、编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.domain.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 org.junit.Test;

import java.io.InputStream;
import java.util.List;

public class AnnotationCRUDTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private IUserDao userDao;

@Before
public void init() throws Exception{
//使用输入流读取用户的配置
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//根据配置文件构造SqlSessionFactory
factory = new SqlSessionFactoryBuilder().build(in);
//使用SqlSessionFactory生产SqlSession
sqlSession = factory.openSession(true);
//使用SqlSession代理增强Dao接口的方法
userDao = sqlSession.getMapper(IUserDao.class);
}

@After
public void destory() throws Exception{
//释放资源
sqlSession.close();
in.close();
}

/**
* 查询所有用户信息
*/
@Test
public void findAllTest(){
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}

/**
* 根据用户的id查询用户信息
*/
@Test
public void findByIdTest(){
System.out.println(userDao.findById(42));
}

/**
* 模糊查询
*/
@Test
public void findByNameTest(){
// List<User> users = userDao.findByName("%王%");
List<User> users = userDao.findByName("%王%");
for (User user: users) {
System.out.println(user);
}
}
}


四、一对一注解查询配置

1、新添加一个Account实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.itheima.domain;

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;
}

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;
}

@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
'}';
}
}

2、配置Account实体类的Dao接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.itheima.dao;

import com.itheima.domain.Account;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

public interface IAccountDao {
/**
* 查询所有
* @return
*/
@Select("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(property = "user",column = "uid",one = @One(
select = "com.itheima.dao.IUserDao.findById",fetchType = FetchType.EAGER))
})
List<Account> findAll();
// FetchType.EAGER:立即加载 FetchType.LAZY:延迟加载 FetchType.DEFAULT:前两种选择其中一种

/**
* 根据用户账户表中的uid查询
* @param uid
* @return
*/
@Select("select * from account where uid = #{uid}")
Account findByUid(Integer uid);
}

此时使用的依然是**@Result注解**,select属性是关联表的实体类的dao接口的方法,fetchType属性是延迟与立即加载的设置。


3、编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.itheima.test;


import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
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 org.junit.Test;

import java.io.InputStream;
import java.util.List;

public class AccountTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private IAccountDao accountDao;

@Before
public void init() throws Exception{
in = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(in);
sqlSession = factory.openSession();
accountDao = sqlSession.getMapper(IAccountDao.class);
}

@After
public void destory() throws Exception{
sqlSession.commit();
sqlSession.close();
in.close();
}

/**
* 查询所有
*/
@Test
public void findAll(){
List<Account> accounts = accountDao.findAll();
for (Account account: accounts) {
System.out.println("------------------------------");
System.out.println(account);
System.out.println(account.getUser());
}
}
}


五、一对多注解查询配置

1、修改User实体类

像原先的User实体类中添加一个Account实体类的List集合

1
2
3
4
5
6
7
8
9
10
//    一对多的关系映射
private List<Account> accounts;

public List<Account> getAccounts() {
return accounts;
}

public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}

2、修改User实体类的Dao接口注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 查询所有
* @return
*/
@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(property = "accounts",column = "id",many = @Many(select = "com.itheima.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))
})
List<User> findAll();

3、编写测试方法

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 查询所有用户信息
*/
@Test
public void findAllTest(){
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println("--------------------------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}

总结:一对多和一对一的配置方式相同。



六、注解开发的二级缓存

1、开启方法

(1)在SqlMapConfig.xml添加配置文件

1
2
3
4
<!-- 配置二级缓存 -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>

但是这个配置可以不添加,因为在MyBatis的官方文档中默认值是”true“,即默认开启。

(2)在需要开启二级缓存的Dao接口上添加注解

1
2
//在需要开启二级缓存的Dao接口上添加注解
@CacheNamespace(blocking = true)

如此二级缓存就开启完成了。









参考资料

传智黑马MyBatis

[https://www.bilibili.com/video/BV1SJ411679L]:

评论