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 能管理它。
使用 SqlSessionTemplate 的步驟
- 配置數據源 dataSource,可以是 dbcp、c3p0、spring-jdbc 等
- 用數據源 dataSource 創建 SqlSessionFactory,並綁定 MyBatis 配置文件
- 設置 SqlSessionFactory 的屬性(可選),也就是 MyBatis 中的配置
- 創建 SqlSession( SqlSessionTemplate 類型),通過構造器注入 SqlSessionFactory 依賴
- 實現類中加入 SqlSession 屬性,方法直接用它(配置 bean 的時候注入 SqlSession 依賴)
如果使用的是 SqlSessionDaoSupport
- 實現類繼承 SqlSessionDaoSupport 類,給實現類注入 SqlSessionFactory 依賴
- 實現類中,方法使用 getSqlSession 獲取 SqlSession
內容好多,越寫越亂!希望后面看得懂😵...
