Water's Blog

坚持更新博客中...


  • 首页

  • 标签

  • 分类

  • 归档

  • 搜索

Mybatis框架(12) —— 实现数据库的CRUD操作(基于注解)

发表于 2019-08-10 更新于 2020-01-31 分类于 Java , MyBatis
本文字数: 14k 阅读时长 ≈ 12 分钟

简介

  • 如何使用映射配置文件的方式完成 CRUD 操作在前面已经详细的介绍过了,本文将通过使用注解的方式完成 CRUD 操作,并演示 Java实体类变量名 和 数据库字段名 相同和不相同的两种不同的处理方法。

目录结构

  • src/main
    • java
      • cn/water/dao
        • UserDao.java(持久层接口)
        • User_UserDao.java(持久层接口)
      • cn/water/domain
        • User.java(实体层)
        • User_User.java(实体层)
      • resources
        • SqlMapConfig.xml(MyBatis主配置文件)
        • jdbcConfig.properties(数据库连接信息文件)
  • src/tese
    • java
      • cn/water/test
        • UserTest.java(测试文件)
        • UserUserTest.java(测试文件)

MyBatis主配置文件

jdbcConfig.properties

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

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
32
33
34
35
36
37
<?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">


<!-- mybatis的主配置文件 -->
<configuration>

<!-- 外部配置 -->
<properties resource="jdbcConfig.properties"></properties>

<!-- 指定包:实体类-->
<typeAliases>
<package name="cn.water.domain"/>
</typeAliases>

<!-- 配置环境 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<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="cn.water.dao"/>
</mappers>

</configuration>

实体类

User.java

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 cn.water.domain;

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

/**
* @author Water
* @date 2019/10/13 - 10:41
* @description Java实体类的变量名 与 数据库的字段名 相同
*/
public class User implements Serializable {

private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;

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

User_User.java

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 cn.water.domain;

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

/**
* @author Water
* @date 2019/10/13 - 11:48
* @description Java实体类的变量名 与 数据库的字段名 不相同
*/
public class User_User implements Serializable {

private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;

@Override
public String toString() {
return "User_User{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", userBirthday=" + userBirthday +
", userSex='" + userSex + '\'' +
", userAddress='" + userAddress + '\'' +
'}';
}

public Integer getUserId() {
return userId;
}

public void setUserId(Integer userId) {
this.userId = userId;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public Date getUserBirthday() {
return userBirthday;
}

public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}

public String getUserSex() {
return userSex;
}

public void setUserSex(String userSex) {
this.userSex = userSex;
}

public String getUserAddress() {
return userAddress;
}

public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
}

持久层接口

UserDao.java

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
package cn.water.dao;

import cn.water.domain.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
* @author Water
* @date 2019/10/12 - 16:43
* @description Java实体类的变量名 与 数据库的字段名 相同
*/
public interface UserDao {

/** 添加用户 */
@Insert("INSERT INTO user VALUES(#{id},#{username},#{birthday},#{sex},#{address})")
int add(User user);

/** 更新用户 */
@Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}")
int update(User user);

/** 删除用户 */
@Delete("DELETE FROM user WHERE id = #{id}")
int delete(Integer id);

/** 查询所有用户 */
@Select("SELECT * FROM user ")
List<User> findAll();

/** 查询单个用户 */
@Select("SELECT * FROM user WHERE id = #{uid}")
User findOne(Integer userId);

/** 根据姓名模糊查询 */
@Select("SELECT * FROM user WHERE username LIKE #{username}")
List<User> findByName01(String name);

@Select("SELECT COUNT(*) FROM user")
/** 查询所有用户的数量 */
Integer findTotal();


}

User_UserDao.java

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
package cn.water.dao;

import cn.water.domain.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
* @author Water
* @date 2019/10/13 - 11:50
* @description 由于 Java实体类的变量名 与 数据库的字段名 不相同
* 所以需要对 需要封装结果集的操作(查询操作) 进行设置
*/
public interface User_UserDao {

/** 查询所有用户 */
@Select("SELECT * FROM user ")
@Results(id = "user_userMap",
value = {
@Result(id = true,column = "id",property = "userId"),
@Result(id = true,column = "username",property = "userName"),
@Result(id = true,column = "birthday",property = "userBirthday"),
@Result(id = true,column = "sex",property = "userSex"),
@Result(id = true,column = "address",property = "userAddress")
})
List<User> findAll();

/** 查询用户,根据ID */
@Select("SELECT * FROM user WHERE id=#{id}")
@ResultMap("user_userMap")
User findById(Integer id);

/** 模糊查询,根据username */
@Select("SELECT * FROM user WHERE username LIKE '%${value}%' ")
@ResultMap("user_userMap")
List<User> findByName(String username);



}

测试类

UserTest.java

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
package cn.water;

import cn.water.dao.UserDao;
import cn.water.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.IOException;
import java.io.InputStream;
import java.util.Date;

/**
* @author Water
* @date 2019/10/13 - 10:48
* @description
*/
public class UserTest {

private InputStream inputStream;
private SqlSessionFactory factory;
private SqlSession session;
private UserDao dao;

@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(inputStream);
session = factory.openSession(true);
dao = session.getMapper(UserDao.class);
}

@After
public void destroy () throws IOException {
session.close();
inputStream.close();
}

/** 添加用户 */
@Test
public void test01(){
User user = new User();
user.setId(1);
user.setUsername("mybatis anno add");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("北京");
System.out.println("受影响的行数:"+dao.add(user));
}

/** 更新用户 */
@Test
public void test02(){
User user = new User();
user.setId(1);
user.setUsername("mybatis anno update");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("北京");

System.out.println("受影响的行数:"+dao.update(user));
}

/** 删除用户 */
@Test
public void test03(){
System.out.println("受影响的行数:"+dao.delete(1));
}

/** 查询所有用户 */
@Test
public void test04(){
for (User user : dao.findAll()) {
System.out.println(user);
}
}

/** 查询用户,根据ID */
@Test
public void test05(){
System.out.println(dao.findById(42));
}

/** 模糊查询,根据username */
@Test
public void test06(){
// for (User user : dao.findByName("%王%")) {
for (User user : dao.findByName("王")) {
System.out.println(user);
}
}

/** 查询用户数量 */
@Test
public void test07(){
System.out.println(dao.findTotal());
}


}

User_UserTest.java

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
package cn.water;

import cn.water.dao.UserDao;
import cn.water.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.IOException;
import java.io.InputStream;
import java.util.Date;

/**
* @author Water
* @date 2019/10/13 - 10:48
* @description
*/
public class User_UserTest {

private InputStream inputStream;
private SqlSessionFactory factory;
private SqlSession session;
private UserDao dao;

@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(inputStream);
session = factory.openSession(true);
dao = session.getMapper(UserDao.class);
}

@After
public void destroy () throws IOException {
session.close();
inputStream.close();
}

/* 查询所用用户 */
@Test
public void test04(){
for (User user : dao.findAll()) {
System.out.println(user);
}
}

/* 查询单个用户 */
@Test
public void test05(){
System.out.println(dao.findById(42));
}

/* 模糊查询 */
@Test
public void test06(){
// for (User user : dao.findByName("%王%")) {
for (User user : dao.findByName("王")) {
System.out.println(user);
}
}


}

实现CRUD操作

  • 单表的CRUD操作是最基本的操作,前面我们的学习都是基于Mybaits的映射配置文件来实现的,现在我们试着使用基于MyBatis注解的方式来实现CRUD操作。
    • 本案例的情况是:Java实体类的变量名 与 数据库的字段名 相同
  • 注解
    • @Insert:实现新增
    • @Update:实现更新
    • @Delete:实现删除
    • @Select:实现查询

添加用户

  • 持久层接口
1
2
@Insert("INSERT INTO user VALUES (#{id},#{username},#{birthday},#{sex},#{address})")
int add (User user);

更新用户

  • 持久层接口
1
2
@Update("UPDATE user SET username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} WHERE id=#{id}")
int update (User user);

删除用户

  • 持久层接口
1
2
@Delete("DELETE FROM user WHERE id=#{id}")
int delete (Integer id);

查找所有用户

  • 持久层接口
1
2
@Select("SELECT * FROM user ")
List<User> findAll();

查询单个用户

  • 持久层接口
1
2
@Select("SELECT * FROM user WHERE id=#{id}")
User findById(Integer id);

根据姓名模糊查询

  • 持久层接口
1
2
3
//    @Select("SELECT * FROM user WHERE username LIKE #{username}")
@Select("SELECT * FROM user WHERE username LIKE '%${value}%' ")
List<User> findByName(String username);

查询所有用户数量

  • 持久层接口
1
2
@Select("SELECT COUNT(*) FROM user ")
Integer findTotal();

实现查询操作

  • 当 Java实体类的变量名 与 数据库的字段名 不相同时,我们需要设置 @Results注解。
  • 注解
    • @Result:实现结果集封装
      • id:是否是主键字段
      • column:数据库的列名
      • property:需要装配的属性名
      • one:需要使用的@One注解(@Result(one=@One)()))
      • many:需要使用的@Many注解(@Result(many=@many)()))
    • @Results:封装多个结果集
    • @ResultMap:实现引用@Results定义的封装

查找所有用户

  • 持久层接口
1
2
3
4
5
6
7
8
9
10
@Select("SELECT * FROM user ")
@Results(id = "user_userMap",
value = {
@Result(id = true,column = "id",property = "userId"),
@Result(id = true,column = "username",property = "userName"),
@Result(id = true,column = "birthday",property = "userBirthday"),
@Result(id = true,column = "sex",property = "userSex"),
@Result(id = true,column = "address",property = "userAddress")
})
List<User> findAll();

查询单个用户

  • 持久层接口
1
2
3
@Select("SELECT * FROM user WHERE id=#{id}")
@ResultMap("user_userMap")
User findById(Integer id);

根据姓名模糊查询

  • 持久层接口
1
2
3
@Select("SELECT * FROM user WHERE username LIKE '%${value}%' ")
@ResultMap("user_userMap")
List<User> findByName(String username);

MyBatis框架(11) —— 缓存机制

发表于 2019-08-09 更新于 2020-01-31 分类于 Java , MyBatis
本文字数: 9.9k 阅读时长 ≈ 9 分钟

简介

  • 什么是缓存?
    • 缓存是存在于内存中的临时数据。
  • 为什么使用缓存?
    • 缓存能够减少和数据库的交互次数,提高执行效率。
  • 什么样的数据适用于缓存,什么样的数据不适用于缓存?
    • 适用于缓存:
      • 经常查询,并且不经常改变的数据。
      • 数据的正确与否对最终结果的影响不大。
    • 不适用于缓存:
      • 经常改变的数据。
      • 数据的正确与否对最终结果的影响非常大。
  • 像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提
    高性能。Mybatis 中缓存分为一级缓存,二级缓存。
    • 一级缓存
      • 当我们执行查询操作之后,Mybatis 框架会将在数据库中的查询的结果自动存储到 SqlSession 中为一级缓存划分的一块区域中。(该区域的低层结构是 Map集合)
      • 当我们再次执行同样的查询操作之后,Mybatis 框架会先从 SqlSession 中的一级缓存区域查询。如果存在数据,就直接拿出来;如果不存在数据,才会去数据库中进行查询。
      • 因为一级缓存区域被设置在 SqlSession 中,所以当 SqlSession 消失时,一级缓存也就消失。
    • 二级缓存
      • 二级缓存被存储在 SqlSessionFactory 中,同一个 SqlSessionFactory 创建的 SqlSession 共享二级缓存。

![](11.缓存\一级缓存 二级缓存.png)

目录结构

src

  • main
    • java
      • cn.water.dao
        • UserDao.java(持久层接口)
      • cn.water.domain
        • User.java(实体类)
    • resources
      • cn.water.dao
        • UserDao.xml(映射配置文件)
      • SqlMapConfig.xml(MyBatis主配置文件)
      • jdbcConfig.properties(数据库连接信息文件)
  • test
    • java.cn.water
      • MybatisTest.java(测试类)

MyBatis配置文件

jdbcConfig.properties

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

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
32
33
34
35
36
37
38
39
40
41
42
43
<?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">


<!-- mybatis的主配置文件 -->
<configuration>

<!-- 外部配置 -->
<properties resource="jdbcConfig.properties"></properties>

<!-- 配置参数 -->
<settings>
<!-- 开启 二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>

<!-- 指定包:实体类-->
<typeAliases>
<package name="cn.water.domain"/>
</typeAliases>

<!-- 配置环境 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<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="cn.water.dao"/>
</mappers>

</configuration>

实体类

User.java

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 cn.water.domain;

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

/**
* @author Water
* @date 2019/10/12 - 7:51
* @description
*/
public class User implements Serializable {

private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;

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

持久层接口

UserDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.water.dao;

import cn.water.domain.User;

/**
* @author Water
* @date 2019/10/12 - 7:51
* @description
*/
public interface UserDao {

/* */
User findById(Integer userId);

}

映射配置文件

UserDao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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.water.dao.UserDao">

<cache></cache>

<select id="findById" parameterType="INT" resultType="user" useCache="true">
<!-- <select id="findById" parameterType="INT" resultType="user" >-->
SELECT * FROM user WHERE id = #{userId};
</select>


</mapper>

测试类

MyBatisTest.java

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
package cn.water;

import cn.water.dao.UserDao;
import cn.water.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.IOException;
import java.io.InputStream;

/**
* @author Water
* @date 2019/10/12 - 7:56
* @description
*/
public class MybatisTest {


/* 成员变量 */
private InputStream inputStream;
private SqlSessionFactory factory;
private SqlSession session;

/* 初始化操作 */
@Before
public void init() throws IOException {
/* 加载 MyBatis配置文件 */
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
/* 获取 工厂类 */
factory = new SqlSessionFactoryBuilder().build(inputStream);
/* 获取 产品类 */
session = factory.openSession(true);/* 设置自动提交 */
}

/* 销毁操作 */
@After
public void destroy() throws IOException {
session.close();
inputStream.close();
}

/** 一级缓存:同一个 SqlSession */
@Test
public void test01(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查询:"+user01);
User user02 = dao.findById(41);
System.out.println("第二次查询:"+user02);
System.out.println("两次查询结果是否相等:"+(user01==user02)); //true
}

/** 一级缓存:不同 SqlSession */
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查询:"+user01);

/* commit()(执行插入、更新、删除)、close() */
session.close();
session = factory.openSession();

UserDao dao02 = session.getMapper(UserDao.class);
User user02 = dao02.findById(41);
System.out.println("第二次查询:"+user02);
System.out.println("两次查询结果是否相等:"+(user01==user02));//false
}


}

一级缓存

测试类

1
2
3
4
5
6
7
8
9
10
/** 一级缓存:同一个 SqlSession */
@Test
public void test01(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查询:"+user01);
User user02 = dao.findById(41);
System.out.println("第二次查询:"+user02);
System.out.println("两次查询结果是否相等:"+(user01==user02));
}

运行结果

  • 一级缓存是SqlSession级别的缓存,只要SqlSession没有flush或close,它就存在。
    • 我们可以发现,在测试一中虽然我们查询了两次,但最后只执行了一次数据库操作,这就是Mybatis提供给我们的一级缓存在起作用了。
    • 因为一级缓存的存在,导致第二次查询id为41的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。

![](11.缓存\控制台 证明 一级缓存.png)

一级缓存的清空

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** 一级缓存:不同 SqlSession */
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查询:"+user01);
/* commit()(执行插入、更新、删除)、close() */
session.close();
session = factory.openSession();
UserDao dao02 = session.getMapper(UserDao.class);
User user02 = dao02.findById(41);
System.out.println("第二次查询:"+user02);
System.out.println("两次查询结果是否相等:"+(user01==user02));
}

运行结果

  • 一级缓存是SqlSession范围的缓存,当调用SqlSession的commit()(修改,添加,删除),close()等方法时,就会清空一级缓存。
  • 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查
    询用户信息。得到用户信息,将用户信息存储到一级缓存中。如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
  • 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。

![](11.缓存\控制台 证明 一级缓存02.png)

二级缓存

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

开启二级缓存

  • 二级缓存的使用步骤:
    • 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
    • 第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置
    • 第三步:让当前的操作支持二级缓存(在select标签中配置)

MyBatis主配置文件

  1. 开启二级缓存支持

    cacheEnabled 默认值为 true(此配置可以省略)

1
2
3
4
5
6
7
8
<!-- 配置参数 -->
<settings>
<!-- 开启 延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 开启 二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>

映射配置文件

  1. 设置 <cache> 标签

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

1
2
3
4
5
<mapper namespace="cn.water.dao.UserDao">

<cache></cache>

</mapper>
  1. <select> 标签 设置 useCache=”true” 属性

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

1
2
3
4
5
6
7
8
9
10
<mapper namespace="cn.water.dao.UserDao">

<cache></cache>

<select id="findById" parameterType="INT" resultType="user" useCache="true" >
SELECT * FROM user WHERE id = #{userId};
</select>


</mapper>

测试类

  • 依然执行 Test02()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查询:"+user01);

/* commit()(执行插入、更新、删除)、close() */
session.close();
session = factory.openSession();

UserDao dao02 = session.getMapper(UserDao.class);
User user02 = dao02.findById(41);
System.out.println("第二次查询:"+user02);
System.out.println("两次查询结果是否相等:"+(user01==user02));//false
}

运行结果

  • 经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二
    次查询时,我们发现并没有对数据库发出sql语句,所以此时的数据就只能是来自于我们所说的二级缓存。

一级缓存

![](11.缓存\控制台 证明 一级缓存02.png)

一级缓存和二级缓存

![](11.缓存\控制台 证明 二级缓存.png)

MyBatis框架(10) —— 实现多表查询的延迟加载操作

发表于 2019-08-09 更新于 2020-01-31 分类于 Java , MyBatis
本文字数: 13k 阅读时长 ≈ 12 分钟

简介

  • 通过前面的学习,我们已经掌握了Mybatis中一对一,一对多,多对多关系的配置及实现,可以实现对象的
    关联查询。实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的账户信息。此时就是我们所说的延迟加载。

延迟加载

  • 延迟加载(懒加载)就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。
    • 优点
      • 先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张速度要快。
    • 坏处
      • 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

目录结构

  • src
    • main
      • java
        • cn.water.dao
          • AccountDao.java(持久层接口)
          • UserDao.java(持久层接口)
        • cn.water.domain
          • Account.java(实体类)
          • User.java(实体类)
      • resources
        • cn.water.dao
          • AccountDao.xml(映射配置文件)
          • UserDao.xml(映射配置文件)
        • SqlMapConfig.xml(MyBatis主配置文件)
        • jdbcConfig.properties(数据库连接信息文件)
    • test
      • java.cn.water
        • MybatisTest.java(测试类)

MyBatis主配置文件

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
32
33
34
35
36
37
<?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">


<!-- mybatis的主配置文件 -->
<configuration>

<!-- 外部配置 -->
<properties resource="jdbcConfig.properties"></properties>

<!-- 指定包:实体类-->
<typeAliases>
<package name="cn.water.domain"/>
</typeAliases>

<!-- 配置环境 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<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="cn.water.dao"/>
</mappers>

</configuration>

数据库连接信息文件

jdbcConfig.properties

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

实体类

Account.java

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
package cn.water.domain;

/**
* @author Water
* @date 2019/10/11 - 8:35
* @description
*/
public class Account {

private Integer id;
private Integer uid;
private Double money;
private User user;

@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
", 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;
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}
}

User.java

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
package cn.water.domain;

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

/**
* @author Water
* @date 2019/10/11 - 8:35
* @description
*/
public class User {

private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;

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

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

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

持久层接口

AccountDao.java

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

import cn.water.domain.Account;

import java.util.List;

/**
* @author Water
* @date 2019/10/11 - 8:35
* @description
*/
public interface AccountDao {

/* 一对一查询 */
List<Account> findAll();

/* 一对多查询 */
List<Account> findById(Integer id);

}

UserDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.water.dao;

import cn.water.domain.Account;
import cn.water.domain.User;

import java.util.List;

/**
* @author Water
* @date 2019/10/11 - 8:35
* @description
*/
public interface UserDao {

/* 一对一查询 */
User findById(Integer id);

/* 一对多查询 */
List<User> findAll();

}

映射配置文件

AccountDao.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
<?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.water.dao.AccountDao">
<!-- 【次表:Account】 -->
<resultMap id="o2o" type="account">
<id column="id" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 【主表:User】 -->
<association
property="user"
javaType="user"
select="cn.water.dao.UserDao.findById"
column="uid"
>
</association>
</resultMap>
<select id="findAll" resultMap="o2o">
SELECT * FROM account
</select>

<select id="findById" parameterType="INT" resultType="account">
SELECT * FROM account WHERE uid = #{id}
</select>

</mapper>

UserDao.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 mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="cn.water.dao.UserDao">
<select id="findById" parameterType="INT" resultType="user">
SELECT * FROM user WHERE id = #{uid}
</select>

<!-- 【主表:User】 -->
<resultMap id="o2m" type="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 【次表:Account】 -->
<collection
property="accounts"
ofType="account"
select="cn.water.dao.AccountDao.findById"
column="id"
>
</collection>
</resultMap>
<select id="findAll" resultMap="o2m">
SELECT * FROM user
</select>

</mapper>

测试类

MyBatisTest.java

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 cn.water;

import cn.water.dao.AccountDao;
import cn.water.dao.UserDao;
import cn.water.domain.Account;
import cn.water.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.IOException;
import java.io.InputStream;

/**
* @author Water
* @date 2019/10/11 - 8:49
* @description
*/
public class MyBatisTest {


/* 成员变量 */
private InputStream inputStream;
private SqlSession session;

/* 初始化操作 */
@Before
public void init() throws IOException {
/* 加载 MyBatis配置文件 */
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
/* 获取 工厂类 */
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
/* 获取 产品类 */
session = factory.openSession(true);/* 设置自动提交 */
}

/* 销毁操作 */
@After
public void destroy() throws IOException {
session.close();
inputStream.close();
}

/* OneToOne */
@Test
public void test01(){
AccountDao dao = session.getMapper(AccountDao.class);
for (Account account : dao.findAll()) {
System.out.println(account);
}
}

/* OneToMany */
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
for (User user : dao.findAll()) {
System.out.println(user);
}
}

}

一对一查询(延迟)

  • 一对一查询:从查询 账户信息(Account) 出发,关联查询 用户信息(User)
    • 立即加载:一次多表查询
      • SELECT u.*,a.* FROM user u,account a WHERE u.id = a.uid;
    • 延迟加载:多次单表查询(提供关联信息)
      • SELECT * FROM account;(获取uid)
      • SELECT * FROM user WHERE id = #{uid}; (提供uid)

Account 实体类

  • 将 用户类(User)设置为变量
1
2
3
4
5
6
7
8
9
10
11
12
    public class Account implements Serializable {

/* 成员变量 */
private Integer id;
private Integer uid;
private Double money;
private User user; /* 用户类 */

/* toString方法 省略 */
/* Setter Getter 省略 */

}

User 实体类

1
2
3
4
5
6
7
8
9
10
11
12
public class User implements Serializable {

/* 成员变量 */
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;

/* toString方法 省略 */
/* Setter Getter 省略 */
}

Account 持久层接口

1
2
/* 一对一查询 */
List<Account> findAll();

User 持久层接口

  • 返回值 对应 Account类的User变量
1
2
/* 一对一查询 */
User findById(Integer id);

Account 映射配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 【次表:Account】 -->
<resultMap id="o2o" type="account">
<id column="id" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 【主表:User】 -->
<association
property="user"
javaType="user"
select="cn.water.dao.UserDao.findById"
column="uid"
>
</association>
</resultMap>
<!-- 单表查询 -->
<select id="findAll" resultMap="o2o">
SELECT * FROM account
</select>

User 映射配置文件

1
2
3
4
<!-- 单表查询 -->
<select id="findById" parameterType="INT" resultType="user">
SELECT * FROM user WHERE id = #{uid}
</select>

测试类

1
2
3
4
5
6
7
@Test
public void test01(){
AccountDao dao = session.getMapper(AccountDao.class);
for (Account account : dao.findAll()) {
System.out.println(account);
}
}

运行结果

![](10.延迟加载\延迟加载 一对一查询 控制台输出.png)

一对多查询(延迟)

  • 一对多查询:从查询 用户信息(User) 出发,关联查询 账户信息(Account)
    • 立即加载:一次多表查询
      • SELECT * FROM user u LEFT OUTER JOIN account a ON u.id = a.uid;
    • 延迟加载:多次单表查询(通过关联信息)
      • SELECT * FROM user;(获取id)
      • SELECT * FROM account WHERE uid = #{id}(提供id)

User 实体类

  • 将 账户类(Account) 的List集合 设置为变量
1
2
3
4
5
6
7
8
9
10
11
12
13
public class User implements Serializable {

/* 成员变量 */
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;/* 账户类的集合 */

/* toString方法 省略 */
/* Setter Getter 省略 */
}

Account 实体类

1
2
3
4
5
6
7
8
9
10
11
public class Account implements Serializable {

/* 成员变量 */
private Integer id;
private Integer uid;
private Double money;

/* toString方法 省略 */
/* Setter Getter 省略 */

}

User 持久层接口

1
2
/* 一对多查询 */
List<User> findAll();

Account 持久层接口

  • 返回值 对应 User类的List<Account>变量
1
2
/* 一对多查询 */
List<Account> findById(Integer id);

User 映射配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 【主表:User】 -->
<resultMap id="o2m" type="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 【次表:Account】 -->
<collection
property="accounts"
ofType="account"
select="cn.water.dao.AccountDao.findById"
column="id"
>
</collection>
</resultMap>
<!-- 单表查询 -->
<select id="findAll" resultMap="o2m">
SELECT * FROM user
</select>

Account 映射配置文件

1
2
3
4
<!-- 单表查询 -->
<select id="findById" parameterType="INT" resultType="account">
SELECT * FROM account WHERE uid = #{id}
</select>

测试类

1
2
3
4
5
6
7
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
for (User user : dao.findAll()) {
System.out.println(user);
}
}

运行结果

![](10.延迟加载\延迟加载 一对多查询 控制台输出.png)

Mybatis框架(9) —— 实现多表查询操作

发表于 2019-08-08 更新于 2020-01-31 分类于 Java , MyBatis
本文字数: 31k 阅读时长 ≈ 28 分钟

简介

多表之间的关系

  • 一对多
    • 用户 ——> 订单、订单、订单、订单
  • 多对一(一对一)
    • 订单、订单、订单、订单 ——> 用户
    • 单个订单 ——> 用户
  • 一对一
    • 中国公民——>身份证
  • 多对多
    • 语文老师、数学老师、英语老师——>小明、小红、小黑、小绿

MyBatis中多表之间的关系

  • 本次案例主要以最为简单的用户(User)、账户(Account)、角色(Role)的模型来分析Mybatis多表关系。
    • 用户为User 表,账户为Account表。
      • 一对多关系
        • 一个用户(User)可以开设多个账户(Account)
          • 从查询 用户信息(User) 出发,关联查询 账户信息(Account)属于一对多查询
      • 一对一关系(多对一)
        • 多个账户(Account)可以对应一个用户(User)
        • 单个账户(Account)只能属于一个用户(User)
          • 从查询 账户信息(Account) 出发,关联查询 用户信息(User)属于一对一查询
      • 多对多关系
        • 一个用户(User)可以拥有多个角色(Role)
        • 一个角色(Role)可以赋予多个用户(User)
          • 多对多关系其实我们看成是双向的一对多关系。

目录结构

  • src
    • main
      • java
        • cn.water.dao
          • M2MDao.java(多对多 持久层接口)
          • O2MDao.java(一对多 持久层接口)
          • O2ODao.java(一对一 持久层接口)
        • cn.water.domain
          • M2M_Role.java(多对多 实体类)
          • M2M_User.java(多对多 实体类)
          • O2M_Account.java(一对多 实体类)
          • O2M_User.java(一对多 实体类)
          • O2O_Account.java(一对一 实体类)
          • O2O_AccountUser.java(一对一 实体类)
          • O2O_User.java(一对一 实体类)
      • resources
        • cn.water.dao
          • M2MDao.xml(多对多 映射配置文件)
          • O2MDao.xml(一对多 映射配置文件)
          • O2ODao.xml(一对一 映射配置文件)
        • SqlMapConfig.xml(MyBatis主配置文件)
        • jdbcConfig.properties(数据库连接信息文件)
    • test
      • java.cn.water
        • MybatisTest.java(测试类)

MyBatis主配置文件

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
32
33
34
35
36
37
<?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">


<!-- mybatis的主配置文件 -->
<configuration>

<!-- 外部配置 -->
<properties resource="jdbcConfig.properties"></properties>

<!-- 指定包:实体类-->
<typeAliases>
<package name="cn.water.domain"/>
</typeAliases>

<!-- 配置环境 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<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="cn.water.dao"/>
</mappers>

</configuration>

数据库连接信息文件

jdbcConfig.properties

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

实体类

M2M_Role.java

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
package cn.water.domain;

import java.io.Serializable;
import java.util.List;

/**
* @author Water
* @date 2019/10/10 - 19:11
* @description ManyToMany 多对多查询
*/
public class M2M_Role implements Serializable {

private Integer roleId;
private String roleName;
private String roleDesc;
private List<M2M_User> users;

@Override
public String toString() {
return "M2M_Role{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
", roleDesc='" + roleDesc + '\'' +
", users=" + users +
'}';
}

public Integer getRoleId() {
return roleId;
}

public void setRoleId(Integer roleId) {
this.roleId = roleId;
}

public String getRoleName() {
return roleName;
}

public void setRoleName(String roleName) {
this.roleName = roleName;
}

public String getRoleDesc() {
return roleDesc;
}

public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}

public List<M2M_User> getUsers() {
return users;
}

public void setUsers(List<M2M_User> users) {
this.users = users;
}
}

M2M_User.java

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
package cn.water.domain;

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

/**
* @author Water
* @date 2019/10/10 - 19:11
* @description ManyToMany 多对多查询
*/
public class M2M_User implements Serializable {

private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<M2M_Role> roles;

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

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

public List<M2M_Role> getRoles() {
return roles;
}

public void setRoles(List<M2M_Role> roles) {
this.roles = roles;
}
}

O2M_Account.java

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
package cn.water.domain;

import java.io.Serializable;

/**
* @author Water
* @date 2019/10/10 - 19:12
* @description OneToMany 一对多查询
*/
public class O2M_Account implements Serializable {

private Integer id;
private Integer uid;
private Double money;


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

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

O2M_User.java

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
package cn.water.domain;

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

/**
* @author Water
* @date 2019/10/10 - 19:10
* @description OneToMany 一对多查询
*/
public class O2M_User implements Serializable {

private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<O2M_Account> accounts;

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

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

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

O2O_Account.java

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
package cn.water.domain;

import java.io.Serializable;

/**
* @author Water
* @date 2019/10/10 - 19:12
* @description OneToOne 一对一查询
*/
public class O2O_Account implements Serializable {

private Integer id;
private Integer uid;
private Double money;
private O2O_User user;

@Override
public String toString() {
return "O2O_Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
", 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;
}

public O2O_User getUser() {
return user;
}

public void setUser(O2O_User user) {
this.user = user;
}
}

O2O_AccountUser.java

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
package cn.water.domain;

import java.io.Serializable;

/**
* @author Water
* @date 2019/10/10 - 19:12
* @description OneToOne 一对一查询
*/
public class O2O_AccountUser extends O2O_Account implements Serializable {

private String username;
private String address;

@Override
public String toString() {
/* 调用父类的toString方法 */
return super.toString()+" O2O_AccountUser{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getAddress() {
return address;
}

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

O2O_User.java

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 cn.water.domain;

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

/**
* @author Water
* @date 2019/10/10 - 19:10
* @description OneToOne 一对一查询
*/
public class O2O_User implements Serializable {

private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;

@Override
public String toString() {
return "O2O_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;
}
}

持久层

M2MDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.water.dao;

import cn.water.domain.M2M_Role;
import cn.water.domain.M2M_User;

import java.util.List;

/**
* @author Water
* @date 2019/10/10 - 19:24
* @description ManyToMany 多对多查询
*/
public interface M2MDao {

/* 多表查询,封装类:Role类 User类 */
List<M2M_Role> findAll01();

/* 多表查询,封装类:Role类 User类 */
List<M2M_User> findAll02();

}

O2MDao.java

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

import cn.water.domain.O2M_User;
import cn.water.domain.O2O_Account;
import cn.water.domain.O2O_User;

import java.util.List;

/**
* @author Water
* @date 2019/10/10 - 19:24
* @description OneToMany 一对多查询
*/
public interface O2MDao {

/* 多表查询,封装类:Account类 User类 */
List<O2M_User> findAll ();

}

O2ODao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.water.dao;

import cn.water.domain.O2O_Account;
import cn.water.domain.O2O_AccountUser;

import java.util.List;

/**
* @author Water
* @date 2019/10/10 - 19:24
* @description OneToOne 一对一查询
*/
public interface O2ODao {

/* 多表查询,封装类:Account类 User类*/
List<O2O_Account> findAll01 ();

/* 多表查询,封装类:父类Account 子类AccountUser */
List<O2O_AccountUser> findAll02 ();

}

映射配置文件

M2MDao.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
36
37
38
39
<?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.water.dao.M2MDao">
<!-- 【Role表】 -->
<resultMap id="role" type="m2m_role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<!-- 【User表】 -->
<collection property="users" ofType="m2m_user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</collection>
</resultMap>
<select id="findAll01" resultMap="role">
SELECT
u.*,
r.id rid,
r.role_name,
r.role_desc
FROM
role r
left outer join
user_role ur
on
r.id = ur.rid
left outer join
user u
on
u.id = ur.uid
</select>
</mapper>

O2MDao.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
<?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.water.dao.O2MDao">
<!-- 【主表:User】 -->
<resultMap id="o2m" type="o2m_user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 【次表:Account】 -->
<collection property="accounts" ofType="o2m_account">
<id column="id" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
</collection>
</resultMap>
<!-- 多表查询 -->
<select id="findAll" resultMap="o2m">
SELECT *
FROM user u
LEFT OUTER JOIN account a
ON u.id = a.uid;
</select>
</mapper>

O2ODao.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
36
37
38
39
40
41
42
43
44
45
<?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.water.dao.O2ODao">
<!-- 【次表:Account】 -->
<resultMap id="o2o" type="o2o_account">
<id column="id" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 【主表:User】 -->
<association property="user" javaType="o2o_user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>
<!-- 多表查询 -->
<select id="findAll01" resultMap="o2o">
SELECT
u.*,
a.*
FROM
user u,
account a
WHERE
u.id = a.uid;
</select>
<!-- 多表查询 -->
<select id="findAll02" resultType="o2o_accountUser">
SELECT
a.*,
u.username,
u.address
FROM
user u,
account a
WHERE
u.id = a.uid;
</select>
</mapper>

测试类

MyBatisTest.java

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
package cn.water;

import cn.water.dao.M2MDao;
import cn.water.dao.O2MDao;
import cn.water.dao.O2ODao;
import cn.water.domain.*;
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.IOException;
import java.io.InputStream;

/**
* @author Water
* @date 2019/10/10 - 19:44
* @description
*/
public class MybatisTest {

/* 成员变量 */
private InputStream inputStream;
private SqlSession session;

/* 初始化操作 */
@Before
public void init() throws IOException {
/* 加载 MyBatis配置文件 */
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
/* 获取 工厂类 */
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
/* 获取 产品类 */
session = factory.openSession(true);/* 设置自动提交 */
}

/* 销毁操作 */
@After
public void destroy() throws IOException {
session.close();
inputStream.close();
}

/* OneToOne */
@Test
public void test01(){
O2ODao dao = session.getMapper(O2ODao.class);
for (O2O_Account account : dao.findAll01()) {
System.out.println(account);
}
}

/* OneToOne */
@Test
public void test02(){
O2ODao dao = session.getMapper(O2ODao.class);
for (O2O_AccountUser accountUser : dao.findAll02()) {
System.out.println(accountUser);
}
}

/* OneToMany */
@Test
public void test03(){
O2MDao dao = session.getMapper(O2MDao.class);
for (O2M_User user : dao.findAll()) {
System.out.println(user);
}
}

/* ManyToMany */
@Test
public void test04(){
M2MDao dao = session.getMapper(M2MDao.class);
for (M2M_Role role : dao.findAll01()) {
System.out.println(role);
}
}

/* ManyToMany */
@Test
public void test05(){
M2MDao dao = session.getMapper(M2MDao.class);
for (M2M_User user : dao.findAll02()) {
System.out.println(user);
}
}


}

一对一查询

  • 从查询 账户信息(Account) 出发,关联查询 用户信息(User)

结果集

  • 当我们查询多个表的时候,我们就需要考虑考虑结果集封装类的问题了。
  • 例如,我们需要查询用户(User)的 username字段 和 address字段 以及账户(Account)的 所有字段。
    • 方法一:根据查询方式,将实体类设置为变量。
    • 方法二:根据查询结果,创建新的实体类。

方法一:将实体类设置为变量

  • 在账户类(Account)直接将 用户类(User)设置为变量,于是账户类即封装了账户信息,又封装了用户信息。
    • 以持久层接口的方法 find01 为案例,查询 Account.uid 与 User.id 相等的所有 Account信息和 User信息。

用户类

1
2
3
4
5
6
7
8
9
10
11
12
public class O2O_User implements Serializable {

/* 成员变量 */
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;

/* toString方法 省略 */
/* Setter Getter 省略 */
}

账户类(结果集封装类)

  • 将 用户类(User)设置为变量
1
2
3
4
5
6
7
8
9
10
11
12
public class O2O_Account implements Serializable {

/* 成员变量 */
private Integer id;
private Integer uid;
private Double money;
private User user; /* 用户类 */

/* toString方法 省略 */
/* Setter Getter 省略 */

}

持久层接口

1
List<O2O_Account> findAll01 ();

映射配置文件

  • 使用resultMap,定义专门的resultMap用于映射一对一查询结果。
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
<!-- 【次表:Account】 -->
<resultMap id="o2o" type="o2o_account">
<id column="id" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 【主表:User】 -->
<association property="user" javaType="o2o_user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>
<!-- 多表查询 -->
<select id="findAll01" resultMap="o2o">
SELECT
u.*,
a.*
FROM
user u,
account a
WHERE
u.id = a.uid;
</select>

测试类

1
2
3
4
5
6
7
@Test
public void test01(){
O2ODao dao = session.getMapper(O2ODao.class);
for (O2O_Account account : dao.findAll01()) {
System.out.println(account);
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
O2O_Account{
id=46,
uid=46,
money=1000.0,
user=O2O_User{id=46, username='老王', birthday=Wed Mar 07 17:37:26 CST 2018, sex='男', address='北京'}}

O2O_Account{
id=45,
uid=45,
money=1000.0,
user=O2O_User{id=45, username='传智播客', birthday=Sun Mar 04 12:04:06 CST 2018, sex='男', address='北京金燕龙'}}

方法二:创建新的实体类

  • 创建 账户子类(AccountUser),由于继承关系我们可以封装账户信息,于是我们只用再设置 usernam变量 和 address变量就可以封装用户信息了。

    • 以持久层接口的方法 find02 为案例,查询 Account.uid 与 User.id 相等的所有 Account信息和 User的usename 和 address信息。

用户类

1
2
3
4
5
6
7
8
9
10
11
12
public class O2O_User implements Serializable {

/* 成员变量 */
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;

/* toString方法 省略 */
/* Setter Getter 省略 */
}

账户类

1
2
3
4
5
6
7
8
9
10
11
public class O2O_Account implements Serializable {

/* 成员变量 */
private Integer id;
private Integer uid;
private Double money;

/* toString方法 省略 */
/* Setter Getter 省略 */

}

账户子类(结果集封装类)

  • 调用父类的toString方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class O2O_AccountUser extends O2O_Account implements Serializable {

/* 成员变量 */
private String username;
private String address;

/* toString方法 省略 */
@Override
public String toString() {
/* 调用父类的toString方法 */
return super.toString()+
" AccountUser{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}

/* Setter Getter 省略 */

}

持久层接口

1
List<O2O_AccountUser> findAll02 ();

映射配置文件

1
2
3
4
5
6
7
8
9
10
11
12
 <!-- 多表查询 -->
<select id="findAll02" resultType="o2o_accountUser">
SELECT
a.*,
u.username,
u.address
FROM
user u,
account a
WHERE
u.id = a.uid;
</select>

测试类

1
2
3
4
5
6
7
@Test
public void test02(){
O2ODao dao = session.getMapper(O2ODao.class);
for (O2O_AccountUser accountUser : dao.findAll02()) {
System.out.println(accountUser);
}
}

运行结果

1
2
3
O2O_Account{id=1, uid=46, money=1000.0, user=null}  O2O_AccountUser{username='老王', address='北京'}
O2O_Account{id=2, uid=45, money=1000.0, user=null} O2O_AccountUser{username='传智播客', address='北京金燕龙'}
O2O_Account{id=3, uid=46, money=2000.0, user=null} O2O_AccountUser{username='老王', address='北京'}

一对多查询

  • 从查询 用户信息(User) 出发,关联查询 账户信息(Account)
  • 用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息
    查询出来,我们想到了左外连接查询比较合适。

用户类

  • 一对多关系映射:主表实体类 应该包含 从表实体类的集合引用
1
2
3
4
5
6
7
8
9
10
11
12
13
public class O2M_User implements Serializable {

/* 成员变量 */
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<O2M_Account> accounts;

/* toString方法 省略 */
/* Setter Getter 省略 */
}

账户类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class O2M_Account implements Serializable {

/* 成员变量 */
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<O2M_Account> accounts;

/* toString方法 省略 */
/* Setter Getter 省略 */
}

持久层接口

1
List<O2M_User> findAll ();

映射文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 <!-- 【主表:User】 -->
<resultMap id="o2m" type="o2m_user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 【次表:Account】 -->
<collection property="accounts" ofType="o2m_account">
<id column="id" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
</collection>
</resultMap>
<!-- 多表查询 -->
<select id="findAll" resultMap="o2m">
SELECT *
FROM
user u
LEFT OUTER JOIN
account a
ON
u.id = a.uid;
</select>

测试类

1
2
3
4
5
6
7
@Test
public void test03(){
O2MDao dao = session.getMapper(O2MDao.class);
for (O2M_User user : dao.findAll()) {
System.out.println(user);
}
}

测试结果

1
2
3
4
5
6
O2M_User{id=41, username='老王', birthday=Tue Feb 27 17:47:08 CST 2018, sex='男', address='北京', accounts=[O2M_Account{id=41, uid=null, money=null}]}
O2M_User{id=42, username='小二王', birthday=Fri Mar 02 15:09:37 CST 2018, sex='女', address='北京金燕龙', accounts=[O2M_Account{id=42, uid=null, money=null}]}
O2M_User{id=43, username='小二王', birthday=Sun Mar 04 11:34:34 CST 2018, sex='女', address='北京金燕龙', accounts=[O2M_Account{id=43, uid=null, money=null}]}
O2M_User{id=45, username='传智播客', birthday=Sun Mar 04 12:04:06 CST 2018, sex='男', address='北京金燕龙', accounts=[O2M_Account{id=45, uid=45, money=1000.0}]}
O2M_User{id=46, username='老王', birthday=Wed Mar 07 17:37:26 CST 2018, sex='男', address='北京', accounts=[O2M_Account{id=46, uid=46, money=1000.0}]}
O2M_User{id=48, username='小马宝莉', birthday=Thu Mar 08 11:44:00 CST 2018, sex='女', address='北京修正', accounts=[O2M_Account{id=48, uid=null, money=null}]}

多对多查询

  • 多对多关系其实我们看成是双向的一对多关系。

用户类

  • 多对多关系映射:两个实体类应该包含彼此的集合引用
1
2
3
4
5
6
7
8
9
10
11
12
13
public class M2M_User implements Serializable {

/* 成员变量 */
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<M2M_Role> roles;

/* toString方法 省略 */
/* Setter Getter 省略 */
}

角色类

  • 多对多关系映射:两个实体类应该包含彼此的集合引用
1
2
3
4
5
6
7
8
9
10
11
public class M2M_Role implements Serializable {

/* 成员变量 */
private Integer roleId;
private String roleName;
private String roleDesc;
private List<M2M_User> users;

/* toString方法 省略 */
/* Setter Getter 省略 */
}

持久层接口

1
List<M2M_Role> findAll();

映射文件

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
 <!-- 【Role表】 -->
<resultMap id="role" type="m2m_role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<!-- 【User表】 -->
<collection property="users" ofType="m2m_user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="role">
SELECT
u.*,
r.id rid,
r.role_name,
r.role_desc
FROM
role r
left outer join
user_role ur
on
r.id = ur.rid
left outer join
user u
on
u.id = ur.uid
</select>

测试类

1
2
3
4
5
6
7
@Test
public void test04(){
M2MDao dao = session.getMapper(M2MDao.class);
for (M2M_Role role : dao.findAll()) {
System.out.println(role);
}
}

测试结果

1
2
3
M2M_Role{roleId=1, roleName='院长', roleDesc='管理整个学院', users=[M2M_User{id=41, username='老王', birthday=Tue Feb 27 17:47:08 CST 2018, sex='男', address='北京', roles=null}, M2M_User{id=45, username='传智播客', birthday=Sun Mar 04 12:04:06 CST 2018, sex='男', address='北京金燕龙', roles=null}]}
M2M_Role{roleId=2, roleName='总裁', roleDesc='管理整个公司', users=[M2M_User{id=41, username='老王', birthday=Tue Feb 27 17:47:08 CST 2018, sex='男', address='北京', roles=null}]}
M2M_Role{roleId=3, roleName='校长', roleDesc='管理整个学校', users=[]}

比较:O2O O2M

一对一

实体类

  • 用户类

    • id
    • username
    • birthday
    • sex
    • address
  • 账户类

    • id
    • uid
    • money
    • user

映射配置文件

  • association标签
    • javaType属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 【次表:Account】 -->
<resultMap id="accountUserMap" type="account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 【主表:User】 -->
<association property="user" javaType="user">
<id property="id" column="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>

一对多

实体类

  • 用户类

    • id
    • username
    • birthday
    • sex
    • address
    • LIst<Account>
  • 账户类

    • id
    • uid
    • money

映射配置文件

  • collection标签
    • ofType属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 【主表:User】 -->
<resultMap id="userAccountMap" type="user">
<id property="id" column="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
<!-- 【次表:Account】 -->
<collection property="accounts" ofType="account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
</collection>
</resultMap>

Mybatis框架(8) —— Java与数据库的数据匹配方式

发表于 2019-08-08 更新于 2020-01-31 分类于 Java , MyBatis
本文字数: 2.3k 阅读时长 ≈ 2 分钟

方式一:修改 JavaBean变量

  • 在 JavaBean中,将 变量名 设置为 数据库的字段名

MySQL数据库

  • 字段
    • id
    • username
    • birthday
    • sex
    • address

Java实体类

  • 变量名
    • id
    • username
    • birthday
    • sex
    • address

映射配置文件

1
2
3
4
5
6
7
8
9
10
<select id="findAll" resultType="cn.water.domain.User">
SELECT
id,
username,
address,
sex,
birthday
FROM
user;
</select>

方式二:修改 SQL语句

  • 在SQL语句中,为 数据库字段 设置 别名

MySQL数据库

  • 字段
    • id
    • username
    • birthday
    • sex
    • address

Java实体类

  • 变量名
    • userId
    • username
    • userBirthday
    • userSex
    • userAddress

映射配置文件

1
2
3
4
5
6
7
8
9
10
<select id="findAll" resultType="cn.water.domain.User">
SELECT
id AS userId,
username,
address AS userAddress,
sex AS userSex,
birthday AS userBrithday
FROM
user;
</select>

方式三:设置 对应关系

  • 在 映射配置文件 中,设置 JavaBean变量 与 数据库字段 的对应关系。

变量:数据类型

MySQL数据库

  • 字段
    • id
    • username
    • birthday
    • sex
    • address

Java实体类

  • 变量名
    • userId
    • username
    • userBirthday
    • userSex
    • userAddress

映射配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 设置对应关系 -->
<resultMap id="userMap" type="cn.water.domain.User">
<!-- 主键字段的对应 -->
<id property="userId" column="id" ></id>
<!-- 非主键字段的对应 -->
<result property="username" column="username" ></result>
<result property="UserBirthday" column="birthday" ></result>
<result property="UserSex" column="sex" ></result>
<result property="UserAddress" column="address" ></result>
</resultMap>

<select id="findAll" resultMap="userMap">
SELECT id * FROM user;
</select>

变量:JavaBean(多表查询)

MySQL数据库

  • 用户表
    • 字段
      • id
      • username
      • birthday
      • sex
      • address
  • 账户表
    • 字段
      • id
      • uid
      • money

Java实体类

  • 账户类
    • 变量名
      • id
      • uid
      • money
      • User

映射配置文件

1
2
3
4
5
6
7
8
9
10
11
12
 <resultMap id="userAccountsMap" 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="accountList" ofType="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
</collection>
</resultMap>

##

Mybatis框架(7) —— 连接池与事务机制

发表于 2019-08-07 更新于 2020-01-31 分类于 Java , MyBatis
本文字数: 6.5k 阅读时长 ≈ 6 分钟

连接池

  • 连接池是用于存储连接的容器。
    • 容器必须是一个线程安全的集合对象,并且具有队列的特性:先进先出。

MyBatis连接池

  • 我们在前面的WEB课程中也学习过类似的连接池技术,而在Mybatis中也有连接池技术,但是它采用的是自
    己的连接池技术。
  • 在Mybatis中我们将它的数据源dataSource分为以下几类:
    • UNPOOLED:采用传统的获取连接的方式。
    • POOLED:采用传统的 javax.sql.DataSource规范中的连接池。
    • JNDI:采用服务器提供的 JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。

![](7.连接池与事务\MyBatis DataSource jar包.png)

UNPLLED

执行语句,控制台显示结果

底层代码

  • 虽然 UNPOOLED 并没有使用池的思想,但是 MyBatis框架依然定义了 java.sql.DataSource接口 的实现类 UnpooledDataSource 来表示 UNPOOLED类型 的数据源。

POOLED

执行语句,控制台显示结果

底层代码

  • MyBatis框架定义了 java.sql.DataSource接口 的是实现类 DataSource 来表示 POOLED类型 的数据源。

MyBatis连接池的配置

  • 在 Mybatis 的 SqlMapConfig.xml配置文件 中,通过 <dataSource type=”pooled”> 来实
    现 Mybatis 中连接池的配置。
1
2
3
4
5
6
7
8
9
10
11
12
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<!-- MyBatis连接池的配置 -->
<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>

Mybatis连接池的存取

  • MyBatis 通过工厂模式来创建 DataSource连接池对象,MyBatis 定义了抽象的工厂接
    口:org.apache.ibatis.datasource.DataSourceFactory,通过其 getDataSource()方法 返回
    DataSource连接池对象。
  • 当我们需要创建 SqlSession对象 并需要执行 SQL语句 时,这时候 MyBatis 才会去调用 dataSource对象
    来创建 connection数据库连接对象。也就是说,Connection对象的创建一直延迟到执行SQL语句
    的时候。
  • 数据库连接是我们最为宝贵的资源,只有在要用到的时候,才去获取并打开连接,当我们用完了就再
    立即将数据库连接归还到连接池中。

事务控制

  • 在 JDBC 中我们可以通过手动方式将事务的提交改为手动方式,通过 setAutoCommit()方法 就可以调整。Mybatis框架 对 JDBC 进行了封装,所以 Mybatis框架 的事务控制方式,本身也是用 JDBC 的在·
    setAutoCommit()方法 来设置事务提交方式的
    。

MyBatis事务控制

  • Mybatis中事务的提交方式,本质上就是调用JDBC的setAutoCommit()来实现事务控制。
  • 我们运行之前所写的代码:
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
public class UserTest {

/* 成员变量 */
private InputStream inputStream;
private SqlSession session;
private UserDao dao;

/* 初始化操作 */
@Before
public void init() throws IOException {
/* 加载 MyBatis配置文件 */
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
/* 获取 工厂类 */
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
/* 获取 产品类 */
session = factory.openSession();
/* 获取 代理对象 */
dao = session.getMapper(UserDao.class);
}

/* 销毁操作 */
@After
public void destroy() throws IOException {
session.close();
inputStream.close();
}


/** 添加用户 */
@Test
public void test01(){
dao.add( new User(100,"ADD",new Date(),"男","UserDao") );
session.commit();
}

/** 更新用户 */
@Test
public void test02(){
dao.update( new User(100,"UPDATE",new Date(),"男","UserDao") );
session.commit();
}

/** 删除用户 */
@Test
public void test03(){
dao.delete(100);
session.commit();
}


}
  • 控制台输出结果
    • 它显示了 Connection数据库连接对象 的整个变化过程

![](7.连接池与事务/MyBatis事务 控制台输出.png)

MyBatis自动提交事务的设置

  • 通过上面的研究和分析,现在我们一起思考,为什么 CUD过程 中必须使用 sqlSession.commit()提交事
    务?
    • 主要原因就是在连接池中取出的连接,都会将调用 connection.setAutoCommit(false)方法,这样我们
      就必须使用 sqlSession.commit()方法,相当于使用了 JDBC 中的 connection.commit()方法 实现事务提
      交。
    • 我们在通过 SqlSession工厂类 生产 SqlSession类 时,可以传递布尔值 true 来设置 自动提交事务。

MyBatis动态SQL语句的编写

if 标签

  • <if>标签 的 test属性 中写的是对象的属性名,如果是包装类的对象要使用OGNL表达式的写法。
    • 另外要注意where 1=1 的作用~!

持久层接口

1
2
/** 根据用户信息,查询用户列表 */
List<User> findByUser1(User user);

映射配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- SELECT * FROM user WHERE 1 = 1 AND username LIKE #{username} AND address LIKE #{address} -->
<select id="findByUser1" resultType="user" parameterType="user">

/* SQL语句 */
SELECT * FROM user WHERE 1 = 1

/* 动态SQL语句 */
<if test="username!=null and username!='' ">
AND username LIKE #{username}
</if>
<if test="address!=null ">
AND address LIKE #{address}
</if>

</select>

测试类

1
2
3
4
5
6
@Test
public void test01(){
for (User user : dao.findByUser1(new User("老王","北京"))) {
System.out.println(user);
}
}

where 标签

  • 为了简化上面where 1=1的条件拼装,我们采用<where>标签来简化开发。

持久层接口

1
2
/** 根据用户信息,查询用户 */
List<User> findByUser2(User user);

映射配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 设置SQL语句 -->
<sql id="select">
SELECT * FROM user
</sql>

<!-- SELECT * FROM user WHERE username LIKE #{username} AND address LIKE #{address} -->
<select id="findByUser2" resultType="user" parameterType="user">

/* 使用设置的 SQL语句 */
<include refid="select"></include>

/* 动态SQL语句 */
<where>
<if test="username!=null and username!='' ">
AND username LIKE #{username}
</if>
<if test="address!=null ">
AND address LIKE #{address}
</if>
</where>

</select>

测试类

1
2
3
4
5
6
@Test
public void test02(){
for (User user : dao.findByUser2(new User("老王","北京"))) {
System.out.println(user);
}
}

foreach 标签

QueryVo类

  • 封装List集合,存储要查询的ID
1
2
3
4
5
6
7
8
9
10
11
12
public class QueryVo implements Serializable {

private List<Integer> ids;

public List<Integer> getIds() {
return ids;
}

public void setIds(List<Integer> ids) {
this.ids = ids;
}
}

持久层接口

1
2
/** 在ID范围,查询用户 */
List<User> findByIDs(QueryVo queryVo);

映射配置文件

  • SQL语句:
    • select 字段 from user where id in (?)
  • <foreach>标签 用于遍历集合,它的属性:
    • collection属性:代表要遍历的集合元素,注意编写时不要写#{}
    • open属性:代表语句的开始部分
    • close属性:代表结束部分
    • item属性:代表遍历集合的每个元素
    • sperator属性:代表分隔符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 设置SQL语句 -->
<sql id="select">
SELECT * FROM user
</sql>

<!-- 在ID范围,查询用户 -->
<select id="findByIDs" resultType="user" parameterType="queryVo" >

/* 使用设置的 SQL语句 */
<include refid="select"></include>

/* 动态SQL语句 */
<where>
<if test="list!=null and list.size()>0 ">
<foreach collection="list" open=" id IN (" close=")" item="item" separator=",">
#{item}
</foreach>
</if>
</where>


</select>

测试类

1
2
3
4
5
6
7
8
@Test
public void test03(){
List<Integer> list = new ArrayList<Integer>();
Collections.addAll(list,41,42,43,46);
for (User user : dao.findByIDs(new QueryVo(list))) {
System.out.println(user);
}
}

Mybatis框架(6) —— MyBatis主配置文件中的常用标签

发表于 2019-08-06 更新于 2020-01-31 分类于 Java , MyBatis
本文字数: 4.6k 阅读时长 ≈ 4 分钟

简介

  • 介绍 MyBatis主配置文件 中常用的三个标签
    • properties标签:用于数据库连接的内外部配置。
    • typeAliases标签:用于对全类名设置别名。
    • package标签:用于指定类。

properties标签

  • 在之前MyBatis主配置文件的环境搭建中,我们将连接数据库的4个基本信息编写在MyBatis主配置文件中,这种方式属于内部配置。
  • 我们也可以选择外部配置的方式,只要我们通过properties标签来引用外部配置文件信息即可。

内部配置

SqlMapConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


<configuration>

<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- 内部配置:连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>

外部配置

  • 外部配置的两种方式

    • resource=”jdbcConfig.properties”
    • url=”file:///C:\Users\water\IDEA-workspace\mybatis_CRUD\src\main\resources\jdbcConfig.properties”
  • URL 统一资源定位符(Uniform Resource Locator)

    • 表示一个资源位置的唯一标识。
      • 由协议、主机、端口和URI组成
        • http://localhost:8080/mybatis/demo
        • file://localhost:8080/mybatis/demo.java
  • URI 统一资源标识符(Uniform Resource Locator)

    • 在某个应用中,表示一个资源位置的唯一标识。

jdbcConfig.properties

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

SqlMapConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- MyBatis主配置文件 -->
<configuration>

<!-- 外部配置的两种方式:resource或url -->
<properties resource="jdbcConfig.properties"></properties>
<properties url="file:///C:\Users\water\IDEA-workspace\mybatis_CRUD\src\main\resources\jdbcConfig.properties"></properties>

<!-- ................. -->
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- ................. -->

</configuration>

typeAliases标签

  • 在之前MyBatis主配置文件的环境搭建中,我们在持久层接口的映射配置文件中的resultType属性中,每次都需要输入冗长的实体类全类名。我们可以通过 typeAliases标签 来取别名,从而避免输入冗长的实体类全类名问题。
  • 别名不区分大小写。

    实体类全类名

UserDao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 添加用户 -->
<select id="add" parameterType="cn.water.domain.User" ></select>

<!-- 更新用户 -->
<select id="update" parameterType="cn.water.domain.User"></select>

<!-- 删除用户 -->
<select id="delete" parameterType="INT" ></select>

<!-- 查询所有 -->
<select id="findAll" resultType="cn.water.domain.User"></select>

<!-- 查询单个用户 -->
<select id="findOne" parameterType="INT" resultType="cn.water.domain.User"></select>

起别名

SqlMapConfig.xml

1
2
3
4
5
6
7
8
9
10
<configuration>
<!-- ................. -->

<!-- 起别名 -->
<typeAliases>
<typeAlias type="cn.water.domain.User" alias="user"></typeAlias>
</typeAliases>

<!-- ................. -->
</configuration>

UserDao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 添加用户 -->
<select id="add" parameterType="user" ></select>

<!-- 更新用户 -->
<select id="update" parameterType="user"></select>

<!-- 删除用户 -->
<select id="delete" parameterType="INT" ></select>

<!-- 查询所有 -->
<select id="findAll" resultType="user"></select>

<!-- 查询单个用户 -->
<select id="findOne" parameterType="INT" resultType="user"></select>

package标签

  • 使用 TypeAliases标签 为每个实体类起别名的确简化了我们的操作,但我们也需要为每一个实体类手动的其别名,仍然有简化的余地。为了是操作更加简便,我们可以使用Package标签来指定包,来自动加载包下的所有实体类,并以其类名作为别名,不区分大小写。另外,Mappers标签中有Package属性,用于指定包,来自动加载包下的所有映射文件。相当于代替了所有的Mapper标签。

mappers标签

mapper标签

1
2
3
4
5
6
<mappers>
<mapper resource="cn/water/dao/UserDao.xml"></mapper>
<mapper resource="cn/water/dao/AccountDao.xml"></mapper>
<mapper resource="cn/water/dao/StudentDao.xml"></mapper>
<mapper resource="cn/water/dao/EmployeeDao.xml"></mapper>
</mappers>

package标签

1
2
3
<mappers>
<package name="cn.water.dao"/>
</mappers>

typeAliases标签

mapper标签

1
2
3
4
5
6
<typeAliases>
<typeAlias type="cn.water.domain.User" alias="user"></typeAlias>
<typeAlias type="cn.water.domain.Account" alias="account"></typeAlias>
<typeAlias type="cn.water.domain.Student" alias="studnet"></typeAlias>
<typeAlias type="cn.water.domain.Employee" alias="employee"></typeAlias>
</typeAliases>

package标签

  • 别名默认为类名
1
2
3
<typeAliases>
<package name="cn.water.domain"/>
</typeAliases>

Mybatis框架(4) —— 手动编写持久层实现类实现CRUD操作

发表于 2019-08-04 更新于 2020-01-31 分类于 Java , MyBatis
本文字数: 11k 阅读时长 ≈ 10 分钟

简介

  • MyBatis环境搭建 的方式在前面已经详细的介绍过了,本文仍然通过映射配置文件,但不再使用代理持久层接口实现类的方式,而是使用手动编写持久层实现类的方式来进行MySQL数据库的CRUD操作。
  • 创建持久层接口实现类的方式在实际开发中并不常用,所以我会将本文的主题放在与使用代理持久层实现类方法的比较上。
  • 改变的内容主要是测试类和持久层接口实现类。

目录结构

  • src/main
    • java
      • cn/water/dao
        • impl
          • UserDaoImpl.java(持久层实现类)
        • UserDao.java(持久层接口)
      • cn/water/domain
        • User.java(实体类)
      • resources
        • cn/water/dao
          • UserDao.xml(映射配置文件)
        • SqlMapConfig.xml(MyBatis主配置文件)
  • src/tese
    • java
      • cn/water/test
        • MybatisTest.java(测试文件)
阅读全文 »

Mybatis框架(3) —— 使用代理持久层实现类实现CRUD操作

发表于 2019-08-03 更新于 2020-01-31 分类于 Java , MyBatis
本文字数: 14k 阅读时长 ≈ 13 分钟

简介

  • MyBatis环境搭建 的方式在前面已经详细的介绍过了,本文将通过映射配置文件,使用代理持久层实现类的方式,来进行MySQL数据库的CRUD操作。
  • 环境搭建成功后,MySQL数据库的CRUD操作基于以下几个文件的基础上进行修改:
    • UserDao.java(持久层接口)
      • 添加CRUD操作的抽象方法。
    • UserDao.xml(持久层接口的映射配置文件)
      • 添加持久层接口的全类名:namespace
      • 添加CRUD操作的mapper子标签:select、update、insert、
      • 添加方法名:id
      • 添加返回值类型的全类名:resultType
阅读全文 »

基于Spring框架的Java数据库连接对象:JdbcTemplate

发表于 2019-08-02 更新于 2020-01-30 分类于 Java , JDBC
本文字数: 3.9k 阅读时长 ≈ 4 分钟

Spring JDBC

  • Spring框架对JDBC的简单封装,它提供了一个JDBCTemplate对象,简化JDBC的开发。

快速入门

  1. 导入jar包

  2. 创建JdbcTemplate对象,传递DataSource对象

    • JdbcTemplate template = new JdbcTemplate(dataSource);
  3. 调用JdbcTemplate的方法,完成CRUD的操作

    • update()
    • queryForMap()
    • queryForList()
    • query()
    • queryForObject()
阅读全文 »
1…345…7
water.

water.

Few words,many deeds.
68 日志
21 分类
43 标签
GitHub E-Mail
Links
  • 残夜
  • Morty
  • Tmiracle
  • Dxoca
  • BlankYk
  • UncleGood
0%
© 2018 – 2020 water. | 658k | 9:58
|