package com.powernode.bank.service.impl;
import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service("accountService")
//写在类上所有的方法都应用事务
public class AccountServiceImpl implements AccountService
{
@Resource(name = "accountDao")
private AccountDao accountDao;
@Override
//数据库三大读问题
//脏读:读取到还没有提交到数据库的数据,叫做脏读(读到内存里面没有commit的数据)
//不可重复读:在同一个事务中,第一次和第二次读到的数据不一样(在同一个事务中读到的数据前后不一致)
//幻读:读到的数据是假的(只要A事务和B事务在同时进行时,永远都会幻读)
//脑子里想象的数据和真实数据不一样,就是幻读,只要多个事务并发,一定会幻读
//多个事务并发一定会幻读
//读未提交:会读内存中的数据,不可重复读,因为事务并发会导致事务中读到的信息前后不一致,导致幻读
//读提交:不会读内存中的数据,仅读取数据库commit了的数据,仍然会因为事务并发读到前后不一致的信息,导致幻读
//可重复读:不会读内存中的数据,仅读取commit数据,在事务未结束前,数据库读取到的缓存信息始终不变,但是会导致幻读(因为其他事务已经修改了数据库信息)
//序列化:事务必须排队执行,不支持并发,解决了幻读问题
public void transfer(String Actno, String toActno, double money)
{
//开启事务
//需要控制事务,因为在这个方法中要完成所有的转账业务
//查询转出账户余额是否充足
Account account = accountDao.selectByAccountNo(Actno);
if(account.getBalance() < money)
{
throw new RuntimeException("余额不足");
}
Account toAccount = accountDao.selectByAccountNo(toActno);
account.setBalance(account.getBalance() - money);
toAccount.setBalance(toAccount.getBalance() + money);
int update = accountDao.update(account);
//模拟异常信息导致中断业务
// String s = null;
// s.toString();
update = update + accountDao.update(toAccount);
//提交事务
//遇到异常回滚事务
if(update != 2)
{
throw new RuntimeException("转账失败,请联系银行");
}
}
}
package com.powernode.bank.service.impl;
import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service("accountService")
//写在类上所有的方法都应用事务
public class AccountServiceImpl implements AccountService
{
@Resource(name = "accountDao")
private AccountDao accountDao;
@Override
//数据库三大读问题
//脏读:读取到还没有提交到数据库的数据,叫做脏读(读到内存里面没有commit的数据)
//不可重复读:在同一个事务中,第一次和第二次读到的数据不一样(在同一个事务中读到的数据前后不一致)
//幻读:读到的数据是假的(只要A事务和B事务在同时进行时,永远都会幻读)
//脑子里想象的数据和真实数据不一样,就是幻读,只要多个事务并发,一定会幻读
//多个事务并发一定会幻读
//读未提交:会读内存中的数据,不可重复读,因为事务并发会导致事务中读到的信息前后不一致,导致幻读
//读提交:不会读内存中的数据,仅读取数据库commit了的数据,仍然会因为事务并发读到前后不一致的信息,导致幻读
//可重复读:不会读内存中的数据,仅读取commit数据,在事务未结束前,数据库读取到的缓存信息始终不变,但是会导致幻读(因为其他事务已经修改了数据库信息)
//序列化:事务必须排队执行,不支持并发,解决了幻读问题
public void transfer(String Actno, String toActno, double money)
{
//开启事务
//需要控制事务,因为在这个方法中要完成所有的转账业务
//查询转出账户余额是否充足
Account account = accountDao.selectByAccountNo(Actno);
if(account.getBalance() < money)
{
throw new RuntimeException("余额不足");
}
Account toAccount = accountDao.selectByAccountNo(toActno);
account.setBalance(account.getBalance() - money);
toAccount.setBalance(toAccount.getBalance() + money);
int update = accountDao.update(account);
//模拟异常信息导致中断业务
// String s = null;
// s.toString();
update = update + accountDao.update(toAccount);
//提交事务
//遇到异常回滚事务
if(update != 2)
{
throw new RuntimeException("转账失败,请联系银行");
}
}
}
package com.powernode.bank.dao.impl;
import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao
{
@Resource(name = "jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Override
public Account selectByAccountNo(String actno)
{
String sql = "select actno,balance from t_act where actno = ?";
Account account = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<>(Account.class),actno);
return account;
}
@Override
public int update(Account account)
{
String sql = "update t_act set balance = ? where actno = ?";
int update = jdbcTemplate.update(sql, account.getBalance(), account.getActno());
return update;
}
}
package com.powernode.bank.dao.impl;
import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao
{
@Resource(name = "jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Override
public Account selectByAccountNo(String actno)
{
String sql = "select actno,balance from t_act where actno = ?";
Account account = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<>(Account.class),actno);
return account;
}
@Override
public int update(Account account)
{
String sql = "update t_act set balance = ? where actno = ?";
int update = jdbcTemplate.update(sql, account.getBalance(), account.getActno());
return update;
}
}
<?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 http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:13306/spring6"></property>
<property name="username" value="root"></property>
<property name="password" value="abc123"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<context:component-scan base-package="com.powernode.bank"></context:component-scan>
<!-- 配置通知,需要TX命名空间,开启TX命名空间才可以使用(事务使用)-->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<!-- 配置通知的相关属性-->
<!-- 通知就是具体的增强代码-->
<tx:attributes>
<!-- 之前讲的所有属性都可以在这个标签配置-->
<tx:method name="transfer" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="modify*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="query*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="select*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="find*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
</tx:attributes>
</tx:advice>
<!-- 配置切面-->
<aop:config>
<!-- 配置切点-->
<aop:pointcut id="transactionPointCut" expression="execution(* com.powernode.bank..*(..))"/>
<!-- 配置切面(通知加上切点)-->
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="transactionPointCut"></aop:advisor>
</aop:config>
</beans>
<?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 http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:13306/spring6"></property>
<property name="username" value="root"></property>
<property name="password" value="abc123"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<context:component-scan base-package="com.powernode.bank"></context:component-scan>
<!-- 配置通知,需要TX命名空间,开启TX命名空间才可以使用(事务使用)-->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<!-- 配置通知的相关属性-->
<!-- 通知就是具体的增强代码-->
<tx:attributes>
<!-- 之前讲的所有属性都可以在这个标签配置-->
<tx:method name="transfer" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="modify*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="query*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="select*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="find*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
</tx:attributes>
</tx:advice>
<!-- 配置切面-->
<aop:config>
<!-- 配置切点-->
<aop:pointcut id="transactionPointCut" expression="execution(* com.powernode.bank..*(..))"/>
<!-- 配置切面(通知加上切点)-->
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="transactionPointCut"></aop:advisor>
</aop:config>
</beans>
package com.powernode.bank.Test;
import com.powernode.bank.service.AccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test
{
private static final Logger logger = LoggerFactory.getLogger(Test.class);
@org.junit.Test
public void TestXMLTransaction()
{
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
AccountService accountService = context.getBean("accountService", AccountService.class);
try
{
accountService.transfer("act-002","act-001",10000);
logger.info("转账成功");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
package com.powernode.bank.Test;
import com.powernode.bank.service.AccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test
{
private static final Logger logger = LoggerFactory.getLogger(Test.class);
@org.junit.Test
public void TestXMLTransaction()
{
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
AccountService accountService = context.getBean("accountService", AccountService.class);
try
{
accountService.transfer("act-002","act-001",10000);
logger.info("转账成功");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}