JDBC模板
xml中配置事务管理,aop的约束
<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">
</beans>
引入jar包(声明式事务管理用到aop的jar)后,
在xml中配置连接池(内置的连接池,DBCP,C3P0) 和JDBC模板
<!-- 配置Spring的jdbc模板================================================= -->
<bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="DataSource"></property>
</bean>
<!-- 配置Spring内置连接池============================== -->
<!--
<bean id="DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
属性注入
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring4_day03?useSSL=true&useUnicode=true&characterEncoding=UTF-8"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
-->
<!-- 配置DBCP连接池=================================== -->
<!--
<bean id="DataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring4_day03?useSSL=true&useUnicode=true&characterEncoding=UTF-8"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
-->
<!-- 引入属性文件======================================= -->
<!-- 通过context标签引入 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置c3p0连接池========================================== -->
<bean id="DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 直接写
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///spring4_day03?useSSL=true&useUnicode=true&characterEncoding=UTF-8"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
-->
<!-- 借用引入的属性文件 -->
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
jdbc属性文件
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring4_day03?useSSL=true&useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root
使用单元测试,测试JDBC模板
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcDemo2 {
@Resource(name="JdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Test
//保存操作
public void test1(){
jdbcTemplate.update("insert into account values(null,?,?)","张如花",400d);
}
@Test
//修改操作
public void test2(){
jdbcTemplate.update("update account set name = ?,money = ? where id = ?","张如花",500d,6);
}
@Test
//删除操作
public void test3(){
jdbcTemplate.update("delete from account where id = ?",7);
}
@Test
//查询操作
public void test4(){
String name = jdbcTemplate.queryForObject("select name from account where id = ?",String.class,6);
System.out.println(name);
}
@Test
//统计查询
public void test5(){
Long count = jdbcTemplate.queryForObject("select count(*) from account ",Long.class);
System.out.println(count);
}
@Test
//封装到对象中
public void test6(){
Account account = jdbcTemplate.queryForObject("select *from account where id = ? ",new MyRowMapper(),6);
System.out.println(account);
}
class MyRowMapper implements RowMapper<Account>{
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account(rs.getInt("id"),rs.getString("name"), rs.getDouble("money"));
return account;
}
}
}
class Account {
private Integer id;
private String name;
private Double money;
public Account() {
}
public Account(Integer id, String name, Double money) {
super();
this.id = id;
this.name = name;
this.money = money;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
}
}
事务管理
public interface AccountDao {
public void outMoney(String from,Double money);
public void inMoney(String to,Double money);
}
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{
@Override
public void outMoney(String from, Double money) {
this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from);
}
@Override
public void inMoney(String to, Double money) {
this.getJdbcTemplate().update("update account set money = money + ? where name = ?",money,to);
}
}
public interface AccountService {
public void transfer(String from,String to,Double money);
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer( String from, String to, Double money) {
accountDao.outMoney(from, money);
//设置除0异常
int d = 1/0;
accountDao.inMoney(to, money);
}
}
一个转账的实例,dao中完成对数据库的操作
dao实现类继承JdbcDaoSupport,JdbcDaoSupport源码中
只要传入一个 连接池,就会自动创建JdbcTemplate,而且JdbcDaoSupport这个类中都有相应的set方法,所以直接在xml中注入连接池就行。
<!-- 配置连接池 -->
<!-- 通过context标签引入属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置c3p0连接池 -->
<bean id="DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置service和dao -->
<bean id="AccountDao" class="txDemo1_编程式.AccountDaoImpl">
<property name="dataSource" ref="DataSource"></property>
</bean>
<bean id="AccountService" class="txDemo1_编程式.AccountServiceImpl">
<property name="accountDao" ref="AccountDao"></property>
</bean>
没有进行事务管理时,进行如下测试,由于AccountServiceImpl 中设置了除零异常,收账的没收到,转账的扣钱了
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx1.xml")
public class SpringDemo1 {
@Resource(name="AccountService")
private AccountService accountService;
@Test
public void test1(){
accountService.transfer("aaa", "bbb", 700d);
}
编程式
在xml中配置事务管理器和事务管理的模板
DataSourceTransactionManager底层使用jdbc管理事务
<!-- 配置平台事务管理器==================== -->
<bean id="TransactionManager" 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="TransactionManager"></property>
</bean>
在AccountServiceImpl中设置一个transactionTemplate属性,执行它的excute方法
将可能发生异常的代码写到内部类重写的方法中就可以解决问题
public class AccountServiceImpl implements AccountService {
//注入dao
private AccountDao accountDao;
//注入事务管理的模板
private TransactionTemplate transactionTemplate;
@Override
public void transfer(final String from,final String to,final Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
accountDao.outMoney(from, money);
int i = 1 / 0;
accountDao.inMoney(to, money);
}
});
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void setAccountDao(AccountDao accountDao){
this.accountDao = accountDao;
}
}
声明式
xml方式(aop)
配置事务管理器
<!-- 配置事务管理器 -->
<bean id="TransactonManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="DataSource"></property>
</bean>
<!-- 配置事务的增强 -->
<tx:advice id="txAdvice" transaction-manager="TransactonManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- aop的配置 -->
<aop:config>
<aop:pointcut expression="execution(* txDemo2_声明式_xml.AccountServiceImpl.*(..))" id="pointcut1"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
其中,propagation设置的是事务的传播行为
Spring中提供了七种事务的传播行为:
保证多个操作在同一个事务中
PROPAGATION_REQUIRED
:默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
PROPAGATION_SUPPORTS
:支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务。
PROPAGATION_MANDATORY
:如果A中有事务,使用A中的事务。如果A没有事务,抛出异常。
保证多个操作不在同一个事务中
PROPAGATION_REQUIRES_NEW
:如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作。
PROPAGATION_NOT_SUPPORTED
:如果A中有事务,将A的事务挂起。不使用事务管理。
PROPAGATION_NEVER
:如果A中有事务,报异常。
嵌套式事务
PROPAGATION_NESTED
:嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。
注解方式
配置事务管理器
<!-- 配置事务管理器 -->
<bean id="TransactonManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="DataSource"></property>
</bean>
开启注解事务
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="TransactonManager"/>
在需要目标类(AccountServiceImpl )上加一个@Transactional注解,这个注解上也可以设置传播行为等属性
@Transactional
public class AccountServiceImpl implements AccountService {
//注入dao
private AccountDao accountDao;
@Override
public void transfer(String from, String to, Double money) {
accountDao.outMoney(from, money);
int i = 1 / 0;
accountDao.inMoney(to, money);
}
public void setAccountDao(AccountDao accountDao){
this.accountDao = accountDao;
}
}