Spring整合MyBatis
了解完 Spring 的基本使用后,就可以将 Spring 和 MyBatis 结合起来使用一下了。这里创建 Spring-10-MyBatis 项目练习一下用 Spring 整合 MyBatis。
1. 回顾MyBatis
距离学习 MyBatis 已经有一段时间了,都快忘了怎么用了。
先尝试单独搭建一个 MyBatis 项目,有以下几步
-
在 Maven 中导入 MyBatis 需要的依赖
<dependencies> <!--Mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency> <!--MyBatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!--junit--> <!--Junit单元测试--> <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 { // 使用MyBatis第一步:获取SqlSessionFactory对象 String resource = "org/mybatis/example/mybatis-config.xml"; // 要导org.apache.ibatis.io.Resources的包! Maven犯病严重 InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } // 从SqlSessionFactory中获取SqlSession public static SqlSession getSqlSession(){ // sqlSession 其实类似于 connection 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"> <!--命名空间namespace要绑定一个对应的Dao/Mapper接口,相当于实现它--> <mapper namespace="com.qiyuan.dao.UserMapper"> <!--select查询语句,使用别名记得配置 typeAlias --> <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> <!--要求接口和其对应的 XML 名字相同,且在同一个包下--> <mapper class="com.qiyuan.dao.UserMapper"/> </mappers>
直接用 class 方式注册绑定,比较简洁,不过要求接口和其对应的 XML 名字相同,且在同一个包下。
-
又来了!配置 Maven 以让 java 文件夹中的 xml 文件能成功导出!在 pom.xml 中添加
<build> <resources> <!--让java目录下的properties和xml文件也能被导出--> <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>
<!--上面 MyBtais 的包-->
...
<!-- Spring 框架 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- AOP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<!-- Spring 管理 JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- mybatis-spring -->
<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
<!-- 用 Spring 的数据源 替换 MyBatis 的数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!--就是 mybatis-config 中 数据源 dataSource 的属性!--> <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 中的就可以删掉了
<!-- mybatis-config.xml --> <environments default="..."> <!--用不到了,删了吧!--> </environments>
-
创建 sqlSessionFactory 的 bean,同时设置数据源 dataSource 属性为上面配置的数据源,设置 configLocation 属性以绑定 MyBatis 配置文件
<!-- sqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!--绑定 MyBatis 配置文件!--> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean>
这里相当于 MyBatisUtil 工具类中的获取 SqlSessionFactory 实例!
public class MyBatisUtil { private static SqlSessionFactory sqlSessionFactory; static { try { // 使用MyBatis第一步:获取SqlSessionFactory对象 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
<!-- sqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> ... <!--如,在这里注册 Mapper --> <property name="mapperLocations" value="classpath:com/qiyuan/dao/*.xml"/> </bean>
这里就用到之前 MyBatis 中不能用的通配符了,因为通配符是由 Spring 提供的!
在 bean 中配置了,mybatis-config 中配置的 mapper 也可以删掉了
<!-- mybatis-config.xml --> <mappers> <!--不用了!--> </mappers>
这样一来,mybatis-config.xml 中几乎已经没有内容了(还剩一个别名 typeAlias ),虽然别名也能在 bean 中配置,不过最好将别名 typeAlias 和设置 settings 放在 mybatis-config.xml 中,方便查看和修改(给 MyBatis 留点面子)。
<!--仅存的 mybatis-conifg 内容--> <?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 依赖
<!-- SqlSessionTemplate 就是 SqlSession!--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--只能用构造器注入,因为它没有 set 方法!--> <!-- 把工厂给它,就能从中 get 到 SqlSession 了--> <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{ // 原来的操作,使用 SqlSession 实现,现在使用 SqlSessionTemplate // 不过还是叫做 sqlSession 亲切! private SqlSessionTemplate sqlSession; // 添加 set 方法,以注入属性 public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } // 在这里进行封装! public List<User> getUserList() { // IoC 的思想!不用去 new 一个 sqlSession 了,注入后就能用! UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); return userList; } }
其中,有一个属性 sqlSession(变成了 SqlSessionTemplate 也是一样用法)及其对应的 set 方法,通过 Spring 依赖注入后就能使用,相当于之前的
SqlSession sqlSession = MyBatisUtil.getSqlSession(); // 关闭应该是由 Spring 管理的吧...
在这个“真”实现类( UserMapperImpl 类)中调用了“假”实现类( UserMapper.xml )的方法,相当于多了一层封装,也变成了一个真实存在的类,方便 Spring 管理!
-
把真实现类交给 Spring 管理,同时进行依赖注入
<bean id="userMapper" class="com.qiyuan.dao.UserMapperImpl"> <!--注入 sqlSession!--> <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); } } } // 执行结果 /* User(id=1, name=祈鸢, pwd=123456) User(id=2, name=qiyuanc2, pwd=0723) User(id=3, name=風栖祈鸢, pwd=07230723) User(id=5, name=祈鸢bbb, pwd=123123) */
执行成功!相比之前,更加简洁明了了。获取 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">
<!-- 用 Spring 的数据源 替换 MyBatis 的数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--就是 mybatis-config 中 数据源 dataSource 的属性!-->
<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>
<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--绑定 MyBatis 配置文件!-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--如,在这里注册 Mapper -->
<property name="mapperLocations" value="classpath:com/qiyuan/dao/*.xml"/>
</bean>
<!-- SqlSessionTemplate 就是 SqlSession!-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能用构造器注入,因为它没有 set 方法!-->
<!-- 把工厂给它,就能从中 get 到 SqlSession 了-->
<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 标签引入 spring-dao.xml -->
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.qiyuan.dao.UserMapperImpl">
<!--注入 sqlSession!-->
<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) {
// 从工厂获取 sqlSessionTemplate
}
}
原来如此!SqlSessionDaoSupport 类中就需要注入一个 sqlSessionFactory,以获取其中的 sqlSessionTemplate 对象,返回的就是这个对象!
所以在注册 UserMapperImpl2 的实现类的时候,要注入 sqlSessionFactory 依赖
<bean id="userMapper2" class="com.qiyuan.dao.UserMapperImpl2">
<!--注入 sqlSessionFactory!-->
<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 能管理它。
内容好多,越写越乱!希望后面看得懂??...