【Mybatis + Spring】 Mybatis - Spring 结合
环境搭建 EvBuild
软件环境准备
- MySQL 5.0 +
- IDEA 2018 +
- JDK1.8 +
依赖包相关
- Junit单元测试
- JDBC驱动
- Mybatis 组件
- Spring组件
- AopWeaver切面织入包
- Mybatis-Spring【MS专用整合包】
- Lombok
- Log4J 日志输出
官方文档:
http://mybatis.org/spring/zh/index.html
什么是 MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。
它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean 中,
以及将 Mybatis 的异常转换为 Spring 的 DataAccessException。
最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
使用须知:
使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。
这很重要——因为本手册中不会提供二者的基本内容,安装和配置教程。
MyBatis-Spring 需要以下版本:
MyBatis-Spring | MyBatis | Spring 框架 | Spring Batch | Java |
---|---|---|---|---|
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
工程创建
在前面的Spring工程基础上创建新模块
开始配置Maven依赖
<dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.6.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.6.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.4</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
资源过滤问题
<!--在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
回顾原生Mybatis开发步骤:
1、编写实体类
2、Mybatis-Config.xml核心配置文件
3、Dao接口,或者说Mapper接口
4、编写Mapper.xml映射器配置、注册映射器
5、测试
实体类POJO
package cn.dai.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer user_id;
private String user_name;
private String user_password;
}
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> <settings> <setting name="logImpl" value="LOG4J"/> </settings> <typeAliases> <package name="cn.dai.pojo"/> </typeAliases> <environments default="dev1"> <environment id="dev1"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///mybatis?serverTimezone=GMT"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mappers/UserMapper.xml"/> </mappers> </configuration>
Mapper.xml
很多人不喜欢映射器文件和接口放在一起,并且Maven也确实存在资源过滤问题
这样,我们可以放在资源目录下,新建一个mappers目录,统一放在这里面
编写映射器UserMapper.xml
<?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="cn.dai.mapper.UserMapper"> <select id="getAllUserList" resultType="user"> SELECT * FROM user; </select> </mapper>
在核心配置中这样注册映射器
<mappers> <mapper resource="mappers/UserMapper.xml"/> </mappers>
MybatisUtil.java
我们自己配置的会话获取工具类
public class MybatisUtil { private static SqlSessionFactory sqlSessionFactory; static { String mybatis_config = "Mybatis-Config.xml"; try { InputStream inputStream = Resources.getResourceAsStream(mybatis_config); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); sqlSessionFactoryBuilder = null; } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession(boolean closeTransaction) { return sqlSessionFactory.openSession(closeTransaction); } }
配置LOG4J日志输出【可选】
如果不要配置,把Mybatis-Config.xml的Settings删除
编写log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/l4j.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
在测试类加入日志实例
org.apache.log4j.Logger; private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(MybatisTest.class);
测试Mybatis环境搭建是否正常
public class MybatisTest { private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(MybatisTest.class); @Test public void sqlTest(){ SqlSession sqlSession = MybatisUtil.getSqlSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> allUserList = mapper.getAllUserList(); for (User user:allUserList){ logger.info(user); } sqlSession.close(); } }
开始整合 Start Integration
现在我们把对象交给Spring管理,
再也不需要我们自己编写的MybatisUtil会话获取工具类了
编写Spring的Bean容器配置文件
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd " > <context:annotation-config /> <context:component-scan base-package="cn.dai" /> <aop:aspectj-autoproxy proxy-target-class="false"/> </beans>
快速上手
要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:
一个 SqlSessionFactory 和至少一个数据映射器类。
在 MyBatis-Spring 中,可使用 SqlSessionFactoryBean来创建 SqlSessionFactory。
要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> </bean>
或者使用纯JavaConfig的注解方式
@Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource()); return factoryBean.getObject(); }
注意:SqlSessionFactory 需要一个 DataSource(数据源)。
这可以是任意的 DataSource,只需要和配置其它 Spring 数据库连接一样配置它就可以了。
【在狂神的是XML配置,上面的会话工厂Bean需要引用一个数据源实例,它采用的是Spring提供的数据源】
<!-- 我们所学过的连接池【数据源】有C3P0、DRUID、HIKARI、DBCP、 --> <!-- 这里使用Spring提供的连接池--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" /> <property name="url" value="jdbc:mysql:///mybatis?serverTimezone=GMT" /> <property name="username" value="root" /> <property name="password" value="123456" /> </bean>
可以看看这个数据源类的源码是怎样的?
源码:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.jdbc.datasource; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; public class DriverManagerDataSource extends AbstractDriverBasedDataSource { public DriverManagerDataSource() { } public DriverManagerDataSource(String url) { this.setUrl(url); } public DriverManagerDataSource(String url, String username, String password) { this.setUrl(url); this.setUsername(username); this.setPassword(password); } public DriverManagerDataSource(String url, Properties conProps) { this.setUrl(url); this.setConnectionProperties(conProps); } public void setDriverClassName(String driverClassName) { Assert.hasText(driverClassName, "Property 'driverClassName' must not be empty"); String driverClassNameToUse = driverClassName.trim(); try { Class.forName(driverClassNameToUse, true, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException var4) { throw new IllegalStateException("Could not load JDBC driver class [" + driverClassNameToUse + "]", var4); } if (this.logger.isDebugEnabled()) { this.logger.debug("Loaded JDBC driver: " + driverClassNameToUse); } } protected Connection getConnectionFromDriver(Properties props) throws SQLException { String url = this.getUrl(); Assert.state(url != null, "'url' not set"); if (this.logger.isDebugEnabled()) { this.logger.debug("Creating new JDBC DriverManager Connection to [" + url + "]"); } return this.getConnectionFromDriverManager(url, props); } protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException { return DriverManager.getConnection(url, props); } }
所以,在Mybatis-Config.xml的环境配置就可以舍弃了
Mybatis-Config.xml可以删除的部分
<environments default="dev1"> <environment id="dev1"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///mybatis?serverTimezone=GMT"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments>
连接参数的读取就可以让Spring的ApplicationContext读取了
接着,Mybatis-Config.xml这个核心配置文件又
可以让会话工厂实例的configuration属性读取
<!-- SQL会话工厂 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configuration" value="classpath:Mybatis-Config.xml" /> </bean>
其实这个会话工厂实例Bean的设置完全涵盖了核心配置
【狂神推荐 别名 & 设置 留给 Mybatis-Config.xml 核心配置,其他部分完全Spring容器接管】
注意映射器的配置只能保存一个,在Spring写了就不能再在Mybatis-Config.xml保留
<!-- SQL会话工厂 --> <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:mappers/UserMapper.xml"/> </bean>
然后是SqlSession的Bean注册
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <!-- 只允许构造器注入 没有setSqlSession方法 --> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean>
但是在Mapper包,我们就需要多写一个实现类
public class UserMapperImpl implements UserMapper{ // 现在使用SqlSessionTemplate完成 SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } public List<User> getAllUserList() { return sqlSession.getMapper(UserMapper.class).getAllUserList(); } }
Bean注册
<bean id="userMapperImpl" class="cn.dai.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSessionTemplate" /> </bean>
测试
import cn.dai.mapper.UserMapper; import cn.dai.pojo.User; import org.apache.log4j.Logger; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.List; public class MybatisSpringIntegration { private static Logger logger = Logger.getLogger(MybatisSpringIntegration.class); @Test public void msTest(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Canned.xml"); UserMapper userMapper = applicationContext.getBean("userMapperImpl", UserMapper.class); List<User> allUserList = userMapper.getAllUserList(); for (User user:allUserList){ logger.info(user); } } }
整合总结:
1、数据库数据准备
2、实体类编写,暂不需要Bean注册
3、编写Mybatis-Config.xml配置,也可以让Spring全权Bean化处理
4、编写Mapper接口,SQL的抽象方法
5、编写Mapper.xml映射器配置,可以放在接口包,也可以是资源目录
6、Mapper的注册交给Mybatis-Config.xml或者是Bean处理
7、LOG4J日志输出配置【可选】
8、配置Spring容器类
9、配置数据源【DataSource 连接池】Bean化
10、配置SQL会话工厂【SqlSessionFactory】Bean化
11、SQL会话工厂可以完全配置Mybatis-Config.xml【可选,自己看情况设置】
12、配置SQL会话模板【SqlSessionTemplate】Bean化
13、编写XXXmapper实现类,同时配置Bean
14、测试,或者实际使用,从ApplicationContext获取即可
第二种整合方式:
只是在上面的基础上有一点改动
官方在我们编写实现类要组合SqlSessionTemplate的这个步骤上封装了
我们只需要继承SqlSessionDaoSupport类即可
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper { public List<User> getAllUserList() { return this.getSqlSession().getMapper(UserMapper.class).getAllUserList(); } }
在Bean的配置上,只需要注入SQL会话工厂即可
<bean id="userMapperImpl2" class="cn.dai.mapper.UserMapperImpl2"> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean>
测试
@Test public void msTest2(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Canned.xml"); UserMapper userMapper = applicationContext.getBean("userMapperImpl2", UserMapper.class); List<User> allUserList = userMapper.getAllUserList(); for (User user:allUserList){ logger.info(user); } }
关于Spring的声明式事务
约束声明
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
事务配置
<!--https://blog.csdn.net/jiadajing267/article/details/81056057--> <!-- 配置声明式事务 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 2. 配置事务属性 --> <!--<tx:advice>元素声明事务通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 根据方法名指定事务的属性 --> <tx:method name="*"/> <!--propagation配置事务传播行为--> <tx:method name="purchase" propagation="REQUIRES_NEW"/> <!--isolation配置事务的隔离级别--> <tx:method name="update*" isolation="SERIALIZABLE"/> <!--rollback-for配置事务遇到异常必须回滚,no-rollback-for配置事务遇到异常必须不能回滚--> <tx:method name="add*" rollback-for="java.io.IOException" no-rollback-for="com.dmsd.spring.tx.BookStockException"/> <!--read-only配置事务只读属性--> <tx:method name="find*" read-only="true"/> <!--timeout配置事务的超时属性--> <tx:method name="get*" timeout="3"/> </tx:attributes> </tx:advice> <!-- 3. 配置事务切入点, 以及把事务切入点和事务属性关联起来 --> <aop:config> <aop:pointcut expression="execution(* cn.dai.mapper.UserMapper.*.*(..))" id="txPointCut"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>