xiaohuihui
for me

Mybatis

2021-01-22 20:23:21
Word count: 15.1k | Reading time: 75min

Mybatis

简介

1.三层架构:

  • 表现层:用于展示数据
  • 业务层:是处理业务需求
  • 持久层:是和数据库交互的

2.持久层次技术解决方案:

JDBC技术:Connection,PreparedStatement,ResultSet

Spring的JdbcTemplate:Spring中对Jdbc的简单封装

Apache的DBUtils:它和Spring的JdbcTemplate相似,也是对Jdbc的简单封装。

以上的都不是框架,JDBC是规范,Spring的JdbcTemplate和Apache的DBUtils都只是工具类。

概述:mybatis是一个持久层框架,用java编写的。它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁琐的过程,它使用了ORM思想实现了结果集的封装。

ORM(Object Relational Mapping)对象关系映射:就是把数据库和实体类的属性对应起来,实体类中的属性和数据库的字段名称保持一致,让我们可以操作实体类就实现操作数据表。

入门

基于XML的方式开发

第一步:创建maven工程

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
36
<?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>com.xhh460</groupId>
<artifactId>mybatis1</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>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>

第二步:创建实体类和dao的接口

实体类: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
package com.xhh460.entity;

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

/**
* @Author coderYang
* @Date 2021/1/22 23:40
*/
public class User implements Serializable {

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

public Integer getId() {
return id;
}

public String getUsername() {
return username;
}

public Date getBirthday() {
return birthday;
}

public String getSex() {
return sex;
}

public String getAddress() {
return address;
}

public void setId(Integer id) {
this.id = id;
}

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

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

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

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

dao接口:IUserDao.java

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

import com.xhh460.entity.User;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/22 23:44
*/

//用户持久层接口
public interface IUserDao {
//查询所有操作
List<User> findAll();
}

第三步:创建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
<?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>
<!-- 配置内容 -->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!--配置数据源(连接池)-->
<dataSource type="POOLED">
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/user_db"/>
<property name="username" value="root"/>
<property name="password" value="admin@123"/>
</dataSource>
</environment>
</environments>

<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="com/xhh460/dao/UserDao.xml"/>
</mappers>
</configuration>

第四步:创建映射配置文件IUserDao.xml

1
2
3
4
5
6
7
8
9
10
<?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.xhh460.dao.IUserDao">
<!--配置查询所有,把查询的结果集封装到User对象里-->
<select id="findAll" resultType="com.xhh460.entity.User">
select * from user
</select>
</mapper>

注意事项

  1. 创建IUserDao.xml和IUserDao.java时名称是为了和我们之前的知识保持一致。在Mybatis中它把持久层的操作接口名称和映射文件也叫做Mapper,所以IUserDao和IUserMapper是一样的
  2. mybatis的映射文件位置必须和dao接口的包结构相同。
  3. 映射配置文件的mapper标签的namespace属性的取值必须是dao接口的全限定类名
  4. 映射配置文件的操作配置,id属性的取值必须是dao接口的方法名。

当我们完成2,3,4步操作之后,我们在开发中就无须编写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
package com.xhh460.test;

import com.xhh460.dao.IUserDao;
import com.xhh460.entity.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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/23 19:12
*/
public class MybatisTest {
public static void main(String[] args) throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产一个SqlSession对象
SqlSession session = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//5.使用代理对象执行方法
List<User> users = userDao.findAll();
for (User user : users){
System.out.println(user);
}
//6.释放资源
session.close();
in.close();
}

}

输出:

image-20210123201519158

注意事项:

​ 不要忘记在映射配置中告知mybatis要封装到哪个实体类中。配置的方式:指定实体类的全限定类名。

基于注解的方式开发

把上述项目中的IUserDao.xml文件移处,在dao接口的方法上使用@Select注解,并指定SQL语句,同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名。

IUserDao.java

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

import com.xhh460.entity.User;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/22 23:44
*/

//用户持久层接口
public interface IUserDao {
//查询所有操作
@Select("select * from user")
List<User> findAll();
}

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">
<!--mybatis主配置文件-->
<configuration>
<!-- 配置内容 -->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!--配置数据源(连接池)-->
<dataSource type="POOLED">
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/user_db"/>
<property name="username" value="root"/>
<property name="password" value="admin@123"/>
</dataSource>
</environment>
</environments>

<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件
如果是用注解来配置的话,此处应该使用class属性来指定被注解的dao全限定类名
-->
<mappers>
<!-- <mapper resource="com/xhh460/dao/IUserDao.xml"/>-->
<mapper class="com.xhh460.dao.IUserDao"/>
</mappers>
</configuration>

传统dao层代码

UserDaoImpl.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
package com.xhh460.dao;

import com.xhh460.entity.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/23 20:56
*/
public class UserDaoImpl implements IUserDao{

private SqlSessionFactory factory;

public UserDaoImpl(SqlSessionFactory factory) {
this.factory = factory;
}

@Override
public List<User> findAll() {
//1.使用工厂创建SqlSession对象
SqlSession session = factory.openSession();
//2.使用session执行查询所有方法
List<User> users = session.selectList("com.xhh460.dao.IUserDao.findAll");
session.close();
//3.返回查询结果集
return users;
}
}

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
package com.xhh460.test;

import com.xhh460.dao.IUserDao;
import com.xhh460.dao.UserDaoImpl;
import com.xhh460.entity.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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/23 19:12
*/
public class MybatisTest {
public static void main(String[] args) throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂创建dao的对象
IUserDao userDao = new UserDaoImpl(factory);
//4.使用代理对象执行方法
List<User> users = userDao.findAll();
for (User user : users){
System.out.println(user);
}
//6.释放资源
in.close();
}

}

Mybatis设计模式分析

image-20210123213525504

自定义Mybatis分析

查询所有的分析

创建代理对象的分析

mybatis在使用代理dao的方式实现增删改查时做的事情:

  1. 创建代理对象
  2. 在代理对象中调用selectList

需要自定的类:

class Resource

class SqlSessionFactoryBuilder

interface SqlSessionFactory

interface SqlSession

步骤:

1.SqlSessionFactoryBuilder接收SqlMapConfig.xml文件流,构建出SqlSessionFactory对象

2.SqlSessionFactory读取SqlMapConfig.xml中连接数据库和mapper映射信息。用来生产出真正操作数据库的SqlSession对象

3.SqlSession对象有两大作用:(1)生成接口代理对象。(2)定义通用增删改查方法。无论哪个分支,除了连接数据库信息,还需要得到sql语句

(1)生成接口代理对象:在SqlSessionImpl对象的getMapper方法中分两步来实现。第一:先用SqlSessionFactory读取数据库连接信息创建Connection对象。第二:通过jdk代理模式创建出代理对象作为getMapper方法返回值,这里主要工作是在创建对象时第三个参数处理类里面得到sql语句。执行对应CRUD操作。

(2)定义通用的增删改查方法:在SqlSessionImpl对象中提供selectList()方法,[当然实际Mybatis框架中还有selectOne,insert等方法]这些方法内也分两步。第一:用SqlSessionFactory读取数据库连接信息创建出jdbc的Connection对象。第二:直接得到sql语句,使用jdbc的Connection对象进行对应的CRUD操作。

4.封装结果集:无论使用分支1生成代理对象,还是直接使用分支2提供的CRUD方法,我们都要对返回的数据库结果集进行封装,变成java对象返回给调用者。所以我们还必须要知道调用者所需要的返回类型。

详细代码查看:F:\java框架合集\Mybatis\mybatis_design

CRUD操作

目录结构

image-20210124204828573

entity层

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
package com.xhh460.entity;

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

/**
* @Author coderYang
* @Date 2021/1/22 23:40
*/
public class User implements Serializable {

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

public Integer getId() {
return id;
}

public String getUsername() {
return username;
}

public Date getBirthday() {
return birthday;
}

public String getSex() {
return sex;
}

public String getAddress() {
return address;
}

public void setId(Integer id) {
this.id = id;
}

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

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

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

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

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

QueryVo.java

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

/**
* @Author coderYang
* @Date 2021/1/24 20:37
*/
public class QueryVo {

private User user;

public User getUser() {
return user;
}

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

dao层文件

IUserDao.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
package com.xhh460.dao;

import com.xhh460.entity.QueryVo;
import com.xhh460.entity.User;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/24 17:37
*/
//持久层接口
public interface IUserDao {
//查询所有操作
List<User> findAll();

/**
* 保存用户
* @param user
*/
void saveUser(User user);

/**
* 更新用户
* @param user
*/
void updateUser(User user);


/**
* 根据id删除用户
* @param userId
*/
void deleteUser(Integer userId);

/**
* 根据id查询用户信息
* @param userId
* @return
*/
User findById(Integer userId);

/**
* 根据名称模糊查询用户信息
* @param name
* @return
*/
List<User> findByName(String name);


/**
* 查询总用户数
* @return
*/
int findTotal();


/**
* 根据queryVo中的条件查询用户
* @param vo
* @return
*/
List<User> findUserByVo(QueryVo vo);
}

Resouces文件夹

resouces.com.xhh460.dao.IUserDao.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
46
47
48
49
<?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.xhh460.dao.IUserDao">
<!--配置查询所有,把查询的结果集封装到User对象里-->
<select id="findAll" resultType="com.xhh460.entity.User">
select * from user
</select>

<!--保存用户-->
<insert id="saveUser" parameterType="com.xhh460.entity.User">
<!--配置插入操作之后,获取插入数据的id-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user (username,address,sex,birthday)values (#{username},#{address},#{sex},#{birthday});
</insert>

<!--更新用户-->
<update id="updateUser" parameterType="com.xhh460.entity.User">
update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}
</update>

<!--删除用户-->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id = #{uid}
</delete>

<!--根据id查询用户-->
<select id="findById" parameterType="java.lang.Integer" resultType="com.xhh460.entity.User">
select * from user where id = #{uid}
</select>

<!--根据名称模糊查询用户-->
<select id="findByName" parameterType="java.lang.String" resultType="com.xhh460.entity.User">
select * from user where username like #{username}
</select>

<!--获取用户的总记录条数-->
<select id="findTotal" resultType="java.lang.Integer">
select count(id) from user
</select>

<!--根据query的条件查询用户-->
<select id="findUserByVo" parameterType="com.xhh460.entity.QueryVo" resultType="com.xhh460.entity.User">
select * from user where username like #{user.username}
</select>
</mapper>

Resources.log4j.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=f:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

Resources.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
<?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>
<!-- 配置内容 -->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--配置事务的类型-->
<transactionManager type="JDBC"/>
<!--配置数据源(连接池)-->
<dataSource type="POOLED">
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/user_db"/>
<property name="username" value="root"/>
<property name="password" value="admin@123"/>
</dataSource>
</environment>
</environments>

<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="com/xhh460/dao/IUserDao.xml"/>
</mappers>
</configuration>

对应测试文件:

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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package com.xhh460.test;

import com.xhh460.dao.IUserDao;
import com.xhh460.entity.QueryVo;
import com.xhh460.entity.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;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/24 17:50
*/
public class MybatisTest {

private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;

@Before //用于在测试方法执行之前执行
public void init() throws IOException {
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.获取SqlSession对象
sqlSession = factory.openSession();
//4.获取dao的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}

@After //用于在测试方法执行之后执行
public void destroy() throws IOException{
//提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}

/**
* 而是查找所有操作
*/
@Test
public void testFindAll(){
//执行查询所有方法
List<User> users = userDao.findAll();
for (User user : users){
System.out.println(user);
}
}

/**
* 测试保存操作
*/
@Test
public void testSave(){
User user = new User();
user.setUsername("javaer");
user.setAddress("CUP");
user.setSex("男");
user.setBirthday(new Date());

System.out.println("保存操作之前:"+user); //保存操作之前:User{id=null, username='javaer', birthday=Sun Jan 24 20:25:26 CST 2021, sex='男', address='CUP'}
//执行方法保存
userDao.saveUser(user);
System.out.println("保存操作之后:"+user); //保存操作之后:User{id=9, username='javaer', birthday=Sun Jan 24 20:25:26 CST 2021, sex='男', address='CUP'}

// 5.执行查询所有方法
List<User> users = userDao.findAll();
for (User user1 : users){
System.out.println(user1);
}
}

/**
* 测试更新操作
*/
@Test
public void testUpdate(){
User user = new User();
user.setId(6);
user.setUsername("javaer");
user.setAddress("CUP");
user.setSex("男");
user.setBirthday(new Date());

//执行保存的方法
userDao.updateUser(user);
}

/**
* 测试删除操作
*/
@Test
public void testDelete(){
//执行删除方法
userDao.deleteUser(8);
}

/**
* 根据id查询用户
*/
@Test
public void testFindOne(){
//执行查询
User user = userDao.findById(6);
if (user != null){
System.out.println(user);
}else {
System.out.println("此用户不存在");
}
}

/**
* 根据名称进行模糊查询
*/
@Test
public void testFindByName(){
//模糊查询的百分号在此处写上
List<User> users = userDao.findByName("%小%");
for (User user : users){
System.out.println(user);
}
}

/**
* 测试查询总记录条数
*/
@Test
public void testFindTotal(){
int total = userDao.findTotal();
System.out.println(total);
}


/**
* 测试使用QueryVo作为查询条件
* 将实体类的包装对象作为查询条件
*/
@Test
public void testFindByVo(){
QueryVo vo = new QueryVo();
User user = new User();
user.setUsername("%小%");
vo.setUser(user);
//模糊查询的百分号在此处写上
List<User> u = userDao.findUserByVo(vo);
for (User user1: u){
System.out.println(user1);
}
}
}
传递pojo对象

Mybatis使用ognl表达式解析对象字段的值,#{}或者${}括号中的值为pojo属性名称。

ognl表达式(Object Graphic Navigation Language):对象图导航语言,它是通过对象中的取值方法来获取数据。在写法上把get给省略了。比如我们获取用户的名称:类中的写法:user.getUsername()。OGNL表达式写法:user.username

mybatis中为什么能直接写username,而不用user.呢?

是因为子啊parameterType中已经提供了属性所属的类,所以此时不需要写对象名。

传递pojo包装对象

​ 开发中通过传递pojo传递查询条件,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。

Mybatis的输出结果封装

解决实体类属性和数据库列名不对应的两种方式方式

image-20210124230552996

此处resultMap对应以上的userMap

image-20210124231026405

查询方法源码追踪

image-20210125171212747

image-20210125171250934

image-20210125171416927

image-20210125171449606

image-20210125171538195

image-20210125171604264

image-20210125171633257

image-20210125171652182

image-20210125171813959

增删改方法源码追踪

image-20210125222648673

image-20210125222802383

image-20210125222834364

image-20210125222859727

image-20210125222928199

image-20210125222946741

无论是增加删除还是修改,最终都会走到PreparedStatement对象的execute()方法。

image-20210125223003944

PreparedStatement对象中:

  • execute():它能执行CRUD中的任意一种语句,它的返回值是一个boolean类型,表示的是是否有结果集,有结果集是true,没有则是false。
  • executeUpdate():它能执行CUD语句,查询无法执行,返回值是影响数据库记录的行数。
  • executeQuery():它只能执行Select语句,无法执行增删改,返回值是:执行结果封装的结果集ResultSet对象。
代理Dao方法源码追踪

image-20210126000358832

image-20210126000414347

image-20210126000438992

到此处我们对MapperProxy进行追踪,即代理类

image-20210126000523177

image-20210126000809038

image-20210126000858372

image-20210126001141375

image-20210126001332846

所以有了代理,我们就不用写dao实现类了。

SqlMapConfig.xml配置文件

propeties:属性

settings:全局配置参数

typeAliases:类型别名

typeHandlers:类型处理器

objectFactory:对象工厂

plugins:插件

environments:环境集合属性对象

envieronment:环境子属性对象

transactionManager:事务管理

dataSource:数据源

mappers:映射器

image-20210126130546097

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
44
45
46
47
48
49
50
<?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可以在标签内部配置数据库的信息,也可以通过外部配置文件信息
resources属性:用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在与类路径下。
url属性:
是按照Url的写法来写地址
URL:Uniform Resource Locator 统一资源定位符,可以唯一标志一个资源的位置
写法:http://localhost:8080/mybatisservier/demo1Servlet
URI:Uniform Resource Identifier 统一资源标识符,它是应用中可以唯一定位一个资源的
-->
<properties resource="jdbcConfig.properties" />

<!--使用typeAliases配置别名,它只能配置entity中类的别名-->
<typeAliases>
<!--typeAliases用于配置别名,type属性指定的是实体类全限定类名,alias属性指定别名,当指定了别名就不再区分大小写-->
<!-- <typeAlias type="com.xhh460.entity.User" alias="user"/>-->
<!--用于指定要配置别名的dao,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
<package name="com.xhh460.entity"/>
</typeAliases>


<!-- 配置环境 -->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--配置事务的类型-->
<transactionManager type="JDBC"/>
<!--配置数据源(连接池)-->
<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>
<!-- <mapper resource="com/xhh460/dao/IUserDao.xml"/>-->
<!--package标签用于指定dao接口所在的包,当指定了之后就不需要写mapper以及resource或者class了,
它能找到dao接口所对应的映射配置-->
<package name="com.xhh460.dao"/>
</mappers>
</configuration>

连接池

我们在实际的开发中都会使用到连接池,因为它可以减少我们获取连接所消耗的时间。

image-20210126215253883

mybaits中的连接池:

​ mybatis连接池提供了三种方式的配置:

​ 配置的位置:

​ 主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池的方式。

​ type属性的取值:

​ POOLED:采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对此规范的实现。

​ UNPOOLED:采用传统的获取连接的方式,虽然也实现了java.sql.DataSource接口,但是并没有使用池的思想。

​ JNDI:采用服务器提供的JNDI技术实现来获取DataSource对象,不同的服务器所能拿到的DataSource是不一样的。注意:如果不 是web或maven的war工程,是不能使用的。

POOLED属性:从池中获取一个连接来用

image-20210126221301626

实现了DataSource接口

image-20210126222356908

image-20210126222333686

POOLED过程:

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
106
107
108
109
110
111
112
113
114
115
private PooledConnection popConnection(String username, String password) throws SQLException {
boolean countedWait = false;
PooledConnection conn = null;
long t = System.currentTimeMillis();
int localBadConnectionCount = 0;

while (conn == null) {
synchronized (state) {
if (!state.idleConnections.isEmpty()) { //判断空闲池中是否还有空闲的连接
// Pool has available connection
conn = state.idleConnections.remove(0); //如果为空,则把第一个连接给拿出来用
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
}
} else {
// Pool does not have available connection
//判断活动池中的连接是否已经达到了最大数量
if (state.activeConnections.size() < poolMaximumActiveConnections) {
// Can create new connection 没满则创建新的连接
conn = new PooledConnection(dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
} else {
// Cannot create new connection
// 当连活动池中的数量达到最大的时候,则把先进入的这个池的连接对象给拿出来
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
if (longestCheckoutTime > poolMaximumCheckoutTime) {
// Can claim overdue connection
state.claimedOverdueConnectionCount++;
state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
state.accumulatedCheckoutTime += longestCheckoutTime;
state.activeConnections.remove(oldestActiveConnection);
if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
try {
oldestActiveConnection.getRealConnection().rollback();
} catch (SQLException e) {
/*
Just log a message for debug and continue to execute the following
statement like nothing happend.
Wrap the bad connection with a new PooledConnection, this will help
to not intterupt current executing thread and give current thread a
chance to join the next competion for another valid/good database
connection. At the end of this loop, bad {@link @conn} will be set as null.
*/
log.debug("Bad connection. Could not roll back");
}
}
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
oldestActiveConnection.invalidate();
if (log.isDebugEnabled()) {
log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
}
} else {
// Must wait
try {
if (!countedWait) {
state.hadToWaitCount++;
countedWait = true;
}
if (log.isDebugEnabled()) {
log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
}
long wt = System.currentTimeMillis();
state.wait(poolTimeToWait);
state.accumulatedWaitTime += System.currentTimeMillis() - wt;
} catch (InterruptedException e) {
break;
}
}
}
}
if (conn != null) {
// ping to server and check the connection is valid or not
if (conn.isValid()) {
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
conn.setCheckoutTimestamp(System.currentTimeMillis());
conn.setLastUsedTimestamp(System.currentTimeMillis());
state.activeConnections.add(conn);
state.requestCount++;
state.accumulatedRequestTime += System.currentTimeMillis() - t;
} else {
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
}
state.badConnectionCount++;
localBadConnectionCount++;
conn = null;
if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Could not get a good connection to the database.");
}
throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
}
}
}
}

}

if (conn == null) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}

return conn;
}

UNPOOLED:每次创建一个新的连接来用

image-20210126221511890

mybatis中的事务

mybatis通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚。

image-20210127144940175

mybaits中基于XML配置的动态SQL语句的使用

Mybatis中的映射文件中,前面我们的SQL都是比较简单的,有时候在业务逻辑复杂的时候,我们的SQL是动态变化的,此时在前面的学习中我们的SQL就不能满足我们的要求了。

动态SQL:

  • mappers配置文件中的几个标签:<if><where><foreach><sql>

java.com.xhh460.dao.IUserDao.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
package com.xhh460.dao;

import com.xhh460.entity.QueryVo;
import com.xhh460.entity.User;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/24 17:37
*/
//持久层接口
public interface IUserDao {
/**
* 查询所有操作
* @return
*/
List<User> findAll();

/**
* 根据id查询用户信息
* @param userId
* @return
*/
User findById(Integer userId);

/**
* 根据名称模糊查询用户信息
* @param name
* @return
*/
List<User> findByName(String name);

/**
* 根据queryVo中的条件查询用户
* @param vo
* @return
*/
List<User> findUserByVo(QueryVo vo);

/**
* 根据传入的参数条件查询
* @param user 查询的条件,有可能有用户名,有可以能有性别,有可能有地址,有可能都有
* @return
*/
List<User> findUserByCondition(User user);

/**
* 根据queryvo中提供的id集合,查询用户信息
* @param vo
* @return
*/
List<User> findUserInIds(QueryVo vo);
}

resources.com.xhh460.dao.IUserDao.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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?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.xhh460.dao.IUserDao">

<resultMap id="userMap" type="com.xhh460.entity.User"/>

<!--抽取重复的sql语句-->
<sql id="defaultUser">
select * from user
</sql>

<!--配置查询所有,把查询的结果集封装到User对象里-->
<select id="findAll" resultMap="userMap">
<include refid="defaultUser"/>
</select>

<!--根据id查询用户-->
<select id="findById" parameterType="java.lang.Integer" resultMap="userMap">
select * from user where id = #{uid}
</select>

<!--根据名称模糊查询用户-->
<select id="findByName" parameterType="java.lang.String" resultMap="userMap">
select * from user where username like #{username}
</select>

<!--根据query的条件查询用户-->
<select id="findUserByVo" parameterType="com.xhh460.entity.QueryVo" resultMap="userMap">
select * from user where username like #{user.username}
</select>

<!--根据条件查询-->
<select id="findUserByCondition" resultMap="userMap" parameterType="com.xhh460.entity.User">
<!-- select * from user where 1 = 1-->
<!-- <if test="username != null">-->
<!-- and username = #{username}-->
<!-- </if>-->
<!-- <if test="sex != null">-->
<!-- and sex = #{sex}-->
<!-- </if>-->

select * from user
<where>
<if test="username != null">
and username = #{username}
</if>
<if test="sex != null">
and sex = #{sex}
</if>
</where>
</select>


<!--根据queryvo中的id集合实现查询用户列表-->
<select id="findUserInIds" resultMap="userMap" parameterType="com.xhh460.entity.QueryVo">
select * from user
<where>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
</mapper>

test.java.com.xhh460.test.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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package com.xhh460.test;

import com.xhh460.dao.IUserDao;
import com.xhh460.entity.QueryVo;
import com.xhh460.entity.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.ArrayList;
import java.util.Date;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/24 17:50
*/
public class MybatisTest {

private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;

@Before //用于在测试方法执行之前执行
public void init() throws IOException {
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.获取SqlSession对象
sqlSession = factory.openSession(true);
//4.获取dao的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}

@After //用于在测试方法执行之后执行
public void destroy() throws IOException{
//提交事务
// sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}

/**
* 而是查找所有操作
*/
@Test
public void testFindAll(){
//执行查询所有方法
List<User> users = userDao.findAll();
for (User user : users){
System.out.println(user);
}
}

/**
* 根据id查询用户
*/
@Test
public void testFindOne(){
//执行查询
User user = userDao.findById(6);
if (user != null){
System.out.println(user);
}else {
System.out.println("此用户不存在");
}
}

/**
* 根据名称进行模糊查询
*/
@Test
public void testFindByName(){
//模糊查询的百分号在此处写上
List<User> users = userDao.findByName("%小%");
for (User user : users){
System.out.println(user);
}
}

/**
* 测试使用QueryVo作为查询条件
* 将实体类的包装对象作为查询条件
*/
@Test
public void testFindByVo(){
QueryVo vo = new QueryVo();
User user = new User();
user.setUsername("%小%");
vo.setUser(user);
//模糊查询的百分号在此处写上
List<User> u = userDao.findUserByVo(vo);
for (User user1: u){
System.out.println(user1);
}
}

/**
*根据条件查找
*/
@Test
public void testFindByCondition(){
User u = new User();
u.setUsername("javaer");
u.setSex("男");
List<User> users = userDao.findUserByCondition(u);
for (User user :users){
System.out.println(user);
}
}

@Test
public void testFindInIds(){
QueryVo vo = new QueryVo();
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(3);
list.add(6);
vo.setIds(list);

//执行查询所有的方法
List<User> users = userDao.findUserInIds(vo);
for (User user : users){
System.out.println(user);
}
}

}

mybatis中的多表操作

  • 一对多

    用户和订单之间就是一对多

  • 多对一

    订单和用户之间就是多对一个

  • 一对一

    一个人只能有一个身份证号码,一个身份证号码只能属于一个人

  • 多对多

    一个学生可以被多个老师教过,一个老师可以教多个学生

特例:如果拿出每一个订单,他都只属于一个用户,所以Mybatis就把多对一看成了一对一。

mybatis中的多表查询:

​ 示例:用户和账户

​ 一个用户可以有多个账户

​ 一个账户只能属于一个用户(多个账户也可以属于同一个用户)

​ 步骤:

​ 1.建立两张表:用户表和账户表

​ 让账户表和用户表之间具备一对多的关系:需要使用外键在账户表中添加

​ 2.建立两个实体类:用户实体类和账户实体类

​ 让用户和账户的实体类能体现出来一对多的关系

​ 3.建立两个配置文件:用户的配置文件和账户的配置文件

​ 4.实现配置:

​ 当我们查询用户时,可以同时得到用户下所包含的账户信息

​ 当我们查询账户时,可以同时得到账户的所属用户信息

建立数据表account

1
2
3
4
5
6
7
8
9
10
DROP TABLE IF EXISTS `account`;

CREATE TABLE `account` (
`ID` int(11) NOT NULL COMMENT '编号',
`UID` int(11) default NULL COMMENT '用户编号',
`MONEY` double default NULL COMMENT '金额',
PRIMARY KEY (`ID`),
KEY `FK_Reference_8` (`UID`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

然后向account表中插入数据:

image-20210127200744442

一对一查询

然后我们进行account的一对一查询操作:

image-20210127201816337

执行结果:

image-20210127201836596

目录:

image-20210127205317178

com.xhh460.dao.IAccountDao

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
package com.xhh460.dao;

import com.xhh460.entity.Account;
import com.xhh460.entity.AccountUser;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/27 16:25
*/
public interface IAccountDao {

/**
* 查询所有账户,同时还要获取到当前账户的所属用户信息
* @return
*/
List<Account> findAll();

/**
* 查询账户,并且带有用户名称和地址信息
* @return
*/
List<AccountUser> findAllAccount();

}

com.xhh460.entity.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
59
package com.xhh460.entity;

import java.io.Serializable;

/**
* @Author coderYang
* @Date 2021/1/27 16:24
*/
public class Account implements Serializable {

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

//从表实体应该包含一个主表实体的对象引用
private 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;
}


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

com.xhh460.entity.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
package com.xhh460.entity;

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

/**
* @Author coderYang
* @Date 2021/1/22 23:40
*/
public class User implements Serializable {

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

public Integer getId() {
return id;
}

public String getUsername() {
return username;
}

public Date getBirthday() {
return birthday;
}

public String getSex() {
return sex;
}

public String getAddress() {
return address;
}

public void setId(Integer id) {
this.id = id;
}

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

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

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

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

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

com.xhh460.entity.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
package com.xhh460.entity;

/**
* @Author coderYang
* @Date 2021/1/27 20:12
*/
public class AccountUser extends Account{

private String username;
private String 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;
}

@Override
public String toString() {
return super.toString()+" AccountUser{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
}

resources.com.xhh460.dao.IAccountDao.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="com.xhh460.dao.IAccountDao">

<!--定义封装account和user的resultMap-->
<resultMap id="accountUserMap" type="com.xhh460.entity.Account">
<id property="id" column="aid"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<!--一对一的关系映射:配置user的内容-->
<association property="user" column="uid" javaType="com.xhh460.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
<result property="sex" column="sex"/>
<result property="birthday" column="birthday"/>
</association>
</resultMap>

<!--配置查询所有,把查询的结果集封装到User对象里-->
<select id="findAll" resultMap="accountUserMap">
select u.*,a.id as aid,a.uid,a.money from account a,user u where u.id = a.uid
</select>

<!--查询所有账户同时包含用户名和地址信息-->
<select id="findAllAccount" resultType="com.xhh460.entity.AccountUser">
select a.*,u.username,u.address from account a ,user u where u.id = a.uid
</select>
</mapper>

测试文件AccountTest.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
package com.xhh460.test;

import com.xhh460.dao.IAccountDao;
import com.xhh460.dao.IUserDao;
import com.xhh460.entity.Account;
import com.xhh460.entity.AccountUser;
import com.xhh460.entity.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.List;

/**
* @Author coderYang
* @Date 2021/1/27 16:28
*/
public class AccountTest {

private InputStream in;
private SqlSession sqlSession;
private IAccountDao accountDao;

@Before //用于在测试方法执行之前执行
public void init() throws IOException {
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.获取SqlSession对象
sqlSession = factory.openSession(true);
//4.获取dao的代理对象
accountDao = sqlSession.getMapper(IAccountDao.class);
}

@After //用于在测试方法执行之后执行
public void destroy() throws IOException{
//提交事务
// sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}

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

/**
* 查询所有账户,同时包含用户名称和地址
*/
@Test
public void testFindAllAccountUser(){
List<AccountUser> accountUsers = accountDao.findAllAccount();
for (AccountUser accountUser : accountUsers){
System.out.println(accountUser);
}
}

}

输出结果:

image-20210127211850304

一对多查询

目录:

image-20210128001720163

java.com.xhh460.dao.IUserDao.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
package com.xhh460.dao;

import com.xhh460.entity.User;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/24 17:37
*/
//持久层接口
public interface IUserDao {

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

/**
* 根据id查询用户信息
* @param userId
* @return
*/
User findById(Integer userId);

}

java.com.xhh460.entity.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
59
package com.xhh460.entity;

import java.io.Serializable;

/**
* @Author coderYang
* @Date 2021/1/27 16:24
*/
public class Account implements Serializable {

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

//从表实体应该包含一个主表实体的对象引用
private 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;
}


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

java.com.xhh460.entity.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
81
package com.xhh460.entity;

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

/**
* @Author coderYang
* @Date 2021/1/22 23:40
*/
public class User implements Serializable {

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

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

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

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

public Integer getId() {
return id;
}

public String getUsername() {
return username;
}

public Date getBirthday() {
return birthday;
}

public String getSex() {
return sex;
}

public String getAddress() {
return address;
}

public void setId(Integer id) {
this.id = id;
}

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

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

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

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

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

resources.com.xhh460.dao.IUserDao.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
<?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.xhh460.dao.IUserDao">

<!--定义User的resultMap-->
<resultMap id="userAccountMap" type="com.xhh460.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
<result property="sex" column="sex"/>
<result property="birthday" column="birthday"/>
<!--配置user对象中accounts集合的映射-->
<collection property="accounts" ofType="com.xhh460.entity.Account">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
</collection>
</resultMap>

<!--配置查询所有,把查询的结果集封装到User对象里-->
<select id="findAll" resultMap="userAccountMap">
select *,a.id as aid from user u left outer join account a on u.id = a.uid
</select>

<!--根据id查询用户-->
<select id="findById" parameterType="java.lang.Integer" resultType="com.xhh460.entity.User">
select * from user where id = #{uid}
</select>

</mapper>

测试文件:

com.xhh460.test.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
package com.xhh460.test;

import com.xhh460.dao.IUserDao;
import com.xhh460.entity.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.ArrayList;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/27 16:28
*/
public class UserTest {

private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;

@Before //用于在测试方法执行之前执行
public void init() throws IOException {
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.获取SqlSession对象
sqlSession = factory.openSession(true);
//4.获取dao的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}

@After //用于在测试方法执行之后执行
public void destroy() throws IOException{
//提交事务
// sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}

/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user : users){
System.out.println("-----每个用户的信息-----");
System.out.println(user);
}
}



}

输出信息:

1
2
3
4
5
6
7
8
9
10
11
12
-----每个用户的信息-----
User{id=1, username='小灰灰', birthday=Thu Dec 10 00:00:00 CST 2020, sex='男', address='大理', accounts=[Account{id=1, uid=1, money=1000.0}]}
-----每个用户的信息-----
User{id=3, username='老王', birthday=Mon Dec 14 00:00:00 CST 2020, sex='男', address='大理', accounts=[Account{id=2, uid=3, money=1000.0}]}
-----每个用户的信息-----
User{id=6, username='javaer', birthday=Sun Jan 24 20:23:55 CST 2021, sex='男', address='CUP', accounts=[Account{id=3, uid=6, money=2000.0}, Account{id=4, uid=6, money=3000.0}]}
-----每个用户的信息-----
User{id=2, username='岳绮罗', birthday=Thu Feb 06 00:00:00 CST 2020, sex='女', address='大理', accounts=[]}
-----每个用户的信息-----
User{id=4, username='小红', birthday=Mon Jan 13 00:00:00 CST 2020, sex='女', address='大理', accounts=[]}
-----每个用户的信息-----
User{id=11, username='pythoner', birthday=Mon Jan 25 16:46:01 CST 2021, sex='女', address='CUP', accounts=[]}
多对多查询

一个用户可以有很多个角色

一个角色可以赋予多个用户

1.建立两张表:用户表,角色表。

​ 让用户表和角色表具有多对多的关系,需要使用中间表,中间表中包含各自的主键,在中间表中是外键

role

image-20210128145504467

中间表:user_role

image-20210128145549155

user

image-20210128145812279

2.建立两个实体:用户实体类和角色实体类

​ 让用户和角色的实体类能体现出来多对多的关系。

java.com.xhh460.entity.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
81
package com.xhh460.entity;

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

/**
* @Author coderYang
* @Date 2021/1/22 23:40
*/
public class User implements Serializable {

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

//多对多关系的映射:一个用户可以具备多个角色
private List<Role> roles;

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

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

public Integer getId() {
return id;
}

public String getUsername() {
return username;
}

public Date getBirthday() {
return birthday;
}

public String getSex() {
return sex;
}

public String getAddress() {
return address;
}

public void setId(Integer id) {
this.id = id;
}

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

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

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

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

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

main.java.com.xhh460.entity.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
60
package com.xhh460.entity;

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

/**
* @Author coderYang
* @Date 2021/1/28 14:24
*/
public class Role implements Serializable {

private Integer roleId;
private String roleName;
private String roleDesc;

//多对多的关系映射:一个角色可以赋予多个用户
private List<User> users;

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

public void setUsers(List<User> users) {
this.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;
}

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

3.创建dao层文件

java.com.xhh460.dao.IUserDao.java

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

import com.xhh460.entity.User;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/24 17:37
*/
//持久层接口
public interface IUserDao {

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

}

java.com.xhh460.entity.IRoleDao.java

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

import com.xhh460.entity.Role;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/28 14:27
*/
public interface IRoleDao {

/**
* 查询所有角色
* @return
*/
List<Role> findAll();
}

4.建立两个配置文件:用户的配置文件和角色的配置文件

resources.com.xhh460.dao.IRoleDao.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="com.xhh460.dao.IRoleDao">

<!--定义role表的ResultMap-->
<resultMap id="roleMap" type="com.xhh460.entity.Role">
<id property="roleId" column="rid"/>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
<collection property="users" ofType="com.xhh460.entity.User">
<id column="id" property="id"/>
<id column="username" property="username"/>
<id column="address" property="address"/>
<id column="sex" property="sex"/>
<id column="birthday" property="birthday"/>
</collection>
</resultMap>

<!--查询所有-->
<select id="findAll" resultMap="roleMap">
SELECT u.*,r.id as 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>

resources,com.xhh460.dao.IUserDao.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="com.xhh460.dao.IUserDao">

<!--定义User的resultMap-->
<resultMap id="userMap" type="com.xhh460.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
<result property="sex" column="sex"/>
<result property="birthday" column="birthday"/>
<!--配置角色集合的映射-->
<collection property="roles" ofType="com.xhh460.entity.Role">
<id property="roleId" column="rid"/>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
</collection>
</resultMap>

<!--配置查询所有,把查询的结果集封装到User对象里-->
<select id="findAll" resultMap="userMap">
SELECT u.*,r.id as 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>

5.实现配置:

当我们查询角色时,可以同时得到角色的所赋予的用户信息:

1
2
3
SELECT u.*,r.id as 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

image-20210128145404448

当我们查询用户时,可以同时得到用户所包含的角色信息

1
2
3
SELECT u.*,r.id as rid,r.role_name,r.role_desc FROM user u
LEFT OUTER JOIN user_role ur ON u.id = ur.UID
LEFT OUTER JOIN role r on r.id = ur.RID

image-20210128152627385

6.输出结果:

测试文件:

test.java.com.xhh460.test.RoleTest.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
package com.xhh460.test;

import com.xhh460.dao.IRoleDao;
import com.xhh460.dao.IUserDao;
import com.xhh460.entity.Role;
import com.xhh460.entity.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.List;

/**
* @Author coderYang
* @Date 2021/1/27 16:28
*/
public class RoleTest {

private InputStream in;
private SqlSession sqlSession;
private IRoleDao roleDao;

@Before //用于在测试方法执行之前执行
public void init() throws IOException {
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.获取SqlSession对象
sqlSession = factory.openSession(true);
//4.获取dao的代理对象
roleDao = sqlSession.getMapper(IRoleDao.class);
}

@After //用于在测试方法执行之后执行
public void destroy() throws IOException{
//提交事务
// sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}

/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<Role> roles = roleDao.findAll();
for (Role role : roles){
System.out.println("-----每个角色的信息-----");
System.out.println(role);
}
}



}

输出结果:

1
2
3
4
5
6
-----每个角色的信息-----
Role{roleId=1, roleName='院长', roleDesc='管理整个学院', users=[User{id=1, username='小灰灰', birthday=Thu Dec 10 00:00:00 CST 2020, sex='男', address='大理', roles=null}, User{id=3, username='老王', birthday=Mon Dec 14 00:00:00 CST 2020, sex='男', address='大理', roles=null}]}
-----每个角色的信息-----
Role{roleId=2, roleName='总裁', roleDesc='管理整个公司', users=[User{id=6, username='javaer', birthday=Sun Jan 24 20:23:55 CST 2021, sex='男', address='CUP', roles=null}]}
-----每个角色的信息-----
Role{roleId=3, roleName='校长', roleDesc='管理整个学校', users=[]}

test.java.com.xhh460.test.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
package com.xhh460.test;

import com.xhh460.dao.IUserDao;
import com.xhh460.entity.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.ArrayList;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/27 16:28
*/
public class UserTest {

private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;

@Before //用于在测试方法执行之前执行
public void init() throws IOException {
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.获取SqlSession对象
sqlSession = factory.openSession(true);
//4.获取dao的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}

@After //用于在测试方法执行之后执行
public void destroy() throws IOException{
//提交事务
// sqlSession.commit();
//释放资源
sqlSession.close();
in.close();
}

/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user : users){
System.out.println("-----每个用户的信息-----");
System.out.println(user);
}
}



}

输出结果:

1
2
3
4
5
6
7
8
-----每个用户的信息-----
User{id=1, username='小灰灰', birthday=Thu Dec 10 00:00:00 CST 2020, sex='男', address='大理', roles=[Role{roleId=1, roleName='院长', roleDesc='管理整个学院', users=null}]}
-----每个用户的信息-----
User{id=3, username='老王', birthday=Mon Dec 14 00:00:00 CST 2020, sex='男', address='大理', roles=[Role{roleId=1, roleName='院长', roleDesc='管理整个学院', users=null}]}
-----每个用户的信息-----
User{id=6, username='javaer', birthday=Sun Jan 24 20:23:55 CST 2021, sex='男', address='CUP', roles=[Role{roleId=2, roleName='总裁', roleDesc='管理整个公司', users=null}]}
-----每个用户的信息-----
User{id=null, username='null', birthday=null, sex='null', address='null', roles=[Role{roleId=3, roleName='校长', roleDesc='管理整个学校', users=null}]}

JNDI

JNDI(Java Naming and Directory Interface ),类似于在一个中心注册一个东西,以后要用的时候,只需要根据名字去注册中心查找,注册中心返回你要的东西。web程序,我们可以将一些东西(比如数据库相关的)交给服务器软件去配置和管理(有全局配置和单个web程序的配置),在程序代码中只要通过名称查找就能得到我们注册的东西,而且如果注册的东西有变,比如更换了数据库,我们只需要修改注册信息,名称不改,因此代码也不需要修改。

如:

项目目录:

项目还是原来的Mybatis项目中对数据库访问的文件,只是现在我们通过JNDI的形式去交给tomcat服务器管理数据。

image-20210128191240876

所需要进行配置的文件:

java.resources.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
<?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" />

<!--使用typeAliases配置别名,它只能配置entity中类的别名-->
<typeAliases>
<package name="com.xhh460.entity"/>
</typeAliases>


<!-- 配置环境 -->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--配置事务的类型-->
<transactionManager type="JDBC"/>
<!--配置连接数据库的必备信息 type属性表示是否使用数据源-->
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/eesy_mybatis"/>
</dataSource>
</environment>
</environments>

<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<!-- <mapper resource="com/xhh460/dao/IUserDao.xml"/>-->
<!--package标签用于指定dao接口所在的包,当指定了之后就不需要写mapper以及resource或者class了,
它能找到dao接口所对应的映射配置-->
<package name="com.xhh460.dao"/>
</mappers>
</configuration>

webapp.MEAT-INF.content.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
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!--
<Resource
name="jdbc/eesy_mybatis" 数据源的名称
type="javax.sql.DataSource" 数据源类型
auth="Container" 数据源提供者
maxActive="20" 最大活动数
maxWait="10000" 最大等待时间
maxIdle="5" 最大空闲数
username="root" 用户名
password="1234" 密码
driverClassName="com.mysql.jdbc.Driver" 驱动类
url="jdbc:mysql://localhost:3306/eesy_mybatis" 连接url字符串
/>
-->
<Resource
name="jdbc/eesy_mybatis"
type="javax.sql.DataSource"
auth="Container"
maxActive="20"
maxWait="10000"
maxIdle="5"
username="root"
password="admin@123"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/user_db"
/>
</Context>

目前我们把数据库交给tomcat服务器管理了,在程序代码中我们提供配置文件就得到了注册的信息,而且如果注册的东西有变,比如更换了数据库,我们只需要修改注册信息,名称不改,因此代码也不需要修改。

index.jsp

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
<%@ page import="java.io.InputStream" %>
<%@ page import="org.apache.ibatis.io.Resources" %>
<%@ page import="org.apache.ibatis.session.SqlSessionFactoryBuilder" %>
<%@ page import="org.apache.ibatis.session.SqlSessionFactory" %>
<%@ page import="org.apache.ibatis.session.SqlSession" %>
<%@ page import="com.xhh460.dao.IUserDao" %>
<%@ page import="com.xhh460.entity.User" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=utf-8" language="java"%>
<html>
<body>
<h2>Hello World!</h2>
<%
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产一个SqlSession对象
SqlSession sqlSession = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
//5.使用代理对象执行方法
List<User> users = userDao.findAll();
for (User user : users){
System.out.println(user);
}
//6.释放资源
sqlSession.close();
in.close();
%>
</body>
</html>

控制台输出:

image-20210128191645106

Mybatis延迟加载

问题:

​ 在一对多中,当我们有一个用户,该用户有100个账户。

​ 在查询用户的时候,要不要把关联的账户查出来?

​ 在查询账户的时候,要不要把关联的用户查出来?

​ 在查询用户时,用户下的账户信息应该是,什么时候使用,是么时候查询。

​ 在查询账户时,账户的所属用户信息应该是随着账户查询一起查询出来的。

什么是延迟加载?

​ 在真正使用数据的时候才发起查询,不用的时候不查询。按需加载(懒加载)

什么是立即加载?

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

在对应的四种表关系中:一对多,多对一,一对一,多对多。

​ 一对多,多对多:通常情况下我们都是采用延迟加载。

​ 多对一,一对一:通常情况下我们都是采用立即加载。

一对一方式(一个账户对一个用户)

image-20210129154702144

image-20210129154539083

不开启延迟加载的时候:

image-20210129154059863

开启延迟加载时:

image-20210129154427411

只有在调用的时候,才会进行sql语句的查询

一对多方式(一个用户有多个账户)

IAccountDao.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
package com.xhh460.dao;

import com.xhh460.entity.Account;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/27 16:25
*/
public interface IAccountDao {

/**
* 查询所有账户,同时还要获取到当前账户的所属用户信息
* @return
*/
List<Account> findAll();

/**
* 根据用户id查询账户信息
* @param uid
* @return
*/
List<Account> findAccountByUid(Integer uid);
}

IAccountDao.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
<?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.xhh460.dao.IAccountDao">

<!--定义封装account和user的resultMap-->
<resultMap id="accountUserMap" type="com.xhh460.entity.Account">
<id property="id" column="id"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<!--一对一的关系映射:配置封装User的内容
select属性指定的内容:查询用户的唯一标识
column属性指定的内容:用户根据id查询时,所需要参数的值
-->
<association property="user" column="uid" javaType="com.xhh460.entity.User"
select="com.xhh460.dao.IUserDao.findById"/>
</resultMap>

<!--配置查询所有,把查询的结果集封装到User对象里-->
<select id="findAll" resultMap="accountUserMap">
select * from account
</select>

<!--根据用户id查询账户列表-->
<select id="findAccountByUid" resultType="com.xhh460.entity.Account">
select * from account where uid = #{uid}
</select>
</mapper>

image-20210129154539083

Mybatis缓存

什么是缓存?

​ 存在于内存中的临时数据。像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提 高性能。

什么样的数据能使用缓存,什么样的数据不能使用?

适用于缓存的数据:

​ 经常查询并且不经常改变的。

​ 数据的正确与否最终对结果影响不大的

不适用于缓存的数据:

​ 经常改变的数据。

​ 数据的正确与否对最终结果影响很大的。如:商品的库存,银行的汇率,股市的牌价。

Mybatis中的一级缓存

一级缓存:

​ 指的是Mybatis中SqlSession对象的缓存。当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一块区域中。该区 域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去SqlSession中查看是否有,有的话直接拿出来用。当SqlSession对 象消失的时候,Mybatis的一级缓存也就消失了。

image-20210130122237868

​ 只要清楚了一级缓存之后,对象就不相等了。

image-20210130123753731

Mybatis中的二级缓存

它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

二级缓存使用步骤:

​ 1.在SqlMapConfig.xml中配置

image-20210130125550327

​ 2.让当前的映射文件支持二级缓存(在IUserDao.xml中配置) image-20210130125708084

​ 3.让当前的操作支持二级缓存(在select标签中配置)

image-20210130125727110

原理:

测试文件:

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 com.xhh460.test;

import com.xhh460.dao.IUserDao;
import com.xhh460.entity.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.List;

/**
* @Author coderYang
* @Date 2021/1/30 12:40
*/
public class SecondLevelTest {
private InputStream in;
private SqlSessionFactory factory;

@Before //用于在测试方法执行之前执行
public void init() throws IOException {
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取SqlSessionFactory对象
factory = new SqlSessionFactoryBuilder().build(in);
}

@After //用于在测试方法执行之后执行
public void destroy() throws IOException{
in.close();
}

/**
* 测试查询所有
*/
@Test
public void testFindAll(){
SqlSession sqlSession = factory.openSession();
IUserDao dao = sqlSession.getMapper(IUserDao.class);
User user = dao.findById(6);
System.out.println(user);
sqlSession.close();

SqlSession sqlSession2 = factory.openSession();
IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
User user2 = dao2.findById(6);
System.out.println(user2);
sqlSession2.close();

System.out.println(user.hashCode() == user2.hashCode()); //false
}

}

Myabtis注解开发

CRUD注解开发

java.com.xhh460.dao.IUserDao.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 com.xhh460.dao;

import com.xhh460.entity.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/22 23:44
*/

/**
* 在mybatis中一共有四个注解
* @Select @Insert @Update @Delete
*/
//用户持久层接口
public interface IUserDao {
//查询所有操作
@Select("select * from user")
List<User> findAll();

/**
* 删除用户
* @param userId 用户id
*/
@Delete("delete from user where id=#{id}")
void deleteUser(Integer userId);


/**
* 根据id查询用户
* @param userId 用户id
* @return User
*/
@Select("select * from user where id = #{id}")
User findUserId(Integer userId);


/**
* 根据用户名称模糊查询
* @param username 用户名称
* @return 查询的用户
*/
@Select("select * from user where username like #{username}")
List<User> findUserByName(String username);


/**
* 查询总用户数量
* @return 总用户数量
*/
@Select("select count(*) from user")
int findTotalUser();
}

java.com.xhh460.entity.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
package com.xhh460.entity;

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

/**
* @Author coderYang
* @Date 2021/1/22 23:40
*/
public class User implements Serializable {

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

public Integer getId() {
return id;
}

public String getUsername() {
return username;
}

public Date getBirthday() {
return birthday;
}

public String getSex() {
return sex;
}

public String getAddress() {
return address;
}

public void setId(Integer id) {
this.id = id;
}

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

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

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

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

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

测试文件

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
package com.xhh460.test;

import com.xhh460.dao.IUserDao;
import com.xhh460.entity.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.List;

/**
* @Author coderYang
* @Date 2021/1/30 13:30
*/
public class MybatisAnnoTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;


@Before
public void init() throws IOException {
//1.读取字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据字节输入流构建SqlSessionFactory
factory = new SqlSessionFactoryBuilder().build(in);
//3.根据SqlSessionFactory生产一个SqlSession
session = factory.openSession();
//4.创建dao代理对象
userDao = session.getMapper(IUserDao.class);
}

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

@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user : users){
System.out.println(user);
}
}

/**
* 删除用户
*/
@Test
public void testDelete(){
userDao.deleteUser(6);
}


/**
* 查询用户
*/
@Test
public void testFindOne(){
User user = userDao.findUserId(6);
System.out.println(user);
}

/**
* 根据名称进行模糊查询
*/
@Test
public void findByName(){
List<User> users = userDao.findUserByName("%小%");
for (User user : users){
System.out.println(user);
}
}

/**
* 查询用户总数
*/
@Test
public void findTotal(){
int total = userDao.findTotalUser();
System.out.println(total);
}
}
如果实体类属性和数据库表字段不对应,可以通过注解这样解决

image-20210130152512443

对于以上注解中的value属性,当只有一个属性的时候,我们可以省去value=

一对一查询注解开发

java.com.xhh460.dao.IAccountDao.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
package com.xhh460.dao;

import com.xhh460.entity.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;

/**
* @Author coderYang
* @Date 2021/1/30 15:28
*/
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.xhh460.dao.IUserDao.findByUserId",
fetchType = FetchType.EAGER))
})
List<Account> 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.xhh460.dao;

import com.xhh460.entity.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/22 23:44
*/

/**
* 在mybatis中一共有四个注解
* @Select @Insert @Update @Delete
*/
//用户持久层接口
public interface IUserDao {
//查询所有操作
@Select("select * from user")
@Results(id = "userMap",value = {
@Result(id = true,column = "id",property = "id"),
@Result(column = "username",property = "username"),
@Result(column = "address",property = "address"),
@Result(column = "sex",property = "sex"),
@Result(column = "birthday",property = "birthday"),
@Result(property = "accounts",column = "id",many = @Many(select =
"com.xhh460.dao.IAccountDao.findAccountByUid",fetchType = FetchType.LAZY))
})
List<User> findAll();

/**
* 根据id查询用户
* @param userId 用户id
* @return User
*/
@Select("select * from user where id = #{id}")
// @ResultMap(value = {"userMap"}) //调用改好的对应Map
User findByUserId(Integer userId);


/**
* 根据用户名称模糊查询
* @param username 用户名称
* @return 查询的用户
*/
@Select("select * from user where username like #{username}")
List<User> findUserByName(String username);


}
注解开发使用二级缓存

image-20210130193411271

image-20210130193426006

Author: 小灰灰

Link: http://xhh460.github.io/2021/01/22/Mybatis/

Copyright: All articles in this blog are licensed.

< PreviousPost
SpringMVC
NextPost >
Spring5
CATALOG
  1. 1. Mybatis
    1. 1.0.1. 简介
    2. 1.0.2. 入门
      1. 1.0.2.1. 基于XML的方式开发
      2. 1.0.2.2. 基于注解的方式开发
      3. 1.0.2.3. 传统dao层代码
      4. 1.0.2.4. Mybatis设计模式分析
      5. 1.0.2.5. 自定义Mybatis分析
        1. 1.0.2.5.1. 查询所有的分析
        2. 1.0.2.5.2. 创建代理对象的分析
      6. 1.0.2.6. CRUD操作
        1. 1.0.2.6.1. 传递pojo对象
        2. 1.0.2.6.2. 传递pojo包装对象
        3. 1.0.2.6.3. Mybatis的输出结果封装
        4. 1.0.2.6.4. 查询方法源码追踪
        5. 1.0.2.6.5. 增删改方法源码追踪
        6. 1.0.2.6.6. 代理Dao方法源码追踪
      7. 1.0.2.7. SqlMapConfig.xml配置文件
      8. 1.0.2.8. 连接池
      9. 1.0.2.9. mybatis中的事务
      10. 1.0.2.10. mybaits中基于XML配置的动态SQL语句的使用
      11. 1.0.2.11. mybatis中的多表操作
        1. 1.0.2.11.1. 一对一查询
        2. 1.0.2.11.2. 一对多查询
        3. 1.0.2.11.3. 多对多查询
      12. 1.0.2.12. JNDI
      13. 1.0.2.13. Mybatis延迟加载
        1. 1.0.2.13.1. 一对一方式(一个账户对一个用户)
        2. 1.0.2.13.2. 一对多方式(一个用户有多个账户)
      14. 1.0.2.14. Mybatis缓存
        1. 1.0.2.14.1. Mybatis中的一级缓存
        2. 1.0.2.14.2. Mybatis中的二级缓存
      15. 1.0.2.15. Myabtis注解开发
        1. 1.0.2.15.1. CRUD注解开发
        2. 1.0.2.15.2. 如果实体类属性和数据库表字段不对应,可以通过注解这样解决
        3. 1.0.2.15.3. 一对一查询注解开发
        4. 1.0.2.15.4. 一对多查询注解开发
        5. 1.0.2.15.5. 注解开发使用二级缓存