文件 一个业务操作(业务方法)包含多个数据操作,这多个数据操作是一个逻辑整体,并遵循ACID原则。 A:原子性,事务中多个数据操作是一个整体,不可分割 begin:开启事务 编程式事务处理:java代码 为了让小伙伴更快地了解Spring 事务处理,下面用银行的交易作为案例 需要的工具:MySQL,postman,Spring 开发工具SpringToolSuite4等 MySQL account表中的数据,exchange(记录)表数据为空 工程目录: pom.xml(工程所需的依赖) application.properties(配置信息) Account.java(实体类) Exchange.java(实体类) AccountMapper.java(对Account的数据访问) Exchange.java(对Exchange的数据访问) BankService.java BankServiceImpl.java NoMoneyException.ajva AccountController.java ExchangeController.java 接下来运行程序,浏览器查看效果: 这里可以取到数据库中account表的数据 接下来就是重点了,假设alice向rose转账5000, 可见, lice向rose转账5000成功了,alice的账号少了5000,而rose的账号多了5000。我们再看下记录exchange表 可以看到12这一条记录,转账成功status为T,失败则为F,但是记录是有的。 在这里动脑子的小伙伴可能会问:如果jack只有4500,他向rose转账5000,怎么办??? 别急,基于这一点,已经做了处理,这种情景记录exchange表是有的,但是转账会报错:余额不足 看下控制台的信息,交易记录为13的显示余额不足 最后我们看下account表的数据,可见数据没有发生变化 记录exchange表 记录exchange表有13这一条数据的,但是status为F。表示转账失败,因为jack的余额不足。 关于Spring 事务处理就到这结束,有问题的小伙伴,欢迎留言!!!数据存储
关系型数据库
关系模型(关系代数)—范式
SQL–标准的数据操作语言
事务处理
NoSQL
1.文档 —mogodb
2.键值型–redis
3.图–Neo4j
4.列–HBase 事务定义
C:一致性,事务中多个数据操作执行的结果一致,全部提交,或者全部撤销(回滚)
I:隔离性,多个事务中相关的数据操作是隔离的,不会交织运行,存在时序
读未提交–存在脏读,存在不可重复读,存在幻读 高并发,没有隔离,数据的不一致
读已提交–解决脏读,存在不可重复读,存在幻读 Oracle 默认级别
可重复读—解决不可重复读,存在幻读
串行化–解决幻读,确保数据一致性,性能差
D:持久性,事务执行后数据状态的存储数据库中的事务操作
save point:设置保存点
commit:提交
rollback:回滚Spring AOP声明式事务处理
声明式事务处理:AOP&注解
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.newer</groupId> <artifactId>ts</artifactId> <version>0.1</version> <name>ts</name> <description>Demo project for Spring Boot</description> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
# spring.datasource.url=jdbc:mysql://127.0.0.1:3306/bank?serverTimezone=UTC spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver logging.level.web=debug spring.http.log-request-details=true
package com.newer.ts.entity; import java.math.BigDecimal; /** * 账户 * @author Admin * */ public class Account { long id; String name; /** * 金融相关不使用DOUBLE */ BigDecimal balance; public Account() { } public Account(long id) { super(); this.id = id; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public BigDecimal getBalance() { return balance; } public void setBalance(BigDecimal balance) { this.balance = balance; } @Override public String toString() { return "Account [id=" + id + ", name=" + name + ", balance=" + balance + "]"; } }
package com.newer.ts.entity; /** * 交易记录 * @author Admin * */ import java.math.BigDecimal; import java.sql.Date; public class Exchange { /** * 编号 */ long id; /** * 交易时间 */ Date time; /** * 转出 */ Account from; /** * 转入 */ Account to; /** * 交易金额 */ BigDecimal money; /** * 状态 */ char status; public Exchange() { } public long getId() { return id; } public void setId(long id) { this.id = id; } public Date getTime() { return time; } public void setTime(Date time) { this.time = time; } public Account getFrom() { return from; } public void setFrom(Account from) { this.from = from; } public Account getTo() { return to; } public void setTo(Account to) { this.to = to; } public BigDecimal getMoney() { return money; } public void setMoney(BigDecimal money) { this.money = money; } public char getStatus() { return status; } public void setStatus(char status) { this.status = status; } @Override public String toString() { return "Exchange [id=" + id + ", time=" + time + ", from=" + from + ", to=" + to + ", money=" + money + ", status=" + status + "]"; } }
package com.newer.ts.mapper; import java.math.BigDecimal; import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import com.newer.ts.entity.Account; @Mapper public interface AccountMapper { @Select("select * from account") List<Account> findAll(); @Select("select * from account where id=#{id}") Account load(long id); /** * 查询账户余额 * @param id 账户编号 * @return 余额 */ @Select("select balance from account where id=#{id}") BigDecimal getBalance(long id); /** * 更新账户余额 * @param id 账户编号 * @param money 金额,正为转入,负为转出 */ @Update("update account set balance = balance + #{money} where id=#{id}") void updateBalance(long id,BigDecimal money); }
package com.newer.ts.mapper; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Update; import com.newer.ts.entity.Exchange; @Mapper public interface ExchangeMapper { /** * 创建交易记录,可获得交易记录的编号 * @param exchange */ @Insert("insert into exchange(`from`,`to`,`money`) values(#{from.id},#{to.id},#{money})") @Options(useGeneratedKeys = true,keyProperty = "id") void create(Exchange exchange); /** * 交易状态更新为成功(事务提交) * @param id */ @Update("update exchange set status='T' where id=#{id}") void updateStatusTrue(long id); }
package com.newer.ts.service; import java.math.BigDecimal; import com.newer.ts.entity.Account; import com.newer.ts.entity.Exchange; import com.newer.ts.exception.NoMoneyException; /** * 定义业务逻辑 * @author Admin * */ public interface BankService { /** * 开户 * @param account 账户基本信息 * @return */ Account createAccount(Account account); /** * 转账 * @param from 支出账户 * @param to 收入账户 * @param money 转账金额 * @throws NoMoneyException * @return 交易信息 * */ Exchange exchange(long from,long to,BigDecimal money) throws NoMoneyException; Exchange createExchange(Exchange exchange); }
package com.newer.ts.service; import java.math.BigDecimal; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.newer.ts.entity.Account; import com.newer.ts.entity.Exchange; import com.newer.ts.exception.NoMoneyException; import com.newer.ts.mapper.AccountMapper; import com.newer.ts.mapper.ExchangeMapper; /** * 业务逻辑的具体实现 * @author Admin * */ @Service public class BankServiceImpl implements BankService{ @Autowired AccountMapper accountMapper; @Autowired ExchangeMapper exchangeMapper; // 注入上下文 @Autowired ApplicationContext context; @Override public Account createAccount(Account account) { return null; } // 标记类(所有公共方法自动开启事务)或者方法 @Transactional( isolation = Isolation.SERIALIZABLE, rollbackFor = NoMoneyException.class ) @Override public Exchange exchange(long from,long to, BigDecimal money) throws NoMoneyException { Exchange exchange=new Exchange(); exchange.setFrom(new Account(from)); exchange.setTo(new Account(to)); exchange.setMoney(money); // 事务的传播行为 Propagation.REQUIRES_NEW // 该方法在新事务中执行 // createExchange(exchange); BankService bankService=context.getBean(BankService.class); bankService.createExchange(exchange); // 支出账户余额是否充足 BigDecimal balance= accountMapper.getBalance(from); // 判断支出账户余额是否充足 if(balance.doubleValue() < money.doubleValue()) { // 余额不足 // 回滚 throw new NoMoneyException(); } // 支出账户扣钱(-) negate accountMapper.updateBalance(from, money.negate()); // 收入账户是否可用(未冻结) // 收入账户加钱(+) accountMapper.updateBalance(to, money); // 交易完成 exchangeMapper.updateStatusTrue(exchange.getId()); return exchange; } // 设置事务的传播行为 // 挂起(暂停)原有的事务,创建一个新的事务执行以下操作 // 若原有事务(外层)事务回滚,该(新事务)事务不回滚 @Transactional(propagation = Propagation.REQUIRES_NEW) public Exchange createExchange(Exchange exchange) { // 写入交易记录(写日志优先) exchangeMapper.create(exchange); System.out.println("交易记录的编号:"+exchange.getId()); return exchange; } //// 实现接口ApplicationContextAware注入上下文,过时 //@Override // public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // context=applicationContext; // // } }
package com.newer.ts.exception; /** * 自定义异常,对正常流程中断, * @author Admin * */ public class NoMoneyException extends Exception { private static final long serialVersionUID=1L; public NoMoneyException() { super("余额不足"); } }
package com.newer.ts.controller; import java.math.BigDecimal; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.newer.ts.entity.Account; import com.newer.ts.mapper.AccountMapper; @RestController @RequestMapping("/account") public class AccountController { @Autowired AccountMapper accountMapper; /** * 所有账户 * @return */ @GetMapping public List<Account> list(){ return accountMapper.findAll(); } /** * 特定账户信息 * @param id 账户编号 * @return */ @GetMapping("/{id}") public Account show(@PathVariable long id) { return accountMapper.load(id); } /** * 账户余额 * @param id 账户编号 * @return */ @GetMapping("/{id}/balance") public BigDecimal balance(@PathVariable long id) { return accountMapper.getBalance(id); } }
package com.newer.ts.controller; import java.math.BigDecimal; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.newer.ts.entity.Exchange; import com.newer.ts.exception.NoMoneyException; import com.newer.ts.service.BankService; @RestController @RequestMapping("/ex") public class ExchangeController { /** * 注入业务逻辑(抽象定义) */ @Autowired BankService bankService; // 改为Post /** * Get /ex/1/2/3000 * @param from * @param to * @param money * @return */ @GetMapping("/{from}/{to}/{money}") public Exchange home( @PathVariable long from, @PathVariable long to, @PathVariable BigDecimal money) { Exchange exchange=null; try { exchange=bankService.exchange(from, to, money); } catch (NoMoneyException e) { System.out.println(e.getMessage()); } return exchange; } }
然后我们再来查看下account表的数据
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算