1.将 MyBatis 与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring 来管理。
2.整合只需要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。
3.实现 Spring 与 MyBatis 的整合。常用的方式:扫描的 Mapper 动态代理。
SM整合的步骤(示例)
1.建表(在数据库中添加新表)
use ssm;
create table users(
userid int primary key,
uname varchar(20),
upass varchar(20)
);
create table accounts(
aid int primary key,
aname varchar(20),
acontent varchar(50)
);
2.新建项目,选择quickstart模板
3.修改完善目录
4.修改pom.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>com.dy</groupId>
<artifactId>spring-sm</artifactId>
<version>1.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!--aspectj依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.19</version>
</dependency>
<!--spring核心ioc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.19</version>
</dependency>
<!--做spring事务用到的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.19</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.19</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!--mybatis和spring集成的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!--阿里公司的数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.9</version>
</dependency>
</dependencies>
<build>
<!--目的是把src/main/java目录中的xml文件包含到输出结果中。输出到classes目录中-->
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
5.添加SqlMapConfig.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>
<!--设置日志输出语句底层代码,显示相应操作的sql语名-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
6.添加jdbc.propertiest属性文件到resources目录下
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&&characterEncoding=gbk
jdbc.username=root
jdbc.password=123456
7.添加applicationContext_mapper.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--读取属性文件jdbc.properties-->
<context:property-placeholder location="jdbc.properties"/>
<!--创建数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置SqlSessionFactoryBean类-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!--配置MyBatis的核心配置文件-->
<property name="configLocation" value="SqlMapConfig.xml"/>
<!--注册实体类的别名-->
<property name="typeAliasesPackage" value="com.dy.pojo"/>
</bean>
<!--注册mapper.xml文件-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dy.mapper"/>
</bean>
</beans>
8.添加applicationContext_service.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"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--导入applicationContext_mapper.xml文件-->
<import resource="applicationContext_mapper.xml"/>
<!--SM是基于注解的开发,所以添加包扫描-->
<context:component-scan base-package="com.dy.service.impl"/>
<!--事务处理-->
</beans>
9.在com.dy.pojo包下添加Users实体类
package com.dy.pojo;
public class Users {
private Integer userid;
private String uname;
private String upass;
@Override
public String toString() { return "Users{" +"userid=" + userid + ", uname='" + uname + '\'' + ", upass='" + upass + '\'' + '}'; }
public Integer getUserid() { return userid; }
public void setUserid(Integer userid) { this.userid = userid; }
public String getUname() { return uname;}
public void setUname(String uname) {this.uname = uname;}
public String getUpass() { return upass;}
public void setUpass(String upass) { this.upass = upass; }
public Users(Integer userid, String uname, String upass) {this.userid = userid; this.uname = uname;this.upass = upass; }
public Users() { }
}
10.在com.dy.mapper包下添加UsersMapper接口和UsersMapper.xml文件
public interface UsersMapper {
int insert(Users users);//添加用户
}
<?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.dy.mapper.UsersMapper">
<insert id="insert" parameterType="users">
insert into users values(#{userid},#{uname},#{upass})
</insert>
</mapper>
11.在com.dy.service包下,添加UsersService接口
public interface UsersService {
int insert (Users users); //添加用户
}
在com.dy.service.mipl包下UsersServiceImpl实现类
@Service //交给Spring去创建对象
public class UsersServiceImpl implements UsersService {
//切记切记:在所有的业务逻辑层中一定会有数据访问层的对象
@Autowired //直接按类型自动注入
// 在usersMapper.xml中完成功能的动态代理对象已经注册到到applicationContext_mapper.xml 当中
UsersMapper usersMapper;
@Override
public int insert(Users users) {
return usersMapper.insert(users);
}
}
12.添加测试类进行功能测试
public class MyTest {
@Test
public void testUsers() {
//创建容器并启动
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_service.xml");
//取出UsersServiceImpl
UsersService uService = (UsersService) ac.getBean("usersServiceImpl");
int num = uService.insert(new Users(100,"张三","123"));
System.out.println(num);
}
}
测试结果:
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5ddea849] was not registered for synchronization because synchronization is not active
5月 16, 2022 6:22:13 下午 com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl info
信息: {dataSource-1} inited
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5d465e4b] will not be managed by Spring
==> Preparing: insert into users values(?,?,?)
==> Parameters: 100(Integer), 张三(String), 123(String)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5ddea849]
1
创建账户实体类
1.在com.dy.pojo包下添加Accounts实体类
package com.dy.pojo;
public class Accounts {
private Integer aid;
private String aname;
private String acontent;
@Override
public String toString() { return "Accounts{" +"aid=" + aid + ", aname='" + aname + '\'' + ", acontent='" + acontent + '\'' + '}'; }
public Integer getAid() {return aid;}
public void setAid(Integer aid) {this.aid = aid; }
public String getAname() { return aname;}
public void setAname(String aname) { this.aname = aname;}
public String getAcontent() { return acontent; }
public void setAcontent(String acontent) {this.acontent = acontent;}
public Accounts(Integer aid, String aname, String acontent) {this.aid = aid; this.aname = aname; this.acontent = acontent; }
public Accounts() {}
}
2.在com.dy.mapper包下添加AccountsMapper接口和AccountsMapper.xml文件
public interface AccountsMapper {
int save(Accounts accounts); //增加帐户
}
<?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.dy.mapper.AccountsMapper">
<insert id="save" parameterType="accounts">
insert into accounts values(#{aid},#{aname},#{acontent})
</insert>
</mapper>
3.在com.dy.service包下,添加AccountsService接口
public interface AccountsService {
int save(Accounts accounts);
}
在com.dy.service.mipl包下AccountsServiceImpl实现类
@Service
public class AccountsServiceImpl implements AccountsService {
//一定会有数据访问层的对象
@Autowired
AccountsMapper accountsMapper;
@Override
public int save(Accounts accounts) {
int num = 0;
num = accountsMapper.save(accounts);
System.out.println("增加帐户成功!num="+num);
return num;
}
}
4.测试类
@Test
public void testAccounts(){
//创建容器并启动
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_service.xml");
AccountsService accountsService = (AccountsService) ac.getBean("accountsServiceImpl");
System.out.println(accountsService.getClass());
accountsService.save(new Accounts(206,"李四6","帐户安全6666!"));
}
测试结果:
增加帐户成功!num=1
Spring的两种事务处理方式
1.注解式的事务
使用@Transactional注解完成事务控制,此注解可添加到类上,则对类中所有方法执行事务的设定.此注解可添加到方法上,只是对此方法执行事务的处理.
1.在applicationContext_service.xml中添加事物管理器
<!--1.添加事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--因为事务必须关联数据库处理,所以要配置数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--2.添加事务的注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
2.在AccountsServiceImpl实现类中手动添加一个异常(测试事务的回滚处理)
@Service
@Transactional(propagation = Propagation.REQUIRED)//使用声明式事务方法需要去掉该注解
public class AccountsServiceImpl implements AccountsService {
//一定会有数据访问层的对象
@Autowired
AccountsMapper accountsMapper;
@Override
public int save(Accounts accounts) {
int num = 0;
num = accountsMapper.save(accounts);
System.out.println("增加帐户成功!num="+num);
System.out.println(1/0);//手工抛出异常 ArithmeticException算数异常
return num;
}
}
当方法在执行过程中,发生任何的错误,@Transactional事务注解能保证将已插入成功的操作撤销掉
3.在UsersServiceImpl实现类中调用(包含)AccountsServiceImpl的功能
@Service //交给Spring去创建对象
@Transactional(propagation = Propagation.REQUIRED) //使用声明式事务方法需要去掉该注解
public class UsersServiceImpl implements UsersService {
@Autowired
UsersMapper usersMapper;
@Autowired
AccountsService accountsService;
@Override
public int insert(Users users) {
int num = usersMapper.insert(users);
System.out.println("用户增加成功!num="+num);
//调用帐户的增加操作,调用的帐户的业务逻辑层的功能
num = accountsService.save(new Accounts(300,"王五","帐户好的呢!"));
return num;
}
}
Spring事务的传播特性:多个事务之间的合并,互斥等都可以通过设置事务的传播特性来解决
常用:
PROPAGATION_REQUIRED:必被包含事务(增删改必用)
PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务
PROPAGATION_SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不单开事务
PROPAGATION_NEVER:不能运行中事务中,如果包在事务中,抛异常
PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境
以下是在不同状态事务下的测试结果:(项目中的所有事务,必须添加到业务逻辑层上)
@Transactional(propagation = Propagation.*******)
2.声明式事务(必须掌握)
在配置文件中添加一次,整个项目遵循事务的设定
要求项目中的方法命名有规范:配置事务切面时可以使用通配符*来匹配所有方法
1.完成增加操作包含 add save insert set
2.更新操作包含 update change modify
3.删除操作包含 delete drop remove clear
4.查询操作包含 select find search get
创建新的配置文件applicationContext_trans.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--此配置文件与applicationContext_service.xml的功能一样,只是事务配置不同-->
<!--导入applicationContext_mapper.xml-->
<import resource="applicationContext_mapper.xml"/>
<!--添加包扫描-->
<context:component-scan base-package="com.dy.service.impl"/>
<!--添加事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务切面-->
<tx:advice id="myadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*select*" read-only="true"/>
<tx:method name="*find*" read-only="true"/>
<tx:method name="*search*" read-only="true"/>
<tx:method name="*get*" read-only="true"/>
<tx:method name="*insert*" propagation="REQUIRED"/>
<!--可指定异常类型,不处理,不进行回滚-->
<!--<tx:method name="*insert*" propagation="REQUIRED" no-rollback-for="ArithmeticException"/> 指定算数异常不回滚-->
<tx:method name="*add*" propagation="REQUIRED"/>
<tx:method name="*save*" propagation="REQUIRED"/>
<tx:method name="*set*" propagation="REQUIRED"/>
<tx:method name="*update*" propagation="REQUIRED"/>
<tx:method name="*change*" propagation="REQUIRED"/>
<tx:method name="*modify*" propagation="REQUIRED"/>
<tx:method name="*delete*" propagation="REQUIRED"/>
<tx:method name="*remove*" propagation="REQUIRED"/>
<tx:method name="*drop*" propagation="REQUIRED"/>
<tx:method name="*clear*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!--绑定切面和切入点-->
<aop:config>
<aop:pointcut id="mycut" expression="execution(* com.dy.service.impl.*.*(..))"/>
<aop:advisor advice-ref="myadvice" pointcut-ref="mycut"/>
</aop:config>
</beans>
测试类
@Test
public void testTrans(){
//创建容器并启动
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_trans.xml");
//取出UsersServiceImpl
UsersService uService = (UsersService) ac.getBean("usersServiceImpl");
int num = uService.insert(new Users(100,"张三","123"));
System.out.println(num);
}
拓展知识(@Transactional注解参数详解)
@Transactional(括号中的内容说明)
propagation = Propagation.REQUIRED 事务的传播特性,
noRollbackForClassName = "异常名称" 指定哪些异常不回滚,使用的是异常的名称 ,
noRollbackFor = 异常名称.class 指定发生什么异常不回滚,使用的是异常的类型,
rollbackForClassName = "异常名称" 指定发生什么异常必须回滚,
rollbackFor = 异常名称.class 指定发生什么异常必须回滚,使用的是异常的类型,
timeout = -1 连接超时设置,默认值是-1,表示永不超时,
readOnly = false 默认是false增删改操作,如果是查询操作,必须设置为true,
isolation = Isolation.DEFAULT 使用数据库自已的隔离级别
Spring中事务的五大隔离级别(了解)
1.未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
2.提交读(Read Committed):只能读取到已经提交的数据。多数数据库默认都是该级别 (不重复读)
3.可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读,但是innoDB解决了幻读
4.串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
5.使用数据库默认的隔离级别isolation = Isolation.DEFAULT
MySQL:mysql默认的事务处理级别是'REPEATABLE-READ',也就是可重复读
Oracle:oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。默认系统事务隔离级别是READ COMMITTED,也就是读已提交