整合Spring和Mybatis

在实际的开发过程中,我们经常需要使用Spring 和Mybatis,那么如何去整合Spring 与Mybatis 呢?本文我们一起来通过详细的案例的方式进行讲解

准备工作

首先,还是介绍下环境

  • idea
  • mysql
  • maven

我们先使用idea 创建一个maven工程,工程名称叫com.ssm,然后在pom.xml中加入以下jar包,可以看到所需要的jar包还是很多的

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
163
164
165
166
167
168
169
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>5.1</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.22</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>

然后我们编写配置文件,在mian-java 这个目录上点击右键创建一个文件夹,然后选择resource,然后在resource 下创建db.properties 文件,内容如下

1
2
3
4
5
6
7
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.10.128:3306/mybatis_db
jdbc.username=root
jdbc.password=123456
jdbc.maxTotal=30
jdbc.maxIdle=10
jdbc.initialSize=5

其对应的数据库创建方式以及数据插入如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> create database mybatis_db;
Query OK, 1 row affected (0.01 sec)

mysql> use mybatis_db;
Database changed
mysql> create table t_user(
-> id int(32) primary key auto_increment,
-> username varchar(50),
-> jobs varchar(50),
-> phone varchar(16));
Query OK, 0 rows affected, 1 warning (0.01 sec)

mysql> insert into t_user values(1,"zhangsan","teacher","13142767333");
Query OK, 1 row affected (0.01 sec)

mysql> insert into t_user values(2,"lisi","engineer","13142767334");
Query OK, 1 row affected (0.01 sec)

mysql> insert into t_user values(3,"wangwu","pilot","12342767334");
Query OK, 1 row affected (0.00 sec)

mysql>

然后还是在resource 目录下创建一个applicationContext.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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!--读取db.properties-->
<context:property-placeholder location="classpath:db.properties"/>
<!--配置数据源 -->
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource">
<!--数据库驱动 -->
<property name="driverClassName" value="${jdbc.driver}" />
<!--连接数据库的ur1 -->
<property name="url" value="${jdbc.url}" />
<!--连接数据库的用户名 -->
<property name="username" value="${jdbc.username}" />
<!--连接数据库的密码-->
<property name="password" value="${jdbc.password}" />
<!--最大连接数-->
<property name="maxTotal" value="${jdbc.maxTotal}" />
<!--最大空闲连接-->
<property name="maxIdle" value="${jdbc.maxIdle}" />
<!--初始化连接数-->
<property name="initialSize" value="${jdbc.initialSize}" />
</bean>
<!--事务管理器,依赖于数据源 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--注册事务管理器驱动,开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--配置MyBatis工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入数据源 -->
<property name="dataSource" ref="dataSource" />
<!--指定核心配置文件位置 -->
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>
</beans>

这个配置文件中主要是定义了数据库的驱动以及连接池、事务管理器等,在最后配置了Mybatis 的工厂,制定其核心配置文件为mybatis-config.xml,并且使用typeAliases 创建一个别名,关于别名,你可以参考官网文档 https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置别名 -->
<typeAliases>
<package name="com.ssm"/>
</typeAliases>
</configuration>

然后我们开始整合,我们先看下使用传统的DAO 方式如何整合,采用传统DAO 方式整合,我们需要编写DAO 接口以及实现类,并且需要向DAO实现类注入SqlSessionFactory,然后通过方法体通过SqlSessionFactory 创建SqlSession。具体怎么做呢?

首先,我们创建一个User.java 的用户类,并生成get和set方法以及tostring方法

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.ssm;

public class User {
private Integer id;
private String username;
private String jobs;
private String phone;

public Integer getId() {
return id;
}

public String getUsername() {
return username;
}

public String getJobs() {
return jobs;
}

public String getPhone() {
return phone;
}

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

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

public void setJobs(String jobs) {
this.jobs = jobs;
}

public void setPhone(String phone) {
this.phone = phone;
}

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


}

然后我们创建一个UserMappler.xml,编写一个根据用户id查询用户的select 语句

1
2
3
4
5
6
7
8
9
<?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="UserMapper">
<!--根据用户编号获取用户信息 -->
<select id="findUserById" parameterType="Integer" resultType="User">
select * from t_user where id=#{id}
</select>
</mapper>

接着我们在Mybatis-config.xml中创建一个映射文件

1
2
3
<mappers>
<mapper resource="UserMapper.xml" />
</mappers>

接着我们创建一个UserDao.java,我们创建一个findUserById 的接口

1
2
3
4
5
package com.ssm;

public interface UserDao {
public User findUserById(Integer id);
}

我们需要在applicationContext.xml 中定义一个bean,其id 为userDao,并指向clas路径,同时定义一个property,引用sqlSessionFactory,
其表示将sqlSessionFactory对象注入该bean 的实例化对象中

1
2
3
<bean id="userDao" class="com.ssm.UserDaoImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>

然后我们需要实现UserDao,我们创建一个UserDaoImpl.java文件,内如下

1
2
3
4
5
6
7
8
9
10
11
12
package com.ssm;

import com.ssm.User;
import com.ssm.UserDao;
import org.mybatis.spring.support.SqlSessionDaoSupport;

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
@Override
public User findUserByID(Integer id) {
return this.getSqlSession().selectOne("findUserById",id);
}
}

接着,我们创建一个测试类

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

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DaoTest {
@Test
public void findUserByIdTest(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
User user = userDao.findUserByID(1);
System.out.println(user);
}
}

执行结果如下

20200412120832

以上是使用传统Dao方式来进行整合,但是我们会发现采用这种方法实现整合会出现大量冗余代码,为此,我们可以使用Mybatis 提供的Mapper 接口进行实现

MapperFactoryBean 是Mybatis-Spring 提供的一个用于根据Mapper 接口生成Mapper 对象的类,这个类在Spring 配置时候可以配置一下参数

  • mapperInterface 用于指定接口
  • SqlSessionFactory 用户指定 SqlSessionFactory
  • SqlSessionTemplate 用户指定 SqlSessionTemplate,如果与SqlSessionFactory同时设定,则只会启用SqlSessionTemplate

了解了基本的配置后,我们就开始使用Mapper 接口的方式进行整合了

首先,我们创建一个UserMapper.java,内容如下

1
2
3
4
5
package com.ssm;

public interface UserMapper {
public User findUserById(Integer id);
}

同时,创建一个UserMapper.xml

1
2
3
4
5
6
7
8
9
<?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.ssm.UserMapper">
<!--根据用户编号获取用户信息 -->
<select id="findUserById" parameterType="Integer" resultType="User">
select * from t_user where id=#{id}
</select>
</mapper>

仔细看你会发现和传统Dao 方式中我们定义接口其实一模一样,不过,接下来可就不一样了,我们需要在spring的配置文件中创建一个id为userMapper 的bean,代码如下

1
2
3
4
5
<!--     Mapper代理开发(基于MapperFactoryBean)-->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.ssm.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

这段配置是用来定义Mapper代理开发,也就是基于MapperFactoryBean的配置,然后我们在测试类中进行测试,你会发现我们并没有写UserMapper 的实现类UserMapperImpl,这是与传统DAO方式的区别

1
2
3
4
5
6
7
8
@Test
public void findUserByIdMapperTest(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = (UserMapper) applicationContext.getBean("userMapper");
User user = userMapper.findUserById(1);
System.out.println(user);

}

执行代码

20200412122058

接下里我们看下给予MapperScannerConfigUre 的整合,在实际的开发过程中,Dao 层会包含很多的接口,如果我们每一个接口都要在Spring中配置对应的Bean,那么这个工作量是相当大的,因此mybatis-spring提供了一种自动扫描的兴衰来配置mybatis中的映射器,即可采用MapperScannerConfigure 类
只需要在spring配置文件中定义如下配置即可,我们将spring配置文件中的传统Dao 和基于MapperFactoryBean 的bean 注释掉,增加如下配置

1
2
3
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ssm" />
</bean>

其中

  • basePackage 是指定映射接口文件所在的包路径,如果你需要扫描多个包,需要加分号或逗号作为分隔符,它会扫描包内的所有文件

然后我们再次执行对应的测试类,发现一样的效果。你会发现通过这样整合,这种是不是特别简单呢?