Spring_four
基于XML的AOP实现事务控制
坐标xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
</dependencies>
删除AccountServiceTest测试类上的@Qualifier的注解,不产生代理对象
/**
* 使用Junit单元测试:测试我们的配置
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {
@Autowired
private AccountService as;
@Test
public void testTransfer(){
as.transfer("aaa","bbb",100f);
}
}
此时事务没有控制住
【配置文件】添加spring的aop
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<!-- 配置Service -->
<bean id="accountService" class="com.it.service.impl.AccountServiceImpl">
<!-- 注入dao -->
<property name="accountDao" ref="accountDao"></property>
<!--注入事务管理器
<property name="txManager" ref="txManager"></property>-->
</bean>
<!--配置Dao对象-->
<bean id="accountDao" class="com.it.dao.impl.AccountDaoImpl">
<!-- 注入QueryRunner -->
<property name="runner" ref="runner"></property>
<!-- 注入ConnectionUtils -->
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!--配置QueryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!--<constructor-arg name="ds" ref="dataSource"></constructor-arg>-->
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--连接数据库的必备信息-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/itcastspring"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 配置Connection的工具类 ConnectionUtils -->
<bean id="connectionUtils" class="com.it.utils.ConnectionUtils">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器-->
<bean id="txManager" class="com.it.utils.TransactionManager">
<!-- 注入ConnectionUtils -->
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.it.service..*.*(..))"></aop:pointcut>
<aop:aspect ref="txManager">
<!--配置前置通知:开启事务-->
<aop:before method="beginTransaction" pointcut-ref="pc"></aop:before>
<!--配置后置通知:提交事务-->
<aop:after-returning method="commitTransaction" pointcut-ref="pc"></aop:after-returning>
<!--配置异常通知:回滚事务-->
<aop:after-throwing method="rollbackTransaction" pointcut-ref="pc"></aop:after-throwing>
<!--配置最终通知:释放连接-->
<aop:after method="closeTransaction" pointcut-ref="pc"></aop:after>
</aop:aspect>
</aop:config>
</beans>
【注解】添加spring的aop
可以使用【前置通知】、【后置通知】、【异常通知】、【最终通知】
坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
</dependencies>
配置AccountServiceImpl.java的注解
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
}
配置AccountDaoImpl.java的注解
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private QueryRunner runner;
@Autowired
private ConnectionUtils connectionUtils;
}
配置ConnectionUtils.java的注解
@Component
public class ConnectionUtils {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
@Autowired
private DataSource dataSource;
}
配置TransactionManager.java的注解
@Component
@Aspect // 切面
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
@Pointcut(value = "execution(* com.it.service..*.*(..))") // 切入点
public void pc(){};
/**
* 开启事务
*/
@Before(value = "pc()")
public void beginTransaction(){
try {
System.out.println("前置通知");
connectionUtils.getThreadConnection().setAutoCommit(false);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 提交事务
*/
@AfterReturning(value = "pc()")
public void commit(){
try {
System.out.println("后置通知");
connectionUtils.getThreadConnection().commit();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 回滚事务
*/
@AfterThrowing(value = "pc()")
public void rollback(){
try {
System.out.println("异常通知");
connectionUtils.getThreadConnection().rollback();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 释放连接
*/
@After(value = "pc()")
public void release(){
try {
System.out.println("最终通知");
connectionUtils.getThreadConnection().close();//把连接还回连接池中
connectionUtils.removeConnection();
}catch (Exception e){
e.printStackTrace();
}
}
}
配置spring容器
配置applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解支持的组件扫描-->
<context:component-scan base-package="com.it"></context:component-scan>
<!--开启aop的注解支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--配置QueryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!--<constructor-arg name="ds" ref="dataSource"></constructor-arg>-->
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--连接数据库的必备信息-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/itcastspring"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
text
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {
@Autowired
private AccountService as;
@Test
public void testTransfer(){
as.transfer("aaa","bbb",100f);
}
}
但是发现,抛出异常。
因为注解的方式执行顺序是【前置通知】、【最终通知】、【异常通知】/【后置通知】
我们需要使用环绕通知解决问题。
配置TransactionManager.java
@Component
@Aspect
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
@Pointcut(value = "execution(* com.it.service..*.*(..))")
public void pc(){};
/**
* 开启事务
*/
//@Before(value = "pc()")
public void beginTransaction(){
try {
System.out.println("前置通知");
connectionUtils.getThreadConnection().setAutoCommit(false);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 提交事务
*/
//@AfterReturning(value = "pc()")
public void commit(){
try {
System.out.println("后置通知");
connectionUtils.getThreadConnection().commit();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 回滚事务
*/
//@AfterThrowing(value = "pc()")
public void rollback(){
try {
System.out.println("异常通知");
connectionUtils.getThreadConnection().rollback();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 释放连接
*/
//@After(value = "pc()")
public void release(){
try {
System.out.println("最终通知");
connectionUtils.getThreadConnection().close();//把连接还回连接池中
connectionUtils.removeConnection();
}catch (Exception e){
e.printStackTrace();
}
}
@Around(value="pc()")
public Object around(ProceedingJoinPoint joinPoint){
Object returnValue = null;
try {
this.beginTransaction(); // 开启事务
returnValue = joinPoint.proceed(joinPoint.getArgs());
this.commit(); // 提交事务
} catch (Throwable throwable) {
throwable.printStackTrace();
this.rollback(); // 回滚事务
}
finally {
this.release(); // 释放资源
}
return returnValue;
}
}
Spring中的JdbcTemplate
1dbcTemplate概述
它是spring框架中提供的一个对象,是对原始Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。
操作关系型数据的:
JdbcTemplate (操作JDBC,操作数据库)
HibernateTemplate (操作hibernate,操作数据库)
操作nosql数据库的: RedisTemplate(操作Redis,非关系型数据库)
操作消息队列(MQ)的: JmsTemplate (操作ActiveMQ,消息队列)
* 短信平台
* 邮件平台
操作索引库的:ElasticSearchTemplate(操作ElasticSearch,全文检索)
我们今天的主角在spring-jdbc-5.0.2.RELEASE.jar中,我们在导包的时候,除了要导入这个jar包外,还需要导入一个spring-tx-5.0.2.RELEASE.jar(它是和事务相关的)。
坐标xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
创建类Account.java
/**
* 账户的实体类
*/
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
}
创建类JdbcTemplateDemo1.java
使用spring提供的数据源
/**
* JdbcTemplate的最基本用法
*/
public class JdbcTemplateDemo1 {
public static void main(String[] args) {
//准备数据源:spring的内置数据源
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/itcastspring");
ds.setUsername("root");
ds.setPassword("root");
//1.创建JdbcTemplate对象
JdbcTemplate jt = new JdbcTemplate();
//给jt设置数据源
jt.setDataSource(ds);
//2.执行操作
jt.execute("insert into account(name,money)values('ccc',1000)");
}
}
2使用spring容器创建数据源和JdbcTemplate
配置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"
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://localhost:3306/itcastspring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
创建JdbcTemplateDemo2.java
/**
* JdbcTemplate的最基本用法
*/
public class JdbcTemplateDemo2 {
public static void main(String[] args) {
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.获取对象
JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);
//3.执行操作
jt.execute("insert into account(name,money)values('eee',2222)");
/* //准备数据源:spring的内置数据源
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/eesy");
ds.setUsername("root");
ds.setPassword("1234");
//1.创建JdbcTemplate对象
JdbcTemplate jt = new JdbcTemplate();
//给jt设置数据源
jt.setDataSource(ds);
//2.执行操作
jt.execute("insert into account(name,money)values('ccc',1000)");*/
}
}
3使用JdbcTemplate操作数据库的CRUD
创建测试类JdbcTemplateDemo3.java
/**
* JdbcTemplate的CRUD操作
*/
public class JdbcTemplateDemo3 {
public static void main(String[] args) {
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.获取对象
JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);
//3.执行操作
//保存
// jt.update("insert into account(name,money)values(?,?)","eee",3333f);
//更新
// jt.update("update account set name=?,money=? where id=?","test",4567,7);
//删除
// jt.update("delete from account where id=?",3);
//查询所有
// List<Account> accounts = jt.query("select * from account where money > ?",new AccountRowMapper(),1000f);
// List<Account> accounts = jt.query("select * from account where money > ?",new BeanPropertyRowMapper<Account>(Account.class),1000f);
// for(Account account : accounts){
// System.out.println(account);
// }
//查询一个
// List<Account> accounts = jt.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);
// System.out.println(accounts.isEmpty()?"没有内容":accounts.get(0));
// Account account = jt.queryForObject("select * from account where id = ?", new BeanPropertyRowMapper<Account>(Account.class),1);
// System.out.println(account);
//查询返回一行一列(使用聚合函数)
// Long count = jt.queryForObject("select count(*) from account where money > ?",Long.class,1000f);
// System.out.println(count);
}
}
/**
* 定义Account的封装策略(解决实体的属性和数据库的字段名称不一致)
*/
class AccountRowMapper implements RowMapper<Account> {
/**
* 把结果集中的数据封装到Account中,然后由spring把每个Account加到集合中
* @param rs
* @param rowNum
* @return
* @throws SQLException
*/
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;
}
}
4 JdbcTemplate操作Dao
创建AccountDao接口
/**
* 账户的持久层接口
*/
public interface AccountDao {
/**
* 根据Id查询账户
* @param accountId
* @return
*/
Account findAccountById(Integer accountId);
/**
* 根据名称查询账户
* @param accountName
* @return
*/
Account findAccountByName(String accountName);
/**
* 更新账户
* @param account
*/
void updateAccount(Account account);
}
创建接口的实现类AccountDaoImpl.java
/**
* 账户的持久层实现类
*/
public class AccountDaoImpl implements AccountDao {
JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Account findAccountById(Integer accountId) {
List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
return accounts.isEmpty()?null:accounts.get(0);
}
public Account findAccountByName(String accountName) {
List<Account> accounts = jdbcTemplate.query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
if(accounts.isEmpty()){
return null;
}
if(accounts.size()>1){
throw new RuntimeException("结果集不唯一");
}
return accounts.get(0);
}
public void updateAccount(Account account) {
jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置账户的持久层-->
<bean id="accountDao" class="com.it.dao.impl.AccountDaoImpl">
<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://localhost:3306/itcastspring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
test
/**
* JdbcTemplate的最基本用法
*/
public class JdbcTemplateDemo4 {
public static void main(String[] args) {
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.获取对象
AccountDao accountDao = ac.getBean("accountDao",AccountDao.class);
Account account = accountDao.findAccountById(1);
System.out.println(account);
account.setMoney(30000f);
accountDao.updateAccount(account);
}
}
5 Spring提供JdbcDaoSupport的使用
使用AccountDaoImpl继承JdbcDaoSupport,实现AccountDao
/**
* 账户的持久层实现类
*/
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
public Account findAccountById(Integer accountId) {
List<Account> accounts = super.getJdbcTemplate().query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
return accounts.isEmpty()?null:accounts.get(0);
}
public Account findAccountByName(String accountName) {
List<Account> accounts = super.getJdbcTemplate().query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
if(accounts.isEmpty()){
return null;
}
if(accounts.size()>1){
throw new RuntimeException("结果集不唯一");
}
return accounts.get(0);
}
public void updateAccount(Account account) {
super.getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置账户的持久层-->
<bean id="accountDao" class="com.it.dao.impl.AccountDaoImpl">
<!--<property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
<property name="dataSource" ref="dataSource"></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://localhost:3306/itcastspring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
test
/**
* JdbcTemplate的最基本用法
*/
public class JdbcTemplateDemo5 {
public static void main(String[] args) {
//1.获取容器
ApplicationContext ac = new
ClassPathXmlApplicationContext("applicationContext.xml");
//2.获取对象
AccountDao accountDao = ac.getBean("accountDao",AccountDao.class);
Account account = accountDao.findAccountById(1);
System.out.println(account);
account.setMoney(30000f);
accountDao.updateAccount(account);
}
}
6 使用JdbcDaoSupport的注解开发
创建AccountDao接口
/**
* 账户的持久层接口
*/
public interface AccountDao {
/**
* 根据Id查询账户
* @param accountId
* @return
*/
Account findAccountById(Integer accountId);
/**
* 根据名称查询账户
* @param accountName
* @return
*/
Account findAccountByName(String accountName);
/**
* 更新账户
* @param account
*/
void updateAccount(Account account);
}
创建AccountDaoImpl2实现AccountDao
/**
* 账户的持久层实现类
*/
@Repository("accountDao")
public class AccountDaoImpl2 extends JdbcDaoSupport implements AccountDao {
@Autowired // 放置到属性上,也可以放置到set方法上(将spring容器中创建的对象,通过set方法的形参传递给该方法)
public void setDi(DataSource dataSource){
super.setDataSource(dataSource);
}
public Account findAccountById(Integer accountId) {
List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
return accounts.isEmpty()?null:accounts.get(0);
}
public Account findAccountByName(String accountName) {
List<Account> accounts = jdbcTemplate.query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
if(accounts.isEmpty()){
return null;
}
if(accounts.size()>1){
throw new RuntimeException("结果集不唯一");
}
return accounts.get(0);
}
public void updateAccount(Account account) {
jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
}
}
applicationContext-anno.xml
重新创建applicationContext-anno.xml演示,表示使用注解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.it"></context:component-scan>
<!-- 配置数据源-->
<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/itcastspring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
test
/**
* JdbcTemplate的最基本用法,使用注解
*/
public class JdbcTemplateDemo5 {
public static void main(String[] args) {
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-anno.xml");
//2.获取对象
AccountDao accountDao = ac.getBean("accountDao",AccountDao.class);
Account account = accountDao.findAccountById(1);
System.out.println(account);
account.setMoney(30000f);
accountDao.updateAccount(account);
}
}
Spring中的事务控制
1.1 Spring事务控制我们要明确的
第一:JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。
第二:spring框架为我们提供了一组事务控制的接口 。具体在后面的第二小节介绍。这组接口是在spring-tx-5.0.2.RELEASE.jar中。
第三:spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式(声明式)实现。我们学习的重点是使用配置(声明式事务处理)的方式实现。
1.2 Spring中事务控制的API介绍
1.2.1 PlatformTransactionManager
此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法,如下图:
我们在开发中都是使用它的实现类:
真正管理事务的对象
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或myBatis 进行持久化数据时使用
org.springframework.orm.hibernate5.HibernateTransactionManager 使用Hibernate版本进行持久化数据时使用
JpaTransactionManager,使用Jpa操作持久化数据时使用
1.2.2 TransactionDefinition
它是事务的定义信息对象,里面有如下方法:
1:事务的隔离级别
2
2:事务的传播行为
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起,从新开启一个新的事务。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER:以非事务方式运行,如果当前存在事务,抛出异常
NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。
3:事务超时时间
默认值是-1,没有超时限制。如果有,以秒为单位、会进行设置,在事务提交/回滚后多长时间,事务失效。
4:是否是只读事务
建议查询时设置为只读。
增删改设置可写。
1.2.3TransactionStatus(了解)
此接口提供的是事务具体的运行状态,方法介绍如下图:
Spring的编程式事务处理
坐标xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
AccountDao.java
/**
* 账户的持久层接口
*/
public interface AccountDao {
/**
* 根据Id查询账户
* @param accountId
* @return
*/
Account findAccountById(Integer accountId);
/**
* 根据名称查询账户
* @param accountName
* @return
*/
Account findAccountByName(String accountName);
/**
* 更新账户
* @param account
*/
void updateAccount(Account account);
}
AccountDaoImpl.java
/**
* 账户的持久层实现类
*/
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
public Account findAccountById(Integer accountId) {
List<Account> accounts = this.getJdbcTemplate().query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
return accounts.isEmpty()?null:accounts.get(0);
}
public Account findAccountByName(String accountName) {
List<Account> accounts = this.getJdbcTemplate().query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
if(accounts.isEmpty()){
return null;
}
if(accounts.size()>1){
throw new RuntimeException("结果集不唯一");
}
return accounts.get(0);
}
public void updateAccount(Account account) {
this.getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
}
}
AccountService.java
/**
* 账户的业务层接口
*/
public interface AccountService {
/**
* 根据id查询账户信息
* @param accountId
* @return
*/
Account findAccountById(Integer accountId);
/**
* 转账
* @param sourceName 转成账户名称
* @param targetName 转入账户名称
* @param money 转账金额
*/
void transfer(String sourceName, String targetName, Float money);
}
AccountServiceImpl.java
/**
* 账户的业务层实现类
*
* 事务控制应该都是在业务层
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
public void transfer(String sourceName, String targetName, Float money) {
System.out.println("transfer....");
//2.1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//2.3转出账户减钱
source.setMoney(source.getMoney()-money);
//2.4转入账户加钱
target.setMoney(target.getMoney()+money);
//2.5更新转出账户
accountDao.updateAccount(source);
int i=1/0;
//2.6更新转入账户
accountDao.updateAccount(target);
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置账户的业务层-->
<bean id="accountService" class="com.it.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置账户的持久层-->
<bean id="accountDao" class="com.it.dao.impl.AccountDaoImpl">
<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://localhost:3306/itcastspring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {
@Autowired
private AccountService as;
@Test
public void testTransfer() {
as.transfer("aaa", "bbb", 100f);
}
}
编程式事务控制
Jdbc的操作,需要配置Jdbc的事务管理器
配置applicationContext.xml
<!-- 一:配置事务管理器=============== -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
配置事务管理模板
配置applicationContext.xml
<!-- 二:配置事务管理的模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
在业务层注入事务管理模板
配置applicationContext-tx1.xml
<!-- 配置账户的业务层-->
<bean id="accountService" class="com.it.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
AccountServiceImpl.java
public class AccountServiceImpl implements AccountService {
// 注入事务管理的模板
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
public void transfer(final String sourceName, final String targetName, final Float money) {
System.out.println("transfer....");
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
System.out.println(transactionStatus.isNewTransaction() + " "+transactionStatus.isCompleted());
//2.1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//2.3转出账户减钱
source.setMoney(source.getMoney()-money);
//2.4转入账户加钱
target.setMoney(target.getMoney()+money);
//2.5更新转出账户
accountDao.updateAccount(source);
// int i=1/0;
//2.6更新转入账户
accountDao.updateAccount(target);
}
});
}
}
Spring的声明式事务处理(**)
配置applicationContext.xml
同时去掉:TransactionTemplate的操作
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/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="accountService" class="com.it.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置账户的持久层-->
<bean id="accountDao" class="com.it.dao.impl.AccountDaoImpl">
<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://localhost:3306/itcastspring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- spring中基于XML的声明式事务控制配置步骤
1、配置事务管理器
2、配置事务的通知
此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的
使用tx:advice标签配置事务通知
属性:
id:给事务通知起一个唯一标识
transaction-manager:给事务通知提供一个事务管理器引用
3、配置AOP中的通用切入点表达式
4、建立事务通知和切入点表达式的对应关系
5、配置事务的属性
是在事务的通知tx:advice标签的内部
-->
<!-- 一:配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 二:配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 配置事务的属性
isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
测试:no-rollback-for="java.lang.ArithmeticException",遇到算数异常不回滚
-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!-- 三:配置aop-->
<aop:config>
<!-- 配置切入点表达式-->
<aop:pointcut id="pc" expression="execution(* com.it.service..*.*(..))"></aop:pointcut>
<!--建立切入点表达式和事务通知的对应关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
</aop:config>
</beans>
注解
配置AccountDaoImpl.java
@Repository
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Autowired
public void setDi(DataSource dataSource){
super.setDataSource(dataSource);
}
}
配置AccountServiceImpl.java
@Service
@Transactional(readOnly = true)
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public void transfer( String sourceName, String targetName, Float money) {
System.out.println("transfer....");
//2.1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//2.3转出账户减钱
source.setMoney(source.getMoney() - money);
//2.4转入账户加钱
target.setMoney(target.getMoney() + money);
//2.5更新转出账户
accountDao.updateAccount(source);
int i=1/0;
//2.6更新转入账户
accountDao.updateAccount(target);
}
}
注意:方法级别的事务会覆盖类级别的事务
配置applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/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">
<context:component-scan base-package="com.it"></context:component-scan>
<!--配置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://localhost:3306/itcastspring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- spring中基于XML的声明式事务控制配置步骤
1、配置事务管理器
2、配置事务的通知,通过注解的方式
-->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
Spring整体的总结:
总结:
1:IOC DI(xml和注解)
2:AOP(5种通知)(xml和注解)
3:声明式事务处理(xml和注解)
- * DBUtils(第三方提供的)
- * JDBCTemplate(是spring提供的)
4:spring3的新特性(纯注解开发)
- @Configuration
- @ConnectionScan
- @Import
- @Bean
自己创建的对象:使用注解
第三方创建的对象(数据源…):使用配置文件