# Spring集成MyBatis 使用Spring维持IOC(控制反转)的思想,利用DI(依赖注入)来给MyBatis注入所需要的组件,比如:DataSource(数据源)、SqlSessionFactory、SqlSession、事务管理对象等 ## 一、导入依赖框架 修改项目的`pom.xml`仓库配置文件 ```xml 4.0.0 cn.doooog springWithMyBatis 1.0-SNAPSHOT 7.0.0-M9 22 22 UTF-8 org.springframework spring-context ${spring.version} org.springframework spring-jdbc ${spring.version} org.aspectj aspectjweaver 1.9.24 org.mybatis mybatis 3.5.19 org.mybatis mybatis-spring 3.0.5 com.mysql mysql-connector-j 9.4.0 ``` ## 二、配置数据源 #### 1、创建Spring配置文件 在`rescources`资源目录下新建目录`cn/doooog/swm`,创建`SpringConfig.xml` ```xml ``` #### 2、创建数据库连接配置 在`rescources`资源目录下创建`db.properties` ```properties jdbc.mysql.driver=com.mysql.cj.jdbc.Driver jdbc.mysql.url=jdbc:mysql://localhost:3306/databaseName?characterEncoding=utf-8 jdbc.mysql.username=username jdbc.mysql.password=password ``` #### 3、配置数据源 ##### 引入DB配置文件 ```xml ``` ##### 配置数据源dataSource ```xml ``` **注意**:因为数据源也是一个对象,所以要用`bean`,其中数据源的类文件在`spring-jdbc`依赖中已经提供。 ##### 配置`SqlSessionFactory` 创建SqlSessionFactory,并且扫描实体包创建别名 ```xml ``` **注意**:可以看到``标签传值的属性一共有两种,其中`value`传的是一个确切的值,`ref`传的是一个对象。 ##### 配置Mapper扫描器 `MapperScanner`自动扫描`mapper`接口,为`mapper`接口注入`SqlSessionFactory` ```xml ``` **注意**:这里使用`sqlSessionFactoryBeanName`而不是`sqlSessionFactory`是因为用`sqlSessionFactory`直接会出现某些问题,已被弃用。 完整的`SpringConfig.xml` ```xml ``` ## 三、创建数据库表实体对象 在项目目录下新建`cn.doooog.swm.po`包,创建`Emp.java`类 ```java package cn.doooog.swm.po; public class Emp { private Integer EMPno; private String ename; private String job; private Integer mgr; private String hiredate; private Double sal; private Double comm; private Integer DEPTno; public Emp() { } public Emp(Integer EMPno, String ename, String job, Integer mgr, String hiredate, Double sal, Double comm, Integer DEPTno) { this.EMPno = EMPno; this.ename = ename; this.job = job; this.mgr = mgr; this.hiredate = hiredate; this.sal = sal; this.comm = comm; this.DEPTno = DEPTno; } public Integer getDEPTno() { return DEPTno; } public void setDEPTno(Integer DEPTno) { this.DEPTno = DEPTno; } public Double getComm() { return comm; } public void setComm(Double comm) { this.comm = comm; } public Double getSal() { return sal; } public void setSal(Double sal) { this.sal = sal; } public String getHiredate() { return hiredate; } public void setHiredate(String hiredate) { this.hiredate = hiredate; } public Integer getMgr() { return mgr; } public void setMgr(Integer mgr) { this.mgr = mgr; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename; } public Integer getEMPno() { return EMPno; } public void setEMPno(Integer EMPno) { this.EMPno = EMPno; } @Override public String toString() { return "Emp{" + "EMPno=" + EMPno + ", ename='" + ename + '\'' + ", job='" + job + '\'' + ", mgr='" + mgr + '\'' + ", hiredate='" + hiredate + '\'' + ", sal=" + sal + ", comm=" + comm + ", DEPTno=" + DEPTno + '}'; } } ``` ## 四、创建数据层 在项目目录下新建`cn.doooog.swm.mapper`包,创建`EmpMapper.java`接口 ```java package cn.doooog.swm.mapper; import cn.doooog.swm.po.Emp; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; @Mapper public interface EmpMapper { @Select("select * from EMP where EMPno=#{EMPno}") Emp getEmpById(int EMPno); } ``` **注意**:这里虽然是数据层,本应使用`@Repository`创建组件,但因为是`mapper`接口,为了告诉Spring这是一个mapper接口,让其将数据源、SQLFactory全部注入到这个接口中,所以使用`@Mapper`来创建组件。 ## 五、创建服务层 在项目目录下新建`cn.doooog.swm.service`包,创建`EmpService.java`接口 ```java package cn.doooog.swm.sercive; import cn.doooog.swm.po.Emp; public interface EmpService { Emp getEmpById(int EMPno); } ``` 新建`cn.doooog.swm.service.impl`包,创建`EmpServiceImpl.java`类 将其注册为`SpringBean`,并使用`@Autowired`注解自动注入`EmpMapper`到属性中。 ```java package cn.doooog.swm.sercive.impl; import cn.doooog.swm.mapper.EmpMapper; import cn.doooog.swm.po.Emp; import cn.doooog.swm.sercive.EmpService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("empService") public class EmpServiceImpl implements EmpService { private EmpMapper empMapper; public EmpServiceImpl() { } @Autowired public EmpServiceImpl(EmpMapper empMapper) { this.empMapper = empMapper; } @Override public Emp getEmpById(int EMPno) { if (EMPno <= 0) return null; return empMapper.getEmpById(EMPno); } } ``` **注意**:此处可以注入`EmpMapper`是因为配置数据源时配置了`MapperScanner`,由其自动实现`mapper`接口并注册为组件。 ## 六、创建测试类 在项目目录下创建`Test.java` ```java package cn.doooog.swm; import cn.doooog.swm.sercive.EmpService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("cn/doooog/swm/SpringConfig.xml"); EmpService empService = (EmpService) context.getBean("empService"); System.out.println(empService.getEmpById(7788)); } } ``` **注意**:`xml`文件的路径必须是相对于`rescources`目录下的全路径。 ## 七、事务管理实现 #### 1、配置Spring事务管理器 在`SpringConfig.xml`配置文件中添加`bean` ```xml ``` #### 2、开启注解事务 ```xml ``` #### 3、实现事务管理 以工资转移为例,在`EmpMapper.java`中添加方法 ```java @Update("update EMP set sal=sal+#{sal} where EMPno=#{EMPno}") int updateSal(@Param("EMPno") int EMPno, @Param("sal") double amount); ``` 在`EmpServiceImpl.java`中添加方法 ```java @Override @Transactional public boolean transfer(int outNo, int inNo, double amount) throws Exception { int i = empMapper.updateSal(outNo, -amount); int j = empMapper.updateSal(inNo, amount); return i == 1 && j == 1; } ``` **注意**:需要事务管理的`service`,在`方法`上加`@Transactional`注解即可,必须为`public方法`才可以,不要捕获异常,要让异常自动抛出,否则不能进行事务回滚。 在测试类中测试即可 #### 4、事务传播行为(机制) 在项目复杂的情况下,事务嵌套了子事务,如果不清楚当前查询是否以事务的方式进行运行,那么就需要为`@Transactional`添加`propagation = Propagation.XXX`的参数。 有以下几种模式 `REQUIRED`(默认):如果当前存在事务,则加入该事务;否则创建新事务。 `SUPPORTS`:如果当前存在事务,则加入;否则以非事务方式执行。 `MANDATORY`:强制要求存在事务,否则抛`IllegalTransactionStateException`。 `REQUIRES_NEW`:无论当前是否有事务,都创建新事务。原事务暂停,新事务独立提交/回滚。 `NOT_SUPPORTED`:以非事务方式执行,挂起当前事务(如果存在)。 `NEVER`:禁止事务存在,否则抛异常。 `NESTED`:如果当前有事务,则在嵌套事务(子事务)中执行,子事务可单独回滚;否则行为同`REQUIRED`。 纵向对比 | 行为 | 是否有事务环境 | 事务操作方式 | 适用场景 | | :--------------- | :------------- | :----------------- | :--------------------------- | | **REQUIRED** | 无 | 新建事务 | 默认选择(80%场景) | | **SUPPORTS** | 无 | 非事务运行 | 灵活查询 | | **MANDATORY** | 无 | 抛异常 | 强制事务环境 | | **REQUIRES_NEW** | 有/无 | 始终新建独立事务 | 独立操作(如日志记录) | | **NESTED** | 有 | 嵌套事务(子事务) | 部分可回滚的操作(复杂流程) | Loading... # Spring集成MyBatis 使用Spring维持IOC(控制反转)的思想,利用DI(依赖注入)来给MyBatis注入所需要的组件,比如:DataSource(数据源)、SqlSessionFactory、SqlSession、事务管理对象等 ## 一、导入依赖框架 修改项目的`pom.xml`仓库配置文件 ```xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.doooog</groupId> <artifactId>springWithMyBatis</artifactId> <version>1.0-SNAPSHOT</version> <properties> <spring.version>7.0.0-M9</spring.version> <maven.compiler.source>22</maven.compiler.source> <maven.compiler.target>22</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- Spring相关依赖 --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.24</version> </dependency> <!-- MyBatis相关依赖 --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.19</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.0.5</version> </dependency> <!-- JDBC相关依赖 --> <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>9.4.0</version> </dependency> </dependencies> </project> ``` ## 二、配置数据源 #### 1、创建Spring配置文件 在`rescources`资源目录下新建目录`cn/doooog/swm`,创建`SpringConfig.xml` ```xml <?xml version="1.0" encoding="utf-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"> <!-- 注解扫描 --> <context:component-scan base-package="cn.doooog.swm"/> <!-- 声明自动为spring容器中配置了aspectj切面的bean自动创建代理,织入通知 --> <aop:aspectj-autoproxy/> </beans> ``` #### 2、创建数据库连接配置 在`rescources`资源目录下创建`db.properties` ```properties jdbc.mysql.driver=com.mysql.cj.jdbc.Driver jdbc.mysql.url=jdbc:mysql://localhost:3306/databaseName?characterEncoding=utf-8 jdbc.mysql.username=username jdbc.mysql.password=password ``` #### 3、配置数据源 ##### 引入DB配置文件 ```xml <context:property-placeholder location="classpath:db.properties"/> ``` ##### 配置数据源dataSource ```xml <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.mysql.driver}"/> <property name="url" value="${jdbc.mysql.url}"/> <property name="username" value="${jdbc.mysql.username}"/> <property name="password" value="${jdbc.mysql.password}"/> </bean> ``` **注意**:因为数据源也是一个对象,所以要用`bean`,其中数据源的类文件在`spring-jdbc`依赖中已经提供。 ##### 配置`SqlSessionFactory` 创建SqlSessionFactory,并且扫描实体包创建别名 ```xml <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="cn.doooog.swm.po"/> </bean> ``` **注意**:可以看到`<property>`标签传值的属性一共有两种,其中`value`传的是一个确切的值,`ref`传的是一个对象。 ##### 配置Mapper扫描器 `MapperScanner`自动扫描`mapper`接口,为`mapper`接口注入`SqlSessionFactory` ```xml <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <property name="basePackage" value="cn.doooog.swm.mapper"/> </bean> ``` **注意**:这里使用`sqlSessionFactoryBeanName`而不是`sqlSessionFactory`是因为用`sqlSessionFactory`直接会出现某些问题,已被弃用。 完整的`SpringConfig.xml` ```xml <?xml version="1.0" encoding="utf-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"> <!-- 注解扫描 --> <context:component-scan base-package="cn.doooog.swm"/> <!-- 引入DB配置文件 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置数据源dataSource --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.mysql.driver}"/> <property name="url" value="${jdbc.mysql.url}"/> <property name="username" value="${jdbc.mysql.username}"/> <property name="password" value="${jdbc.mysql.password}"/> </bean> <!-- 创建SqlSessionFactory,并且扫描实体包创建别名 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="cn.doooog.swm.po"/> </bean> <!-- 配置Mapper,自动扫描mapper接口,为mapper接口注入SqlSessionFactory --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <property name="basePackage" value="cn.doooog.swm.mapper"/> </bean> <!-- 声明自动为spring容器中配置了aspectj切面的bean自动创建代理,织入通知 --> <aop:aspectj-autoproxy/> </beans> ``` ## 三、创建数据库表实体对象 在项目目录下新建`cn.doooog.swm.po`包,创建`Emp.java`类 ```java package cn.doooog.swm.po; public class Emp { private Integer EMPno; private String ename; private String job; private Integer mgr; private String hiredate; private Double sal; private Double comm; private Integer DEPTno; public Emp() { } public Emp(Integer EMPno, String ename, String job, Integer mgr, String hiredate, Double sal, Double comm, Integer DEPTno) { this.EMPno = EMPno; this.ename = ename; this.job = job; this.mgr = mgr; this.hiredate = hiredate; this.sal = sal; this.comm = comm; this.DEPTno = DEPTno; } public Integer getDEPTno() { return DEPTno; } public void setDEPTno(Integer DEPTno) { this.DEPTno = DEPTno; } public Double getComm() { return comm; } public void setComm(Double comm) { this.comm = comm; } public Double getSal() { return sal; } public void setSal(Double sal) { this.sal = sal; } public String getHiredate() { return hiredate; } public void setHiredate(String hiredate) { this.hiredate = hiredate; } public Integer getMgr() { return mgr; } public void setMgr(Integer mgr) { this.mgr = mgr; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename; } public Integer getEMPno() { return EMPno; } public void setEMPno(Integer EMPno) { this.EMPno = EMPno; } @Override public String toString() { return "Emp{" + "EMPno=" + EMPno + ", ename='" + ename + '\'' + ", job='" + job + '\'' + ", mgr='" + mgr + '\'' + ", hiredate='" + hiredate + '\'' + ", sal=" + sal + ", comm=" + comm + ", DEPTno=" + DEPTno + '}'; } } ``` ## 四、创建数据层 在项目目录下新建`cn.doooog.swm.mapper`包,创建`EmpMapper.java`接口 ```java package cn.doooog.swm.mapper; import cn.doooog.swm.po.Emp; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; @Mapper public interface EmpMapper { @Select("select * from EMP where EMPno=#{EMPno}") Emp getEmpById(int EMPno); } ``` **注意**:这里虽然是数据层,本应使用`@Repository`创建组件,但因为是`mapper`接口,为了告诉Spring这是一个mapper接口,让其将数据源、SQLFactory全部注入到这个接口中,所以使用`@Mapper`来创建组件。 ## 五、创建服务层 在项目目录下新建`cn.doooog.swm.service`包,创建`EmpService.java`接口 ```java package cn.doooog.swm.sercive; import cn.doooog.swm.po.Emp; public interface EmpService { Emp getEmpById(int EMPno); } ``` 新建`cn.doooog.swm.service.impl`包,创建`EmpServiceImpl.java`类 将其注册为`SpringBean`,并使用`@Autowired`注解自动注入`EmpMapper`到属性中。 ```java package cn.doooog.swm.sercive.impl; import cn.doooog.swm.mapper.EmpMapper; import cn.doooog.swm.po.Emp; import cn.doooog.swm.sercive.EmpService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("empService") public class EmpServiceImpl implements EmpService { private EmpMapper empMapper; public EmpServiceImpl() { } @Autowired public EmpServiceImpl(EmpMapper empMapper) { this.empMapper = empMapper; } @Override public Emp getEmpById(int EMPno) { if (EMPno <= 0) return null; return empMapper.getEmpById(EMPno); } } ``` **注意**:此处可以注入`EmpMapper`是因为配置数据源时配置了`MapperScanner`,由其自动实现`mapper`接口并注册为组件。 ## 六、创建测试类 在项目目录下创建`Test.java` ```java package cn.doooog.swm; import cn.doooog.swm.sercive.EmpService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("cn/doooog/swm/SpringConfig.xml"); EmpService empService = (EmpService) context.getBean("empService"); System.out.println(empService.getEmpById(7788)); } } ``` **注意**:`xml`文件的路径必须是相对于`rescources`目录下的全路径。 ## 七、事务管理实现 #### 1、配置Spring事务管理器 在`SpringConfig.xml`配置文件中添加`bean` ```xml <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> ``` #### 2、开启注解事务 ```xml <tx:annotation-driven transaction-manager="transactionManager"/> ``` #### 3、实现事务管理 以工资转移为例,在`EmpMapper.java`中添加方法 ```java @Update("update EMP set sal=sal+#{sal} where EMPno=#{EMPno}") int updateSal(@Param("EMPno") int EMPno, @Param("sal") double amount); ``` 在`EmpServiceImpl.java`中添加方法 ```java @Override @Transactional public boolean transfer(int outNo, int inNo, double amount) throws Exception { int i = empMapper.updateSal(outNo, -amount); int j = empMapper.updateSal(inNo, amount); return i == 1 && j == 1; } ``` **注意**:需要事务管理的`service`,在`方法`上加`@Transactional`注解即可,必须为`public方法`才可以,不要捕获异常,要让异常自动抛出,否则不能进行事务回滚。 在测试类中测试即可 #### 4、事务传播行为(机制) 在项目复杂的情况下,事务嵌套了子事务,如果不清楚当前查询是否以事务的方式进行运行,那么就需要为`@Transactional`添加`propagation = Propagation.XXX`的参数。 有以下几种模式 `REQUIRED`(默认):如果当前存在事务,则加入该事务;否则创建新事务。 `SUPPORTS`:如果当前存在事务,则加入;否则以非事务方式执行。 `MANDATORY`:强制要求存在事务,否则抛`IllegalTransactionStateException`。 `REQUIRES_NEW`:无论当前是否有事务,都创建新事务。原事务暂停,新事务独立提交/回滚。 `NOT_SUPPORTED`:以非事务方式执行,挂起当前事务(如果存在)。 `NEVER`:禁止事务存在,否则抛异常。 `NESTED`:如果当前有事务,则在嵌套事务(子事务)中执行,子事务可单独回滚;否则行为同`REQUIRED`。 纵向对比 | 行为 | 是否有事务环境 | 事务操作方式 | 适用场景 | | :--------------- | :------------- | :----------------- | :--------------------------- | | **REQUIRED** | 无 | 新建事务 | 默认选择(80%场景) | | **SUPPORTS** | 无 | 非事务运行 | 灵活查询 | | **MANDATORY** | 无 | 抛异常 | 强制事务环境 | | **REQUIRES_NEW** | 有/无 | 始终新建独立事务 | 独立操作(如日志记录) | | **NESTED** | 有 | 嵌套事务(子事务) | 部分可回滚的操作(复杂流程) | 最后修改:2025 年 10 月 22 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 👆请简单粗暴地爱我。