spring JDBC 事务管理
spring JDBC 环境
添加依赖
<?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>org.example</groupId>
<artifactId>spring</artifactId>
<version>1.0-SNAPSHOT</version>
<name>spring</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<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.12</version>
<scope>test</scope>
</dependency>
<!-- spring框架-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.16</version>
</dependency>
<!-- spingTest-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.16</version>
</dependency>
<!-- aop-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9</version>
</dependency>
<!-- jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.16</version>
</dependency>
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
</dependencies>
<build>
</build>
</project>
在resources下配置jdbc 环境
spring.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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/contex" 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
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
>
<!-- bean definitions here -->
<context:component-scan base-package="org.example"/>
<aop:aspectj-autoproxy/>
<!-- jdbc配置文件加载-->
<context:property-placeholder location="jdbc.properties"/>
<!-- c3p0 数据源配置-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- jdbcTemplate 模板对象 注入数据源-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
jdbc.properties
#mysql???
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/数据库名字?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
#??????
jdbc.username=root
#?????
jdbc.password=password
数据库
CREATE TABLE `tb_account` (
`account_id` int NOT NULL AUTO_INCREMENT,
`account_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`account_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`money` double(11, 2) NOT NULL,
`remark` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`create_time` datetime NOT NULL,
`updata_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP,
`user_id` int NOT NULL,
PRIMARY KEY (`account_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of tb_account
-- ----------------------------
INSERT INTO `tb_account` VALUES (1, '账户1', '建设银行', 1000.00, '零花钱', '2022-03-31 00:02:48', '2022-03-31 00:02:54', 1);
INSERT INTO `tb_account` VALUES (2, '账户2', '工商银行', 10000.00, '奖金', '2022-03-31 00:04:40', '2022-03-31 00:04:43', 1);
SET FOREIGN_KEY_CHECKS = 1;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hi6pWbip-1649157590573)(C:\Users\19869\AppData\Roaming\Typora\typora-user-images\image-20220405152528395.png)]
实体类
package org.example.domain;
import java.util.Date;
public class Account {
private Integer accountId;
private String accountName;
private String accountType;
private Double money;
private String remark;
private Date createTime;
private Date updateTime;
private Integer userid;
public Account() {
}
public Account( String accountName, String accountType, Double money, String remark, Integer userid) {
this.accountName = accountName;
this.accountType = accountType;
this.money = money;
this.remark = remark;
this.userid = userid;
}
public Integer getAccountId() {
return accountId;
}
public void setAccountId(Integer accountId) {
this.accountId = accountId;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public String getAccountType() {
return accountType;
}
public void setAccountType(String accountType) {
this.accountType = accountType;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid = userid;
}
@Override
public String toString() {
return "Account{" +
"accountId=" + accountId +
", accountName='" + accountName + '\'' +
", accountType='" + accountType + '\'' +
", money=" + money +
", remark='" + remark + '\'' +
", createTime=" + createTime +
", updateTime=" + updateTime +
", userid=" + userid +
'}';
}
}
,来看一个转账事务
dao
public interface AccountDao {
//支出
public int outAccount(Integer accountId , double money);
//收入
public int inAccount(Integer accountId, double money);
import org.example.domain.Account;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.List;
@Repository
public class AccountDaoImpl implements AccountDao{
@Resource
private JdbcTemplate jdbcTemplate;
@Override
public int outAccount(Integer accountId, double money) {
String sql = "update tb_account set money = money - ? where account_id =?";
Object[] objects ={money ,accountId};
return jdbcTemplate.update(sql,objects);
}
@Override
public int inAccount(Integer accountId, double money) {
String sql = "update tb_account set money = money+ ? where account_id =?";
Object[] objects ={money ,accountId};
return jdbcTemplate.update(sql,objects);
}
}
service
import org.example.dao.AccountDao;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class AccountService {
@Resource
AccountDao accountDao;
public int updateAccountByTranfer(Integer outId,Integer inId,double money){
int code =0;
int i = accountDao.outAccount(outId, money);
int i1 = accountDao.inAccount(inId, money);
if(i==1 && i1==1){
code=1;
}
return code;
}
}
Test
import org.example.Service.AccountService;
import org.junit.Test;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class) //将运行环境运行在spring 测试环境下
@ContextConfiguration(locations = {"classpath:spring.xml"})//获得上下文环境、
public class springTest extends BaseTest {
@Resource
private AccountService accountService;
@Test
public void test (){
int code = accountService.updateAccountByTranfer(1, 2, 500.00);
if (code==1){
System.out.println("转账成功");
}
}
}
//输出 转账成功
数据库
这样子是正常的我们来搞个异常
会报错来看数据库
账户一少了500.但是账户2并没有多500.
这样子就要有事务的介入了
事务的四大特性
原子性:共生死,要么全部成功,要么全部失败!一致性(一致性)
一致性:事务在执行前后,数据库中数据要保持一致性状态。(如转账的过程账户操作后数据必须保持一致)·
隔离性(lsolation)
事务与事务之间的执行应当是相互隔离互不影响的。(多个角色对统一记录进行操作必须保证没有任何干扰),当然没有影响是不可能的,为了让影响级别降到最低,通过隔离级别加以限制:
1.未提交(读未提交)
隔离级别最低的一种事务级别.在这种隔离级别下,会引发脏读、不可重复读和幻读.
2.READ_COMMIT(读已提交)读到的都是别人提交后的值。这种隔离级别下,会引发不可重复读和幻读,但避免了脏读。
3.REPEATABLE_READ(可重复读)
读到的都是别人提交后的值.这种隔离级别下,会引发不可重复读和幻读,但避免了脏读.
.4.SERIALIZABLE(串行化)
最严格的隔离级别.在可序列化隔离级别下,所有事务按照次序依次执行.脏读、不可重复读、幻读都不会出现.
持久性:事务一旦提交,数据库不会改变
Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
spring 事务配置
添加依赖
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx https://www.springframework.org/schema/aop/spring-tx.xsd
spring.xml
<!--spring 事务配置-->
<!-- 开启Aop代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 配置事务管理器-->
<bean id="txManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 c3p0-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="txManger">
<!--定义什么方法需要事务处理-->
<tx:attributes>
<tx:method name="updateAccountByTranfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- aop设置-->
<aop:config>
<aop:pointcut id="cut" expression="execution(* org.example.Service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="cut"/>
</aop:config>
注解配置
<!--spring 事务配置-->
<!-- 开启Aop代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 配置事务管理器-->
<bean id="txManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 c3p0-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启事务通知-->
<tx:annotation-driven transaction-manager="txManger"/>
在方法上添加注解 @Transactional(propagation = Propagation.REQUIRED)