版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35537301/article/details/81747989
转账案例
dao层
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import com.itheima.domain.Account;
import com.itheima.rowmapper.MyRowMapper;
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void updateAcount(Account a) {
jdbcTemplate.update("update account set money=? where name=?", a.getMoney(), a.getName());
}
@Override
public Account findAccount(String name) {
List<Account> list = jdbcTemplate.query("select * from account where name = ?", new MyRowMapper(), name);
return list.isEmpty() ? null : list.get(0);
}
}
service层
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String fromName, String toName, float money) {
// 1.查询2个账户对象
Account fromAccount = accountDao.findAccount(fromName);
Account toAccount = accountDao.findAccount(toName);
// 2.计算余额
fromAccount.setMoney(fromAccount.getMoney() - money);
toAccount.setMoney(toAccount.getMoney() + money);
// 3.更新表数据
accountDao.updateAcount(fromAccount);
int i = 10 / 0;
accountDao.updateAcount(toAccount);
}
}
db.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring
jdbc.username=root
jdbc.password=root
applicationContext.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
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 加载jdbc.properties文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<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>
<!-- 使用JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="accountDao" class="com.itheima.dao.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="accountService" class="com.itheima.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
</beans>
测试类
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima.service.AccountService;
public class TestTransfer {
@Test
public void test01() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService as = (AccountService) ac.getBean("accountService");
as.transfer("tom", "jerry", 1000);
}
}
结果
事务
spring-tx-4.2.4.RELEASE.jar(事务)
概念
- 事务是逻辑上的一组操作,要么全都成功,要么全都失败
- 一组操作:多条sql语句不可分隔(在一个语句块)
- 如何保证多条语句在一个语句块:要使用同一个Connection连接
事务的特性
- 原子性:(事务内的操作是一个整体,不可分割);
- 一致性:(在事务开启之后,到事务提交之前,应该保证事务的操作不能改变);
- 隔离性:(一个事务在执行期间不能受到其他事务的干扰)
- 持久性:(事务一旦结束,结果就不能再次改变);
不考虑事务的安全性问题
- 读问题
- 脏读:一个事务读到了另一个事务未提交的数据。
- 不可重复读
- 一个事务读到了另一个事务提交的数据(update)
- 老汉原来卡里有1万,去银行存5000,银行业务员操作时,家人网购花了800,并提交,业务员提交,打印回执单发现,此时只有7000元
- 虚读/幻读:一个事务读到了另一个事务提交的数据(insert)
- 写问题
- 丢失更新
- 解决:悲观锁和乐观锁
事务的隔离级别
- 1 read uncommitted :未提交读.脏读,不可重复读,虚读都可能发生
- 2 read committed :已提交读.避免脏读.但是不可重复读和虚读有可能发生.(Oracle默认)
- 4 repeatable read :可重复读.避免脏读,不可重复读.但是虚读有可能发生.(MySql默认)
- 8 serializable :可解决所有问题
spring事务管理的API
平台事务管理器
- PlatformTransactionManager接口
- 真正管理事务的类
- 该接口有具体的实现类,根据不同的持久层框架,需要选择不同的实现类
- DataSourceTransactionManager :使用的Spring的JDBC模板或者MyBatis框架开发的时候,使用事务管理
- HibernateTransactionManager :Hibernate开发的时候,使用事务管理
- 该接口的常用方法
- void commit(TransactionStatus status)
- TransactionStatus getTransaction(TransactionDefinition definition)
- void rollback(TransactionStatus status)
事务定义信息
- TransactionDefinition接口
- 定义事务的隔离级别,传播行为,超时,只读
- 事务隔离级别的常量
- static int ISOLATION_DEFAULT -- 采用数据库的默认隔离级别
- static int ISOLATION_READ_UNCOMMITTED
- static int ISOLATION_READ_COMMITTED
- static int ISOLATION_REPEATABLE_READ
- static int ISOLATION_SERIALIZABLE
事务的状态
- TransactionStatus接口
- 定义事务是否是新事务,是否有保存点
API的关系
平台事务管理器根据事务定义信息(TransactionDefinition)中定义内容进行事务的管理,在管理事务的过程中就会产生一些事务的状态,将产生的状态保存到事务状态信息(TransactionStatus)中
事务的传播行为
PROPAGATION_REQUIRED :默认值。支持当前的事务,如果不存在,新建一个事务。
PROPAGATION_REQUIRES_NEW :有事务,挂起原有事务,新建一个事务。
PROPAGATION_NESTED :嵌套事务,执行完之前的操作的时候,设置一个保存点,如果后续代码没有错误,执行通过,如果有错误,允许用户自己设置回滚到保存点还是最初始状态。
XML方式管理事务
applicationContext.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
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">
<!-- 加载jdbc.properties文件 -->
<context:property-placeholder location="classpath:db.properties" />
<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>
<!-- 创建dao -->
<bean id="accountDao" class="com.itheima.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 创建service -->
<bean id="accountService" class="com.itheima.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置平台事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务的传播行为 ,事务的增强 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--
name :要绑定事务的方法名,可以使用通配符,可以配置多个
propagation :传播行为
isolation :隔离级别
read-only :是否只读
timeout :超时信息
rollback-for:发生哪些异常回滚
no-rollback-for:发生哪些异常不回滚
-->
<tx:method name="transfer" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.itheima.service.*.*(..))"
id="pointcut1" />
<!-- 织入 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1" />
</aop:config>
</beans>
dao层
import java.util.List;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.itheima.domain.Account;
import com.itheima.rowmapper.MyRowMapper;
/**
* @ClassName: AccountDaoImpl
* @Description:继承JdbcDaoSupport,只能用xml注入
* @author jsz
* @date 2018年8月16日
*/
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void updateAcount(Account a) {
this.getJdbcTemplate().update("update account set money=? where name=?", a.getMoney(), a.getName());
}
@Override
public Account findAccount(String name) {
List<Account> list = this.getJdbcTemplate().query("select * from account where name = ?", new MyRowMapper(), name);
return list.isEmpty() ? null : list.get(0);
}
}
service层
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String fromName, String toName, float money) {
// 1.查询2个账户对象
Account fromAccount = accountDao.findAccount(fromName);
Account toAccount = accountDao.findAccount(toName);
// 2.计算余额
fromAccount.setMoney(fromAccount.getMoney() - money);
toAccount.setMoney(toAccount.getMoney() + money);
// 3.更新表数据
accountDao.updateAcount(fromAccount);
int i = 10 / 0;
accountDao.updateAcount(toAccount);
}
}
测试类
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima.service.AccountService;
public class TestTransfer {
@Test
public void test01() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService as = (AccountService) ac.getBean("accountService");
as.transfer("tom", "jerry", 1000);
}
}
XML&&注解的方式配置事务
dao层
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.itheima.domain.Account;
import com.itheima.rowmapper.MyRowMapper;
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void updateAcount(Account a) {
jdbcTemplate.update("update account set money=? where name=?", a.getMoney(), a.getName());
}
@Override
public Account findAccount(String name) {
List<Account> list = jdbcTemplate.query("select * from account where name = ?", new MyRowMapper(), name);
return list.isEmpty() ? null : list.get(0);
}
}
service层
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
@Service("accountService")
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
@Transactional(readOnly=false,propagation=Propagation.REQUIRED)
public void transfer(String fromName, String toName, float money) {
// 1.查询2个账户对象
Account fromAccount = accountDao.findAccount(fromName);
Account toAccount = accountDao.findAccount(toName);
// 2.计算余额
fromAccount.setMoney(fromAccount.getMoney() - money);
toAccount.setMoney(toAccount.getMoney() + money);
// 3.更新表数据
accountDao.updateAcount(fromAccount);
int i = 10 / 0;
accountDao.updateAcount(toAccount);
}
}
applicationContext.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
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">
<!-- 配置spring注解要扫描的包 -->
<context:component-scan base-package="com.itheima"/>
<!-- 加载jdbc.properties文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 配置数据源 -->
<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"></property>
</bean>
<!-- 开启spring对注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
测试类
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima.service.AccountService;
public class TestTransfer {
@Test
public void test01() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService as = (AccountService) ac.getBean("accountService");
as.transfer("tom", "jerry", 1000);
}
}
纯注解方式配置事务
jar包
配置类
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@ComponentScan(basePackages = "com.itheima")
@EnableTransactionManagement // 开启spring对注解的支持
public class SpringConfiguration {
/**
* @MethodName:createDS
* @Description:配置数据源
* @return
* @throws Exception
*/
@Bean(name = "dataSource")
public DataSource createDS() throws Exception {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring");
return dataSource;
}
/**
* @MethodName:createTransactionManager
* @Description:配置事务管理器
* @param dataSource
* @return
*/
@Bean
public PlatformTransactionManager createTransactionManager(@Qualifier("dataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
/**
* @MethodName:createTemplate
* @Description:配置JDBC模板
* @param dataSource
* @return
*/
@Bean
public JdbcTemplate createTemplate(@Qualifier("dataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
dao层
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.itheima.domain.Account;
import com.itheima.rowmapper.MyRowMapper;
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void updateAcount(Account a) {
jdbcTemplate.update("update account set money=? where name=?", a.getMoney(), a.getName());
}
@Override
public Account findAccount(String name) {
List<Account> list = jdbcTemplate.query("select * from account where name = ?", new MyRowMapper(), name);
return list.isEmpty() ? null : list.get(0);
}
}
service层
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
@Service("accountService")
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
@Transactional(readOnly=false,propagation=Propagation.REQUIRED)
public void transfer(String fromName, String toName, float money) {
// 1.查询2个账户对象
Account fromAccount = accountDao.findAccount(fromName);
Account toAccount = accountDao.findAccount(toName);
// 2.计算余额
fromAccount.setMoney(fromAccount.getMoney() - money);
toAccount.setMoney(toAccount.getMoney() + money);
// 3.更新表数据
accountDao.updateAcount(fromAccount);
int i = 10 / 0;
accountDao.updateAcount(toAccount);
}
}
测试类
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.itheima.config.SpringConfiguration;
import com.itheima.service.AccountService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=SpringConfiguration.class)
public class TestTransfer {
@Autowired
private AccountService accountService;
@Test
public void test01() throws Exception {
accountService.transfer("tom", "jerry", 500);
}
}