目录
2、JdbcTemplate获取Connection并验证是否为同一个
1、案例环境
AccountServiceImpl
package shiwu;
import java.util.List;
import rumen.Account;
public class AccountServiceImpl implements IAccountService{
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
Account a1 = accountDao.findByName(sourceName);
Account a2 = accountDao.findByName(targetName);
a1.setMoney(a1.getMoney()-money);
a2.setMoney(a2.getMoney()+money);
accountDao.update(a1);
//int i = 1/0;
accountDao.update(a2);
}
@Override
public List<Account> findAll() {
List<Account> list = accountDao.findAll();
return list;
}
}
AccountDaoImpl
package shiwu;
import java.util.List;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import rumen.Account;
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{
@Override
public Account findByName(String name) {
List<Account> list = getJdbcTemplate().query("select * from account where name = ?",
new BeanPropertyRowMapper<Account>(Account.class),name );
return list.get(0);
}
@Override
public void update(Account a) {
getJdbcTemplate().update("update account set money = ? where name = ?",a.getMoney(),a.getName());
}
@Override
public List<Account> findAll() {
List<Account> list = getJdbcTemplate().query("select * from account",
new BeanPropertyRowMapper<Account>(Account.class));
return list;
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountService" class="shiwu.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<bean id="accountDao" class="shiwu.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="mysql"></property>
</bean>
</beans>
AccountTest
package shiwu;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import rumen.Account;
public class AccountTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("shiwu/bean.xml");
IAccountService as = ac.getBean("accountService",IAccountService.class);
as.transfer("aa", "bb", 1000f);
List<Account> list = as.findAll();
for(Account account : list) {
System.out.println(account);
}
}
}
2、JdbcTemplate获取Connection并验证是否为同一个
package shiwu;
import java.sql.Connection;
import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.datasource.DataSourceUtils;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("shiwu/bean.xml");
//获取数据源
DataSource ds = ac.getBean("dataSource",DataSource.class);
//根据spring提供的的工具根据数据源获取connection
Connection c1 = DataSourceUtils.getConnection(ds);
Connection c2 = DataSourceUtils.getConnection(ds);
System.out.println(c1==c2);//false
}
}
结果:false,不是同一个
问题:想要完成事务,conn必须是同一个,要把conn和线程绑定
解决:
package shiwu;
import java.sql.Connection;
import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("shiwu/bean.xml");
//获取数据源
DataSource ds = ac.getBean("dataSource",DataSource.class);
//通过sprin提供的事务同步管理器对象把Connection和线程绑定
TransactionSynchronizationManager.initSynchronization();
//根据spring提供的的工具根据数据源获取connection
Connection c1 = DataSourceUtils.getConnection(ds);
Connection c2 = DataSourceUtils.getConnection(ds);
System.out.println(c1==c2);//true
}
}
3、添加事务控制
package shiwu;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import rumen.Account;
public class AccountServiceImpl implements IAccountService{
private IAccountDao accountDao;
private DataSource dataSource;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
Connection conn = null;
try {
TransactionSynchronizationManager.initSynchronization();
conn = DataSourceUtils.getConnection(dataSource);
conn.setAutoCommit(false);
Account a1 = accountDao.findByName(sourceName);
Account a2 = accountDao.findByName(targetName);
a1.setMoney(a1.getMoney()-money);
a2.setMoney(a2.getMoney()+money);
accountDao.update(a1);
//int i = 1/0;
accountDao.update(a2);
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
@Override
public List<Account> findAll() {
List<Account> list = accountDao.findAll();
return list;
}
}
<bean id="accountService" class="shiwu.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountDao" class="shiwu.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="mysql"></property>
</bean>
4、事务传播行为
REQUIRED(required):如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入到这个事务中,也就有了。(默认)
SUPPORTS(supports):支持当前事务,如果当前没有事务,就以非事务方式执行。
5、Spring提供的API实现事务控制
package shiwu;
import java.util.List;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import rumen.Account;
public class AccountServiceImpl implements IAccountService{
private IAccountDao accountDao;
private PlatformTransactionManager manager;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
public void setManager(PlatformTransactionManager manager) {
this.manager = manager;
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
TransactionStatus status = null;
try {
DefaultTransactionDefinition df = new DefaultTransactionDefinition();
//设置定义信息:隔离级别 传播行为 过期时间 是否只读
df.setTimeout(-1);
df.setReadOnly(false);
df.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
status = manager.getTransaction(df);
Account a1 = accountDao.findByName(sourceName);
Account a2 = accountDao.findByName(targetName);
a1.setMoney(a1.getMoney()-money);
a2.setMoney(a2.getMoney()+money);
accountDao.update(a1);
//int i = 1/0;
accountDao.update(a2);
//提交事务
manager.commit(status);
} catch (Exception e) {
manager.rollback(status);
e.printStackTrace();
}
}
@Override
public List<Account> findAll() {
List<Account> list = accountDao.findAll();
return list;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountService" class="shiwu.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="manager" ref="manager"></property>
</bean>
<bean id="accountDao" class="shiwu.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="mysql"></property>
</bean>
<!-- 配置事务控制管理器 -->
<bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
6、使用事务控制模板
package shiwu;
import java.util.List;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import rumen.Account;
public class AccountServiceImpl implements IAccountService{
private IAccountDao accountDao;
private TransactionTemplate template;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
public void setTemplate(TransactionTemplate template) {
this.template = template;
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
template.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus arg0) {
Account a1 = accountDao.findByName(sourceName);
Account a2 = accountDao.findByName(targetName);
a1.setMoney(a1.getMoney()-money);
a2.setMoney(a2.getMoney()+money);
accountDao.update(a1);
//int i = 1/0;
accountDao.update(a2);
return null;
}
});
}
@Override
public List<Account> findAll() {
List<Account> list = accountDao.findAll();
return list;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountService" class="shiwu.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="template" ref="transactionTemplate"></property>
</bean>
<bean id="accountDao" class="shiwu.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="mysql"></property>
</bean>
<!-- 配置事务控制管理器 -->
<bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务控制模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="manager"></property>
</bean>
</beans>
7、Spring基于xml声明式事务控制
用 tx的advice配置事务的通知,设置属性时至少要用2个 method标签,用于增删改查。
设置 method 的 name 属性时,可以用 * 或者类似于 find* 格式,用后者要注意,只能用于find开头的方法
事务属性的配置:
1.isolation="DEFAULT":用于指定事务的隔离级别。默认值是:DEFAULT。表示数据库的隔离级别。
2.read-only="false" :用于指定事务是否只读。默认值是:false,不只读。只有查询方法才会为true。
3.timeout="-1" :用于只读事务的过期时间。默认值是:-1,表示永不过期。如果设了值,则以秒为单位。
4.propagation="REQUIRED" :用于指定事务的传播行为。默认值是:REQUIRED,表示一定有事务,一般增删改使用。当查询时用SUPPORTS。
5.rollback-for="":用于指定要用一个异常,当产生该异常时,事务回滚。产生其他异常时,事务不回滚。没有默认值,表示任何异常都回滚。
6.no-rollback-for="":用于指定一个异常,当产生该异常时,事务不回滚。 产生其他异常时,事务回滚。没有默认值,表示任何异常都回滚。
<?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: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/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="accountService" class="shengming.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="manager" ref="manager"></property>
</bean>
<bean id="accountDao" class="shengming.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="mysql"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 使用tx名称空间下的标签配置事务的通知 -->
<tx:advice id="txAdvice" transaction-manager="manager">
<tx:attributes>
<!--
配置属性:(用到哪个,配置哪个)
isolation="DEFAULT"
read-only="false"
timeout="-1"
propagation="REQUIRED"
rollback-for=""
no-rollback-for=""
-->
<tx:method name="*" read-only="false" propagation="REQUIRED"/><!-- 增删改 -->
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/><!-- 查 -->
</tx:attributes>
</tx:advice>
<!-- 配置aop -->
<aop:config>
<aop:pointcut expression="execution(* shengming.*.*(..)) " id="pt"/>
<!-- 建立切入点表达式与事务通知的关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
</beans>
8、Spring基于注解声明式事务控制
AccountServiceImp
package shengming;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import rumen.Account;
@Service("accountService")
@Transactional
public class AccountServiceImpl implements IAccountService{
@Autowired
private IAccountDao accountDao;
@Autowired
private PlatformTransactionManager manager;
@Override
public void transfer(String sourceName, String targetName, Float money) {
TransactionStatus status = null;
try {
DefaultTransactionDefinition df = new DefaultTransactionDefinition();
df.setTimeout(-1);
df.setReadOnly(false);
df.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
status = manager.getTransaction(df);
Account a1 = accountDao.findByName(sourceName);
Account a2 = accountDao.findByName(targetName);
a1.setMoney(a1.getMoney()-money);
a2.setMoney(a2.getMoney()+money);
accountDao.update(a1);
//int i = 1/0;
accountDao.update(a2);
manager.commit(status);
}catch (Exception e) {
manager.rollback(status);
e.printStackTrace();
}
}
@Override
public List<Account> findAll() {
List<Account> list = accountDao.findAll();
return list;
}
}
AccountDaoImpl
package shengming;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import rumen.Account;
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Account findByName(String name) {
List<Account> list = jdbcTemplate.query("select * from account where name = ?",
new BeanPropertyRowMapper<Account>(Account.class),name );
return list.get(0);
}
@Override
public void update(Account a) {
jdbcTemplate.update("update account set money = ? where name = ?",a.getMoney(),a.getName());
}
@Override
public List<Account> findAll() {
List<Account> list = jdbcTemplate.query("select * from account",
new BeanPropertyRowMapper<Account>(Account.class));
return list;
}
}
<?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:tx="http://www.springframework.org/schema/tx"
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/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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="shengming"></context:component-scan>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="mysql"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启spring对注解事务的支持,并引用事务管理器 -->
<tx:annotation-driven transaction-manager="manager"/>
</beans>
9、Transactional的使用
该注解可以写在类、方法、接口上。
优先级:就近原则,方法>类>接口
注:用注解的步骤要比xml少,但是常用的是xml配置。
原因:xml配置一次,当前项目中的所有业务都能使用