Spring 中的 JdbcTemplate以及事务控制(基于注解和xml)

Spring 中的 JdbcTemplate

它是 spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装。spring 框架为我们提供了很多的操作模板类。

操作关系型数据的:JdbcTemplate 、HibernateTemplate

操作 nosql 数据库的:RedisTemplate

操作消息队列的:JmsTemplate

public JdbcTemplate() { 
} 
public JdbcTemplate(DataSource dataSource) { 
    setDataSource(dataSource); 
    afterPropertiesSet(); 
} 
public JdbcTemplate(DataSource dataSource, boolean lazyInit) { 
    setDataSource(dataSource); 
    setLazyInit(lazyInit); 
    afterPropertiesSet(); 
}

除了默认构造函数之外,都需要提供一个数据源。并且都有set方法,可以使用依赖注入。

spring 中配置数据源

环境搭建,导入依赖

编写 spring 的配置文件

<?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">
</beans>

配置数据源

配置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:///spring_day02"></property> 
    <property name="user" value="root"></property>
	<property name="password" value="1234"></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:// /spring_day02"></property> 
    <property name="username" value="root"></property> 
    <property name="password" value="1234"></property>
</bean>

配置 spring 内置数据源

spring 框架也提供了一个内置数据源,我们也可以使用 spring 的内置数据源,它就在spring-jdbc-5.0.2.REEASE.jar 包中:

<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> 
    <property name="url" value="jdbc:mysql:///spring_day02"></property> 
    <property name="username" value="root"></property> 
    <property name="password" value="1234"></property>
</bean>

将数据库连接的信息配置到属性文件中

【定义属性文件】

jdbc.driverClass=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql:///spring_day02

jdbc.username=root

jdbc.password=123

【引入外部的属性文件】

一种方式:

 <!-- 引入外部属性文件: -->
 <bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
     <property name="location" value="classpath:jdbc.properties"/> 
 </bean>

另一种方式:

<context:property-placeholder location="classpath:jdbc.properties"/>

JdbcTemplate 的增删改查操作

spring 配置文件中配置 JdbcTemplate

<?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"> 
    
    <!-- 配置一个数据库的操作模板:JdbcTemplate --> 
    <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.jdbc.Driver"></property> 
        <property name="url" value="jdbc:mysql:///spring_day02"></property> 
        <property name="username" value="root"></property> 
        <property name="password" value="1234"></property> 
    </bean>
</beans>

最基本使用

public class JdbcTemplateDemo2 { 
	public static void main(String[] args) { 
        //1.获取 Spring 容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 
        //2.根据 id 获取 bean 对象
        JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate"); 
        //3.执行操作
        jt.execute("insert into account(name,money)values('eee',500)");
    } 
}

保存操作

public class JdbcTemplateDemo3 { 
	public static void main(String[] args) { 
        //1.获取 Spring 容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 
        //2.根据 id 获取 bean 对象
        JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate"); 
        //3.执行操作
        //保存
        jt.update("insert into account(name,money)values(?,?)","fff",5000); 
    } 
}

更新操作

public class JdbcTemplateDemo3 { 
    public static void main(String[] args) { 
        //1.获取 Spring 容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 
        //2.根据 id 获取 bean 对象
        JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate"); 
        //3.执行操作
        //修改
        jt.update("update account set money = money-? where id = ?",300,6); 
    } 
}

删除操作

public class JdbcTemplateDemo3 { 
    public static void main(String[] args) { 
        //1.获取 Spring 容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 
        //2.根据 id 获取 bean 对象
        JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate"); 
        //3.执行操作
        //删除
        jt.update("delete from account where id = ?",6); 
    } 
}

查询所有操作

public class JdbcTemplateDemo3 { 
    public static void main(String[] args) { 
        //1.获取 Spring 容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 
        //2.根据 id 获取 bean 对象
        JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate"); 
        //3.执行操作
        //查询所有
        List<Account> accounts = jt.query("select * from account where money > ? ",new AccountRowMapper(), 500); 
        for(Account o : accounts){ 
        	System.out.println(o); 
        } 
    } 
} 

public class AccountRowMapper implements RowMapper<Account>{
    @Override 
    public Account mapRow(ResultSet rs, int rowNum) throws SQLException { 
        Account account = new Account(); 
        account.setId(rs.getInt("id")); 
        account.setName(rs.getString("name")); 
        account.setMoney(rs.getFloat("money")); 
        return account; 
    }
}

查询一个操作

//使用 RowMapper 的方式:常用的方式
public class JdbcTemplateDemo3 { 
    public static void main(String[] args) { 
        //1.获取 Spring 容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 
        //2.根据 id 获取 bean 对象
        JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate"); 
        //3.执行操作
        //查询一个
        List<Account> as = jt.query("select * from account where id = ? ", new AccountRowMapper(), 55);
        System.out.println(as.isEmpty()?"没有结果":as.get(0)); 
    } 
} 

//使用 ResultSetExtractor 的方式:不常用的方式
public class JdbcTemplateDemo3 { 
    public static void main(String[] args) { 
        //1.获取 Spring 容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 
        //2.根据 id 获取 bean 对象
        JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate"); 
        //3.执行操作
        //查询一个
        Account account = jt.query("select * from account where id = ?", new AccountResultSetExtractor(),3); 
        System.out.println(account); 
    } 
}

查询返回一行一列操作

public class JdbcTemplateDemo3 { 
    public static void main(String[] args) { 
        //1.获取 Spring 容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 
        //2.根据 id 获取 bean 对象
        JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate"); 
        //3.执行操作
        //查询返回一行一列:使用聚合函数,在不使用 group by 字句时,都是返回一行一列。最长用的就是分页中获取总记录条数
        Integer total = jt.queryForObject("select count(*) from account where money > ? ", Integer.class,500);
        System.out.println(total); 
    } 
}

dao 中使用 JdbcTemplate

第一种方式:在 dao 中定义 JdbcTemplate

/** 
* 账户的接口
*/ 
public interface IAccountDao {
    /** 
    * 根据 id 查询账户信息
    * @param id
    * @return
    */
    Account findAccountById(Integer id); 
    /** 
    * 根据名称查询账户信息
    * @return
    */
    Account findAccountByName(String name); 
    /** 
    * 更新账户信息
    * @param account 
    */
    void updateAccount(Account account); 
} 

/** 
* 账户的持久层实现类
* 此版本的 dao,需要给 dao 注入 JdbcTemplate 
*/ 
public class AccountDaoImpl implements IAccountDao {
    private JdbcTemplate jdbcTemplate; 
    
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 
    	this.jdbcTemplate = jdbcTemplate; 
    } 
    
    @Override 
    public Account findAccountById(Integer id) {
        List<Account> list = jdbcTemplate.query("select * from account where id = ? ",new AccountRowMapper(),id);
        return list.isEmpty()?null:list.get(0); 
    } 
    @Override 
    public Account findAccountByName(String name) {
        List<Account> list = jdbcTemplate.query("select * from account where name = ? ",new AccountRowMapper(),name); 
        if(list.isEmpty()){ 
        	return null; 
        } 
        if(list.size()>1){ 
        	throw new RuntimeException("结果集不唯一,不是只有一个账户对象"); 
        } 
        return list.get(0); 
    } 
    @Override 
    public void updateAccount(Account account) { 
   	 	jdbcTemplate.update("update account set money = ? where id = ? ",account.getMoney(),account.getId()); 
    } 
}
<?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"> 
    <!-- 配置一个 dao --> 
    <bean id="accountDao" class="com.dao.impl.AccountDaoImpl"> 
        <!-- 注入 jdbcTemplate --> 
        <property name="jdbcTemplate" ref="jdbcTemplate"></property> 
    </bean> 
    
    <!-- 配置一个数据库的操作模板:JdbcTemplate --> 
    <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.jdbc.Driver"></property> 
        <property name="url" value="jdbc:mysql:///spring_day04"></property> 
        <property name="username" value="root"></property> 
        <property name="password" value="1234"></property>
    </bean> 
</beans>

第二种方式:让 dao 继承 JdbcDaoSupport

JdbcDaoSupport 是 spring 框架为我们提供的一个类,该类中定义了一个 JdbcTemplate 对象,我们可以直接获取使用,但是要想创建该对象,需要为其提供一个数据源.

public abstract class JdbcDaoSupport extends DaoSupport { 
    //定义对象
    private JdbcTemplate jdbcTemplate; 
    
    //set 方法注入数据源,判断是否注入了,注入了就创建 JdbcTemplate 
    public final void setDataSource(DataSource dataSource) { 
        if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) { //如果提供了数据源就创建 JdbcTemplate
            this.jdbcTemplate = createJdbcTemplate(dataSource); 
            initTemplateConfig(); 
        } 
    } 
    //使用数据源创建 JdcbTemplate
    protected JdbcTemplate createJdbcTemplate(DataSource dataSource) { 
    	return new JdbcTemplate(dataSource); 
    } 
    //当然,我们也可以通过注入 JdbcTemplate 对象
    public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    	this.jdbcTemplate = jdbcTemplate;
    	initTemplateConfig();
    }
    //使用 getJdbcTmeplate 方法获取操作模板对象
    public final JdbcTemplate getJdbcTemplate() {
    return this.jdbcTemplate; 
    }
}
/**
* 账户的接口
*/
public interface IAccountDao {
    /**
    * 根据 id 查询账户信息
    * @param id
    * @return
    */
    Account findAccountById(Integer id);
    /**
    * 根据名称查询账户信息
    * @return
    */
    Account findAccountByName(String name);
    /**
    * 更新账户信息
    * @param account
    */
    void updateAccount(Account account);
}

/**
* 账户的持久层实现类
* 此版本 dao,只需要给它的父类注入一个数据源
*/
public class AccountDaoImpl2 extends JdbcDaoSupport implements IAccountDao {
    @Override
    public Account findAccountById(Integer id) {
        //getJdbcTemplate()方法是从父类上继承下来的。
        List<Account> list = getJdbcTemplate().query("select * from account where id = ? ",new AccountRowMapper(),id);
        return list.isEmpty()?null:list.get(0); 
    } 
    @Override 
    public Account findAccountByName(String name) {
        //getJdbcTemplate()方法是从父类上继承下来的。
        List<Account> list = getJdbcTemplate().query("select * from account where name = ? ",new AccountRowMapper(),name);
        if(list.isEmpty()){ 
        	return null; 
        } 
        if(list.size()>1){ 
        	throw new RuntimeException("结果集不唯一,不是只有一个账户对象"); 
        } 
        return list.get(0); 
    } 
    @Override 
    public void updateAccount(Account account) { 
    	//getJdbcTemplate()方法是从父类上继承下来的。
    	getJdbcTemplate().update("update account set money = ? where id = ? ",account.getMoney(),account.getId()); 
    } 
}
<?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"> 
    
    <!-- 配置 dao2 --> 
    <bean id="accountDao2" class="com.dao.impl.AccountDaoImpl2">
        <!-- 注入 dataSource --> 
        <property name="dataSource" ref="dataSource"></property> 
    </bean>
    <!-- 配置数据源 --> 
    <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> 
        <property name="url" value="jdbc:mysql:///spring_day04"></property> 
        <property name="username" value="root"></property>
        <property name="password" value="1234"></property>
    </bean>
</beans>

两版 Dao 有什么区别呢?

第一种在 Dao 类中定义 JdbcTemplate 的方式,适用于所有配置方式(xml和注解都可以)。

第二种让 Dao 继承 JdbcDaoSupport 的方式,只能用于基于 XML 的方式,注解用不了。

Spring 中的事务控制

第一:JavaEE 体系进行分层开发,事务处理位于业务层,Spring 提供了分层设计业务层的事务处理解决方案。

第二:spring 框架为我们提供了一组事务控制的接口。这组接口是在spring-tx-5.0.2.RELEASE.jar 中。

第三:spring 的事务控制都是基于 AOP 的,它既可以使用编程的方式实现,也可以使用配置的方式现。

Spring 中事务控制的 API 介绍

PlatformTransactionManager

此接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法,

PlatformTransactionManager接口提供事务操作的方法,包含有3个具体的操作

  • TransactionStatus getTransaction(TransactionDefinitiondefinition)——获取事务状态信息
  • void commit(TransactionStatus status)——提交事务
  • void rollback(TransactionStatus status)——回滚事务

我们在开发中都是使用它的实现类,真正管理事务的对象
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用 Spring JDBC 或 iBatis 进行持久化数据时使用,org.springframework.orm.hibernate5.HibernateTransactionManager 使用Hibernate 版本进行持久化数据时使用

TransactionDefinition

它是事务的定义信息对象

  • String getName()——获取事务对象名称

  • int getlsolationLevel()——获取事务隔离级

  • int getPropagationBehavior()——获取事务传播行为

  • int getTimeout()——获取事务超时时间

  • boolean isReadOnly()——获取事务是否只读

读写型事务:增加、删除、修改开启事务。

只读型事务:执行查询时,也会开启事务。

事务的隔离级别

事务隔离级反映事务提交并发访问时的处理态度

  • ISOLATION_DEFAULT——默认级别,归属下列某一种

  • ISOLATION_READ_UNCOMMITTED——可以读取未提交数据

  • ISOLATION_READ_COMMITTED——只能读取已提交数据,解决脏读问题(Oracle默认级别)

  • ISOLATION_REPEATABLE_READ——是否读取其他事务提交修改后的数据,解决不可重复读
    问题(MySQL默认级别)

  • ISOLATION_SERIALIZABLE——是否读取其他事务提交添加后的数据,解决幻影读问题

事务的传播行为

REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)

SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)

MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常

REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。

NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起

NEVER:以非事务方式运行,如果当前存在事务,抛出异常

NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作。

超时时间

默认值是-1,没有超时限制。如果有,以秒为单位进行设置。

是否是只读事务

建议查询时设置为只读。

TransactionStatus

此接口提供的是事务具体的运行状态,

TransactionStatus接口描述了某个时间点上事务对象的状态信息,包含有6个具体的操作

  • void flush()——刷新事务
  • boolean hasSavepoint()——获取是否是否存在存储点
  • boolean isCompleted()——获取事务是否完成
  • boolean isNewTransaction()——获取事务是否为新的事务
  • boolean isRollbackOnly()——获取事务是否回滚
  • void setRollbackOnly()——设置事务回滚

基于 XML 的声明式事务控制(配置方式)

第一步:拷贝必要的 jar 包到工程的 lib 目录

第二步:创建 spring 的配置文件并导入约束

此处需要导入 aop 和 tx 两个名称空间

<?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/tx 
 http://www.springframework.org/schema/tx/spring-tx.xsd
 http://www.springframework.org/schema/aop 
 http://www.springframework.org/schema/aop/spring-aop.xsd"> 
</beans>

第三步:准备数据库表和实体类

第四步:编写业务层接口和实现类

第五步:编写 Dao 接口和实现类

/** 
* 账户的持久层接口
*/ 
public interface IAccountDao {
    /** 
    * 根据 id 查询账户信息
    * @param id
    * @return
    */
    Account findAccountById(Integer id); 
    /** 
    * 根据名称查询账户信息
    * @return
    */
    Account findAccountByName(String name);
    /**
    * 更新账户信息
    * @param account
    */
    void updateAccount(Account account);
}
/**
* 账户的持久层实现类
* 此版本 dao,只需要给它的父类注入一个数据源
*/
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
    @Override
    public Account findAccountById(Integer id) {
    	List<Account> list = getJdbcTemplate().query("select * from account where id = ? ",new AccountRowMapper(),id);
    	return list.isEmpty()?null:list.get(0);
    }
    @Override
    public Account findAccountByName(String name) {
        List<Account> list = getJdbcTemplate().query("select * from account where name = ? ",new AccountRowMapper(),name);
        if(list.isEmpty()){
        	return null; 
        }
        if(list.size()>1){
        	throw new RuntimeException("结果集不唯一,不是只有一个账户对象");
        }
        return list.get(0);
    }
                                                 
    @Override
    public void updateAccount(Account account) {
    	getJdbcTemplate().update("update account set money = ? where id = ? ",account.getMoney(),account.getId());
    } 
}
                         
/**
* 账户的封装类 RowMapper 的实现类
*/ 
public class AccountRowMapper implements RowMapper<Account>{
    @Override 
    public Account mapRow(ResultSet rs, int rowNum) throws SQLException { 
        Account account = new Account(); 
        account.setId(rs.getInt("id")); 
        account.setName(rs.getString("name")); 
        account.setMoney(rs.getFloat("money")); 
        return account; 
    } 
}

第六步:在配置文件中配置业务层和持久层对

<!-- 配置 service --> 
<bean id="accountService" class="com.service.impl.AccountServiceImpl"> 
	<property name="accountDao" ref="accountDao"></property> 
</bean>

<!-- 配置 dao --> 
<bean id="accountDao" class="com.dao.impl.AccountDaoImpl"> 
	<!-- 注入 dataSource --> 
	<property name="dataSource" ref="dataSource"></property> 
</bean>

<!-- 配置数据源 --> 
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> 
    <property name="url" value="jdbc:mysql:///spring_day04"></property> 
    <property name="username" value="root"></property> 
    <property name="password" value="1234"></property> 
</bean>

配置步骤

第一步:配置事务管理器

<!-- 配置一个事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入 DataSource --> 
	<property name="dataSource" ref="dataSource"></property> 
</bean>

第二步:配置事务的通知引用事务管理器

<!-- 事务的配置 --> 
<tx:advice id="txAdvice" transaction-manager="transactionManager"> 
</tx:advice>

第三步:配置事务的属性

<!--在 tx:advice 标签内部 配置事务的属性 --> 
<tx:attributes> 
<!-- 指定方法名称:是业务核心方法 
read-only:是否是只读事务。默认 false,不只读。
isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。 
propagation:指定事务的传播行为。
timeout:指定超时时间。默认值为:-1。永不超时。
rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常,事务不回滚。
没有默认值,任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回
滚。没有默认值,任何异常都回滚。
-->
    <tx:method name="*" read-only="false" propagation="REQUIRED"/> 
    <tx:method name="find*" read-only="true" propagation="SUPPORTS"/> 
</tx:attributes>

第四步:配置 AOP 切入点表达式

<!-- 配置 aop --> 
<aop:config> 
    <!-- 配置切入点表达式 -->
    <aop:pointcut expression="execution(* com.service.impl.*.*(..))" id="pt1"/>
</aop:config>

<aop:pointcut>标签定义了切入点,也就是告诉spring,我要在这个切入点范围内进行事物控制了

<tx:method>标签定义了具体进行事务管理的方法,这些方法才真正具有事务管理的属性,pointcut的范围应该包含 method

第五步:配置切入点表达式和事务通知的对应关系

<!-- 在 aop:config 标签内部:建立事务的通知和切入点表达式的关系 --> 
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
<?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/tx
 http://www.springframework.org/schema/tx/spring-tx.xsd
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="placeholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:jdbc.properties"></property>
    </bean>
    <bean id="accountBiz" class="com.sibd.biz.impl.AccountBizImpl">
        <property name="accountDao" ref="accountDao"></property>
   </bean>
   <bean id="accountDao" class="com.sibd.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
   </bean>

   <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
       <property name="user" value="${user}"></property>
       <property name="password" value="${pwd}"></property>
       <property name="driverClass" value="${class}"></property>
       <property name="jdbcUrl" value="${url}"></property>
   </bean>

    <!--配置声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <tx:advice id="advice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" isolation="SERIALIZABLE" read-only="false" propagation="REQUIRED"></tx:method>
            <tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="pc" expression="execution(* com.sibd.biz.impl.*.*(..))"/>
        <aop:advisor advice-ref="advice" pointcut-ref="pc"/>
    </aop:config>

</beans>

基于注解的配置方式

第一步:拷贝必备的 jar 包到工程的 lib 目录

第二步:创建 spring 的配置文件导入约束并配置扫描的包

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:context="http://www.springframework.org/schema/context"
 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
 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">
    <!-- 配置 spring 创建容器时要扫描的包 --> 
    <context:component-scan base-package="com"></context:component-scan> 
    <!-- 配置 JdbcTemplate-->
    <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://localhost:3306/spring_day02"></property> 
        <property name="username" value="root"></property> 
        <property name="password" value="1234"></property>
    </bean>
</beans>

第三步:创建数据库表和实体类

第四步:创建业务层接口和实现类并使用注解让 spring 管理

第五步:创建 Dao 接口和实现类并使用注解让 spring 管理

配置步骤

第一步:配置事务管理器并注入数据源

<!-- 配置事务管理器 --> 
<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="dataSource"></property> 
</bean>

第二步:在业务层使用@Transactional 注解

@Service("accountService")
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS) 
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountDao accountDao; 
    @Override 
    public Account findAccountById(Integer id) { 
    	return accountDao.findAccountById(id); 
    } 
    @Override 
    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    public void transfer(String sourceName, String targeName, Float money) { 
        //1.根据名称查询两个账户
        Account source = accountDao.findAccountByName(sourceName);
        Account target = accountDao.findAccountByName(targeName); 
        //2.修改两个账户的金额
        source.setMoney(source.getMoney()-money);//转出账户减钱
        target.setMoney(target.getMoney()+money);//转入账户加钱
        //3.更新两个账户
        accountDao.updateAccount(source); 
        //int i=1/0; 
        accountDao.updateAccount(target);
    } 
}

该注解的属性和 xml 中的属性含义一致。该注解可以出现在接口上,类上和方法上。

出现接口上,表示该接口的所有实现类都有事务支持。

出现在类上,表示类中所有方法有事务支持

出现在方法上,表示方法有事务支持。

以上三个位置的优先级:方法>类>接口

第三步:在配置文件中开启 spring 对注解事务的支持

<!-- 开启 spring 对注解事务的支持 --> 
<tx:annotation-driven transaction-manager="transactionManager"/>

不使用 xml 的配置方式

@Configuration
@EnableTransactionManagement 
public class SpringTxConfiguration { 
	//里面配置数据源,配置 JdbcTemplate,配置事务管理器。在之前的步骤已经写过了。
}
上一篇:mockjs数据占位符


下一篇:Dubbo3 源码系列 -- 环境准备