一、事务基本概念
1、什么是事务:
逻辑上的操作,这些操作要么都成功,有一个失败,那么都失败。事务由事务开始与事务结束之间执行全部数据库操作组成
例:银行转账,A向B转账100元,A账户少100元,而B账户增加100元,钱的总量是不变的。
而如果A账户钱少了,B用户钱没有增加,总量发生改变。
事务在执行过程中发生错误,会回滚到事务开始前的状态,就好像这个事务从来没有执行过一样。
事务保证了逻辑操作的成功
2、事务的特性:
1)原子性:事务操作要么全都完成,要么全部不完成
2)一致性:事务执行前后,数据库都必须处于一致状态。例:银行转账,A向B转账100元,A账户少100元,而B账户增加100元,钱的总量是不变的。
3)隔离性:在事务操作以前,每个事务有之间的数据空间,互不影响
4)持久性:在事务执行之后,对数据库的更新必须保存下来
二、事务种类
1、编程式(个人理解为手动搭建事务 比较繁琐 有时间在写)
2、声明式事务(通过配置让框架实现功能)
1)基于注解的声明式事务
- 添加配置(在bean.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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tool
http://www.springframework.org/schema/tool/spring-tool.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
">
<!--组件扫描-->
<context:component-scan base-package="com.it.spring6.tx"></context:component-scan>
<!-- 引入外部属性文件jdbc.properties,创建数据源对象-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 创建jdbcTemplate对象,注入数据源-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="druidDataSource"></property>
</bean>
<!--事务管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"></property>
</bean>
<!--
开启事务注解驱动
通过注解@Transactonal所标识的方法或标识的类中所有的方法,都会被事务管理器管理事务
-->
<!--transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id同名 可以省略不写transaction-manager-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
2.创建表
CREATE TABLE `t_book`(
`book_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`book_name` VARCHAR(20) DEFAULT NULL COMMENT '图书名称',
`price` int(11) DEFAULT NULL COMMENT '价格',
`stock` int(10) unsigned DEFAULT NULL COMMENT '库存(无符号)',
PRIMARY KEY(`book_id`)
)ENGINE=INNODB AUTO_INCREMENT=3 CHARSET=utf8;
INSERT INTO`t_book`(`book_id`,`book_name`,`price`,`stock`)value(1,'活着',30,100),(2,'生死疲劳',30,100);
CREATE TABLE `t_user`(
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` VARCHAR(20) DEFAULT NULL COMMENT '用户名',
`balance` INT(10) UNSIGNED DEFAULT NULL COMMENT '余额(无符号)',
PRIMARY KEY(`user_id`)
)ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=UTF8;
INSERT INTO `t_user` (`user_id`, `username`, `balance`) VALUES (1, 'admin', 50);
INSERT INTO `t_user` (`user_id`, `username`, `balance`) VALUES (2, 'sun', 500);
实现表格:
实现功能:
在数据库中显示:
2)项目结构
BookCotroller.java
package com.it.spring6.tx.controller;
import com.it.spring6.tx.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class BookController {
@Autowired
private BookService bookService;
//买书的方法:图书的id和用户id
public void buyBook(Integer bookId,Integer userId){
//调用service方法
bookService.buyBook(bookId,userId);
}
}
BookDao.java
package com.it.spring6.tx.dao;
public interface BookDao {
//根据图书id查询图书价格
Integer getBookPriceByBookId(Integer bookId);
//更新图书表库存量 -1
void updateStock(Integer bookId);
//更新用户表用户余额 -图书价格
void updateUserBalance(Integer bookId, Integer price);
}
BookDaoImpl.java
package com.it.spring6.tx.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
//dao层数据访问层 真正访问数据库的
@Repository
public class BookDaoImpl implements BookDao{
@Autowired
private JdbcTemplate jdbcTemplate;
//根据图书id查询图书的价格
@Override
public Integer getBookPriceByBookId(Integer bookId) {
String sql = "select price from t_book where book_id=?";
Integer price = jdbcTemplate.queryForObject(sql, Integer.class, bookId);
return price;
}
//更新库存
@Override
public void updateStock(Integer bookId) {
String sql = "update t_book set stock=stock-1 where book_id=?";
jdbcTemplate.update(sql, bookId);
}
//更新用户余额 -图书价格
@Override
public void updateUserBalance(Integer userId, Integer price) {
String sql = "update t_user set balance=balance-? where user_id=?";
jdbcTemplate.update(sql,price,userId);
}
}
BookService.java
package com.it.spring6.tx.service;
public interface BookService {
//买书的方法:图书的id和用户id
void buyBook(Integer bookId, Integer userId);
}
BookServiceImpl.java //@Transactional开启事务
package com.it.spring6.tx.service;
import com.it.spring6.tx.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
//Service层 业务逻辑层
@Service
public class BookServiceImpl implements BookService{
@Autowired
private BookDao bookDao;
//买书的方法:图书的id和用户id
@Transactional //放在方法上 只会对当前方法起作用 放在类上会对所有方法起作用
@Override
public void buyBook(Integer bookId, Integer userId) {
//根据图书id查询图书价格
Integer price = bookDao.getBookPriceByBookId(bookId);
//更新图书表库存量 -1
bookDao.updateStock(bookId);
//更新用户表用户余额 -图书价格
bookDao.updateUserBalance(userId,price);
}
}
TestBook.java
package com.it.spring6.tx;
import com.it.spring6.tx.controller.BookController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig(locations = "classpath:bean.xml")
public class TestBookTx {
@Autowired
private BookController bookController;
@Test
public void testBuyBook(){
bookController.buyBook(1,2);
}
}
运行成功
关于jdbc: