Spring整合MyBatis
了解完 Spring 的基本使用后,就可以将 Spring 和 MyBatis 结合起来使用一下了。这里创建 Spring-10-MyBatis 项目练习一下用 Spring 整合 MyBatis。
1. 回顾MyBatis
距离学习 MyBatis 已经有一段时间了,都快忘了怎么用了。
先尝试单独搭建一个 MyBatis 项目,有以下几步
-
在 Maven 中导入 MyBatis 需要的依赖 <dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
</dependencies>
这些是 MyBatis 需要的依赖,还没有涉及到 Spring。 -
创建配置文件 mybatis-config.xml <?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="0723"/>
</dataSource>
</environment>
</environments>
</configuration>
这里就不用 db.properties 文件获取属性了,后面这个工作要交给 Spring 了。 -
创建 MyBatis 工具类,直接从之前的 MyBatis 笔记中偷过来😋 public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
}
基本的统一配置就完成了,下面就是对应数据库编写实体类和对应的 Mapper 了。 写到测试方法的时候错回来了:这是什么几把东西啊? String resource = "org/mybatis/example/mybatis-config.xml";
捏麻麻的,抄!抄出问题来了。配置文件放在 resources 文件夹中,直接 ↓ 就行了! String resource = "mybatis-config.xml";
-
编写实体类 User,属性对应数据库中的字段,以免多生事端
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private int id;
private String name;
private String pwd;
}
顺便把 Lombok 也用上了,这玩意是真滴好用😋 -
写完实体类,就要写对应的 Dao 层接口,即 UserMapper package com.qiyuan.dao;
...
public interface UserMapper {
List<User> getUserList();
}
注意放在 dao 包下哦,差点就和 User 类放一起了。 -
有了接口,就要有其对应的实现,即 UserMapper.xml,这里和 User 类放在同一包下(要记得让 Maven 能导出嗷) <?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.qiyuan.dao.UserMapper">
<select id="getUserList" resultType="User">
select * from user
</select>
</mapper>
这里用到了别名,要在 mybatis-config.xml 中配置哦 <typeAliases>
<package name="com.qiyuan.entity"/>
</typeAliases>
直接包扫描,反正现在也不用 log4j 日志😕 -
然后不要忘记,在 mybatis-config.xml 中注册映射 mapper,特意写成一个步骤! <mappers>
<mapper class="com.qiyuan.dao.UserMapper"/>
</mappers>
直接用 class 方式注册绑定,比较简洁,不过要求接口和其对应的 XML 名字相同,且在同一个包下。 -
又来了!**配置 Maven 以让 java 文件夹中的 xml 文件能成功导出!**在 pom.xml 中添加 <build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
resources 目录下的本来就能导出,加了反而报错。 -
到现在才是真搞完了,执行测试方法 public class MyTest {
@Test
public void getUserListTest(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
这样就成功了。写到了遇到了两个 Bug:一是 MyBatis 工具类中的资源文件路径写错了,见3;二就是 Maven 的配置导出问题了,见8。
最基础的 MyBatis 应用就完成了,接下来引入 Spring。
2. MyBatis-Spring
通过 Spring 使用 MyBatis 有两种方式:使用 SqlSessionTemplate 和使用 SqlSessionDaoSupport。
2.1 导入依赖
要将 MyBatis 和 Spirng 结合起来,除了导入上面 MyBatis 的依赖,当然还要有 Spring 的依赖
<dependencies>
...
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
</dependencies>
注意这里相比之前的 Spring 项目,多了 spring-jdbc 和 mybatis-spring 的包,前者用于 Spring 管理数据库,后者作用就是是将 MyBatis 和 Spring 结合起来。
什么是 MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException 。 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
2.2 使用SqlSessionTemplate
SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession 。 SqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。
通过 Spring 去使用 MyBatis 的步骤为
-
创建 spring-dao.xml 配置文件(也可以是其他名字啦),管理数据库的配置,也相当于 MyBatisUtil 工具类 配置数据源 dataSource
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="0723"/>
</bean>
在 Spring 中配置了数据源,mybatis-config 中的就可以删掉了
<environments default="...">
</environments>
-
创建 sqlSessionFactory 的 bean,同时设置数据源 dataSource 属性为上面配置的数据源,设置 configLocation 属性以绑定 MyBatis 配置文件
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
这里相当于 MyBatisUtil 工具类中的获取 SqlSessionFactory 实例! public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
...
}
-
在 sqlSessionFactory 的 bean 中也可以配置其属性,和在 mybatis-config 中配置是一样的!如注册 Mapper
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
...
<property name="mapperLocations" value="classpath:com/qiyuan/dao/*.xml"/>
</bean>
这里就用到之前 MyBatis 中不能用的通配符了,因为通配符是由 Spring 提供的! 在 bean 中配置了,mybatis-config 中配置的 mapper 也可以删掉了
<mappers>
</mappers>
这样一来,mybatis-config.xml 中几乎已经没有内容了(还剩一个别名 typeAlias ),虽然别名也能在 bean 中配置,不过最好将别名 typeAlias 和设置 settings 放在 mybatis-config.xml 中,方便查看和修改(给 MyBatis 留点面子)。
<?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.qiyuan.entity"/>
</typeAliases>
</configuration>
-
有了配置好的 sqlSessionFactory 后,就可以用它获取 sqlSession了。 创建 sqlSession 的 bean,注入 sqlSessionFactory 依赖
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
SqlSessionTemplate 类只能通过构造器注入 sqlSessionFactory 依赖,这就是一个 SqlSession 了。 -
重点来了!由于面向对象的思想,要把对象交给 Spring 管理,而之前使用 MyBatis 时,Mapper.xml 充当了接口的实现类,这个实现类无法让 Spring 管理,所以要写一个真正的接口实现类,封装 Mapper.xml,交给 Spring 管理! 创建 UserMapperImpl 类,实现了 UserMapper 接口,即有对数据库操作的方法 public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getUserList() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
return userList;
}
}
其中,有一个属性 sqlSession(变成了 SqlSessionTemplate 也是一样用法)及其对应的 set 方法,通过 Spring 依赖注入后就能使用,相当于之前的 SqlSession sqlSession = MyBatisUtil.getSqlSession();
在这个“真”实现类( UserMapperImpl 类)中调用了“假”实现类( UserMapper.xml )的方法,相当于多了一层封装,也变成了一个真实存在的类,方便 Spring 管理! -
把真实现类交给 Spring 管理,同时进行依赖注入 <bean id="userMapper" class="com.qiyuan.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
这时,获取 userMapper 对象后执行其中的方法,就会到 UserMapper.xml 执行对应的语句,和之前区别不大,只是多了一层封装以方便管理! -
现在就可以用起来试一试了,测试方法 public class MyTest {
@Test
public void MyBatisSpringTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
}
执行成功!相比之前,更加简洁明了了。获取 context 容器,获取容器的的对象,调用对象的方法,一气呵成!
优化:可以注意到,大部分配置都在 spring-dao.xml 文件中,这个文件做了几件事
- 配置数据源,即连接数据库的一些配置( mybatis-config.xml 中的 environment 部分)
- 创建 sqlSessionFactory 的 bean,进行依赖注入( mybatis-config.xml 中的 mapper 部分,MyBatisUtil 的创建 sqlSessionFactory 部分)
- 创建 sqlSession 的 bean,将 sqlSessionFactory 注入进去( MyBatisUtil 中的 sqlSessionFactory.openSession )
- 创建真实现类 UserMapperImpl 的 bean,为其注入 sqlSession
其中,第1、2、3步都是配置和工具类干的事情,属于改动比较少的部分;而第4步属于会经常会进行的步骤,如增加 StudentMapperImpl、TeacherMapperImpl 等的 bean。
所以将第4步这种配置抽出来,留下1、2、3步,使得 spring-dao.xml 变成了一个比较纯洁的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="0723"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/qiyuan/dao/*.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
至于第4步这种操作,创建了一个要具体用到的对象,还是放到 applicationContext.xml 中进行管理吧!
创建 applicationContext.xml,通过 import 标签引入 spring-dao.xml,把真正要用到的对象,即 userMapper 交给它管理
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.qiyuan.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
这样几个配置文件的作用都很明确了,mybatis-config 负责 MyBatis 的一些配置(别名、设置),spring-dao 管理了 MyBatis 连接数据库、创建 SqlSession和注册 mapper 的配置,applicationContext 整合了 Spring 的配置(现在是 spring-dao,后面还会有 spring-mvc 等等)和管理要用到对象。
记得加载配置文件的时候加载 applicationContext.xml 哦!
2.3 使用SqlSessionDaoSupport
使用 SqlSessionDaoSupport 与使用 SqlSessionTemplate 大同小异,只不过更简化了一点。
上面说到,UserMapperImpl 类中有一个 SqlSessionTemplate 类型的 sqlSession 属性,实现的方法中封装了使用 SqlSession 对数据库的操作,调用它的方法就相当于在使用 SqlSession。
这种方式在使用前需要注入 sqlSession 属性,而 SqlSession 又由 SqlSessionFactory 创建。也就是说,使用这种方式需要 SqlSessionFactory 和 SqlSession 的 bean。
**先说结论,使用 SqlSessionDaoSupport 省略了创建 SqlSession 的 bean 的步骤。**创建 UserMapperImpl2 实现类,继承 SqlSessionDaoSupport 类,实现 UserMapper 接口
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
public List<User> getUserList() {
return null;
}
}
重点来了!**SqlSessionDaoSupport 类中有 getSqlSession 方法,可以直接获得一个 sqlSession!**用这个 sqlSession 去执行数据库操作就行了!
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
public List<User> getUserList() {
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
return userList;
}
}
能用是能用,但必须思考为什么能用。**为什么 SqlSessionDaoSupport 类可以通过 getSqlSession 方法返回一个 SqlSession?**我们知道,SqlSession 是由 SqlSessionFactory 创建的,所以,点进去看看
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
}
}
原来如此!SqlSessionDaoSupport 类中就需要注入一个 sqlSessionFactory,以获取其中的 sqlSessionTemplate 对象,返回的就是这个对象!
所以在注册 UserMapperImpl2 的实现类的时候,要注入 sqlSessionFactory 依赖
<bean id="userMapper2" class="com.qiyuan.dao.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
它会通过注入的 sqlSessionFactory,获取 sqlSessionTemplate,也就是之前的方式获取到的 SqlSession 了(回见 2.2 / 4. )。
执行测试方法,获取的是 userMapper2 对象,执行结果相同!
public class MyTest {
@Test
public void MyBatisSpringTest2(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
}
这样就完成了,相比直接使用 SqlSessionTemplate 的方式,SqlSessionDaoSupport 将其封装了起来,从 SqlSessionDaoSupport 中就可以获取到 SqlSession,不用配置 sqlSession 的 bean 和注入了。
3. 总结
使用 MyBatis-Spring 有两种方式
- 使用SqlSessionTemplate:就是直接使用 SqlSession,需要将 SqlSession 注入到实现类中进行使用。
- 使用SqlSessionDaoSupport:实现类继承 SqlSessionDaoSupport 类,把工厂交给它(通过注入),就能从它获取 SqlSession。
使用 MyBatis-Spring 需要创建与 Mapper.xml 对应的实现类,在实现类中调用 Mapper.xml 实现数据库操作。实例化 Mapper.xml 为一个实现类的目的是使 Spring 能管理它。
内容好多,越写越乱!希望后面看得懂😵…
|