03_Spring_注解AOP编程&jdbcTemplate&声明事务

一、@Aspectj注解配置切面编程

1.1. 搭建环境

1. Spring基本包(4+2: beans,context,core,expression + logging+log4j)
2. aop编程导入aop相关的包(2+2 : aop联盟,aspectj,aop联盟整合包,aspectj整合包)
3. Spring-Junit测试:test

--------------------------------------------------------------------------
4. 导入log4j.properties日志配置文件,导入含有beans,context,aop约束的applicationContext.xml

1.2. 使用注解完成aop编程

1. 确定目标对象,注册bean
2. 编写增强类,注册bean
3. 使用注解配置切面和切入点
4. 测试


目标对象:UserServiceImpl / ProductService

@Service("userService")
public class UserServiceImpl implements UserService {

    @Override
    // 使用Spring的AOP编程增强
    public void save() {
        System.out.println("用户注册.........");
    }
}

//没有接口的目标对象bean
@Service
public class ProductService {

    //模拟添加商品..对该方法进行增强
    public void save() {
        System.out.println("ProductService.......添加商品...........");
    }
}

并在两个目标对象上使用@Service完成了bean的注解
---------------------------------------------------------------------------

编写增强类,注册bean(MyAspect):

//通知/增强类,对切入点进行增强
@Component //注册bean,将增强类交给Spring管理
@Aspect //将该类标识为切面类(该类里面有增强方法),相当于
public class MyAspect {

    //前置增强,参数value:写切入点表达式
    //相当于
    @Before("bean(*Service)")
    public void writeLog() {
        System.out.println("开始写日志了......Spring..aop开发");
    }
}


-----------------------------------------------------------------------------

到这里,目标bean的定义,增强类的定义以及切面的配置完成,但是还需要在核心配置文件中开启注解扫描与自动代理机制。




    
    
    
    
    


-----------------------------------------------------------------------------

测试:
@RunWith(SpringJUnit4ClassRunner.class)//Spring整合Junit
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class SpringTest {

    //注入测试bean
    @Autowired
    @Qualifier("userService")
    private UserService userService;
    
    @Autowired
    private ProductService productService;
    
    @Test
    public void test() {
        userService.save();
        System.out.println("======================");
        productService.save();
    }
}

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第1张图片
img36.png

补充:

我们的aop代理是使用的Spring的内部代理机制,默认是如果有接口就优先对接口代理(jdk动态代理)。

问题:如果目标对象有接口,能否只对实现类代理,而不对接口进行代理?可以!
03_Spring_注解AOP编程&jdbcTemplate&声明事务_第2张图片
img37.png
这里要使用子类的扩展方法,而接口中没有该方法,因为代理对象与目标对象不能互转,所以不能调用。
这时可以使用类代理(cglib动态代理),只需要设置 proxy-target-class = true 
03_Spring_注解AOP编程&jdbcTemplate&声明事务_第3张图片
img39.png

1.3. 使用@Pointcut 定义切入点

问题:如果直接在通知注解中写切入点表达式,会发生重复编写,后期不便于维护

解决:

在实际开发中,切入点都是单独定义维护的,如:
* 使用xml定义切入点
* 使用注解单独定义切入点@Pointcut

语法要求:
    切点方法:private void 无参数方法,方法名为切点名

=============================================================================

//通知/增强类,对切入点进行增强
@Component
@Aspect
public class MyAspect {

    // 定义切入点,id/name就是方法名
    // 优点:方便统一维护
    @Pointcut("bean(*Service)") // 定义切入点表达式
    private void myPointcut() {
    }

    // @Before("bean(*Service)")
    // 建立切入点与通知之前的关联,完整类名.方法名
    // @Before("com.itdream.spring.aopanno.MyAspect.myPointcut()")
    @Before("myPointcut()") // 因为在类的内部,可简写
    public void writeLog() {
        System.out.println("开始写日志了......Spring..aop开发");
    }
}

测试结果:

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第4张图片
img40.png

另外,一个通知方法也可以对多个切入点进行增强:

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第5张图片
img41.png

二、Spring JdbcTemplate模板类

2.1 概述

Spring中有一系列XxxTemplate类,这些类主要是用于简化xxx的编程。

Spring JdbcTemplate 是一个模板工具类,简化Jdbc编程 (类似 Apache DbUtils )

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第6张图片
img42.png
JdbcTemplate 简化 jdbc编程
HibernateTemplate 极大的简化 Hibernate编程

2.2 JdbcTemplate快速入门-代码

目标:使用Jdbc Template操作数据库。

1. 搭建环境
    1. 4+2个Spring基本jar包:beans,core,context,expression+logging,log4j.
    
    2. 导入JDBC模板开发包(2个):spring-jdbc.jar , spring-tx.jar ,有耦合性,因此操作jdbc需要导入tx,操作事务需要导入jdbc
    
    3. 导入测试包:spring-test.jar

    4. 数据库驱动
    
    5. 导入log4j.properties配置文件和核心配置文件applicationContext.xml

2. 代码
    1. 编写Service层、dao层操作数据库


===========================================================================

dao层:

public class UserDAO {

    //使用jdbc Template模板类操作数据库
    public void create() {
        //创建数据源(连接数据库),内置连接池,不建议生产环境下使用
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        
        //创建JdbcTemplate模板对象,简化JDBC代码
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //设置数据源(连接池)
        jdbcTemplate.setDataSource(dataSource);
        jdbcTemplate.execute("create table user001 (name varchar(20))");
    }
}

service层:
public class UserService {
    //业务层,调用dao层
    public void create() {
        UserDAO userDAO = new UserDAO();
        userDAO.create();
    }
}

以上是使用JdbcTemplate操作数据库的基本方式。

2.3. JdbcTemplate的XML配置方式

目标:将数据源和jdbcTemplate都交给Spring来管理。

这里还是使用Spring的内置的连接池。

applicationContext.xml:




    
    
        
        
        
        
    

    
    
        
    
    
    
    
        
    
    
    
    
        
    


注意:
    UserDAO中提供JdbcTemplate的声明以及setter方法,以供属性注入。
    UserService中提供UserDAO的声明以及setter方法,以供UserDAO注入。
    
测试:
@RunWith(SpringJUnit4ClassRunner.class)//整合Spring与Junit
@ContextConfiguration(locations="classpath:applicationContext.xml")//为Spring容器指定配置文件
public class SpringTest {
    
    @Autowired
    private UserService userService;

    @Test
    public void test() {
        userService.create();
        System.out.println("建表成功");
    }
}

2.4 使用JdbcDaoSupport简化开发

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第7张图片
img43.png
上面我们使用xml配置,将数据源,模板类,UserDAO以及UserService交给了Spring管理,但是配置文件中写的代码有点多。

Spring提供了JdbcDaoSupport来方便Dao中注入Jdbc Template.

我们只需要继承JdbcDaoSupport,将这个dao类注册成bean,注入dataSource数据源即可。

它会调用父类JdbcDaoSupport的setDataSource方法,将注入的DataSource传入,返回一个JdbcTemplate

因此,我们在dao类中,直接获取父类配置了注入数据源的JdbcTemplate使用即可


============================================================================

userDAO:(之前定义用于注入的JdbcTemplate代码就节省了)

public class UserDAO extends JdbcDaoSupport {

    // Spring注入的DataSource会调用父类JdbcDaoSupport中的setDataSource方法
    // 该方法会return new JdbcTemplate(dataSource);这个dataSource就是配置文件中注入的dataSource\
    // 因此父类中就有了一个配置了注入数据源的JdbcTemplate,我们直接get父类的JdbcTemplate模板类使用即可

    // 使用jdbc Template模板类操作数据库
    public void create() {
        getJdbcTemplate().execute("create table user002 (name varchar(20))");
    }
}


UserService:

public class UserService {

    private UserDAO userDAO;

    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    // 业务层,调用dao层
    public void create() {
        userDAO.create();
    }
}

--------------------------------------------------------------------------
配置文件applicationContext.xml:(不需要注册JdbcTemplate,直接向DAO中注入数据源dataSource即可)




    
    
        
        
        
        
    

    
    
        
    
    
    
    
        
    


测试:

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第8张图片
img44.png

2.4 配置自定义连接池

2.4.1 DBCP连接池的配置
1.导入DBCP连接池所需的jar包(tomcat内置了apache dbcp的jar包)
    commons-pool-1.5.6.jar  commons-dbcp-1.4.jar

2. 配置DBCP连接池(根据setter方法确定name)


    
    
    
    


DBCP连接池的配置与Spring内置连接池的配置几乎一模一样,修改一下class即可。
2.4.2 C3P0连接池的配置
1.导入C3P0连接池所需的jar包
    com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar

2. 配置DBCP连接池(根据setter方法确定name)



    
    
    
    


2.5. 外部属性文件的引入配置

上面我们进行了数据源(连接池)在Spring中的配置,我们有时候需要更换连接池,而配置文件的代码很多,修改起来不方便,因此将它的参数提取出来放在一个配置文件中。

模拟需求:
现在数据源的相关参数配置,是测试环境下的。
现在,要将工程搭建在正式的服务器上,因为测试环境和正式环境的数据库肯定不是一个。
所以肯定首先要更改数据源相关的配置。将数据源相关配置参数,外置。

目的:可以将xml配置中可能要经常修改内容,抽取到一个properties文件 
应用:使用properties文件配置参数,如数据库连接参数等。

------------------------------------------------------------------------------

1. 从applicationContext.xml中抽取经常修改的变量
例如:
    jdbc.dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
    jdbc.driverClass=com.mysql.jdbc.Driver
    jdbc.jdbcUrl=jdbc:mysql://localhost:3306/spring
    jdbc.username=root
    jdbc.password=root

2. 在applicationContext.xml中读取抽取出去的变量
 
首先要使用context:property-placeholder标签引入外界配置文件。

    1. 因此首先要导入,context有关的schema约束。
    2. 引入外界配置文件设置
    3. 引入变量





    
    
    
    
        
        
        
        
    
    ............................

2.6. 使用Jdbc Template进行CRUD操作

ProductDAO :

//使用JDBC Template进行CRUD操作
public class ProductDAO extends JdbcDaoSupport {

    // 继承了JdbcDaoSupport类,不需注入JdbcTemplate,只需要注入DataSource连接池即可
    // DAO中注入连接池DataSource,Spring会找到父类中的setDataSource,从而创建一个配置了注入连接池的JdbcTemplate
    // 因此在这里,我们可以直接调用父类的getJdbcTemplate()方法即可

    // 增加商品
    public void save(Product product) {
        String sql = "insert into product values(null,?,?)";
        getJdbcTemplate().update(sql, product.getPname(), product.getPrice());
    }

    // 删除商品
    public void delete(Product product) {
        String sql = "delete from product where pid=?";
        getJdbcTemplate().update(sql, product.getPid());
    }

    // 修改商品
    public void update(Product product) {
        String sql = "update product set price=? where pid=?";
        getJdbcTemplate().update(sql, product.getPrice(), product.getPid());
    }

    // 投影查询
    public Object findNameById(int pid) {
        String sql = "select pname from product where pid=?";
        return getJdbcTemplate().queryForObject(sql, new SingleColumnRowMapper<>(), pid);
    }

    // 投影查询
    public Object findColumnsById(int pid) {
        String sql = "select pname,price from product where pid=?";
        return getJdbcTemplate().queryForObject(sql, new ColumnMapRowMapper(), pid);
    }

    // 查询单个商品
    public Product query(int pid) {
        String sql = "select * from product where pid=?";
        return getJdbcTemplate().queryForObject(sql, new BeanPropertyRowMapper<>(Product.class), pid);
    }

    // 查询多商品
    public List queryProducts(String pname) {
        String sql = "select * from product where pname like ?";
        return getJdbcTemplate().query(sql, new BeanPropertyRowMapper(Product.class), "%" + pname + "%");
    }
    
    //查询所有商品
    public List findAll() {
        String sql = "select * from product";
        return getJdbcTemplate().query(sql, new BeanPropertyRowMapper(Product.class));
    }
}

---------------------------------------------------------------------------

配置文件applicationContext.xml:




    
    
    
    
        
        
        
        
    

    
    
        
    

    
    
        
        
        
    


---------------------------------------------------------------------------

测试:
@RunWith(SpringJUnit4ClassRunner.class) // 整合Spring,否则不能注入测试bean
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpringTest {

    @Autowired
    private ProductDAO productDAO;

    @Autowired
    private Product product;

    @Test
    public void test() {
        // productDAO.save(product);
        // productDAO.delete(product);
        // product.setPid(2);
        // product.setPrice(2999D);
        // productDAO.update(product);
        
        System.out.println(productDAO.findNameById(2));
        System.out.println(productDAO.findColumnsById(2));
        System.out.println(productDAO.query(2));
        System.out.println(productDAO.queryProducts("one"));
        System.out.println(productDAO.findAll());
    }
}

=============================================================================

总结:
  • 增删改的操作使用update
  • 简单查询使用```queryForObject````查询
    • 封装单个对象使用BeanPropertyRowMapper封装
    • 投影查询对象属性使用ColumnMapRowMapper
  • 复杂的查询使用query查询
    • 封装使用new BeanPropertyRowMapper封装

三、 Spring的事务管理机制【了解】

Spring事务管理高层抽象主要包括3个接口,Spring的事务主要是由他们共同完成的:

  • PlatformTransactionManager:事务管理器—主要用于平台相关事务的管理
  • TransactionDefinition: 事务定义信息(隔离、传播、超时、只读)—通过配置如何进行事务管理。
  • TransactionStatus:事务具体运行状态—事务管理过程中,每个时间点事务的状态信息。

3.1. PlatformTransactionManager事务管理器

该接口提供三个方法:

  • commit : 提交事务
  • rollback : 回滚事务
  • getTransaction : 获取事务状态

Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现:

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第9张图片
img45.png
  • DataSourceTransactionManager针对JdbcTemplate、MyBatis事务控制 ,使用Connection(连接)进行事务控制 :

    • connection.setAutoCommit(false):开启事务
    • connection.rollback():回滚事物
    • connection.commit():提交事物
  • HibernateTransactionManager针对Hibernate框架进行事务管理, 使用Session的Transaction相关操作进行事务控制 :

    • session.beginTransaction():开启事务
    • session.getTransaction.commit:提交事务
    • session.getTransaction.rollback:回滚事务

根据选择和使用的持久层技术,来选择对应的事务管理器。

3.2. TransactionDefinition事务定义信息

该接口主要提供的方法:

  • getIsolationLevel:隔离级别获取
  • getPropagationBehavior:传播行为获取
  • getTimeout:获取超时时间(事务的有效期)
  • isReadOnly: 是否只读。事务管理器能够根据这个返回值进行优化。
    • 保存、更新、删除—对数据进行操作-设置为false(默认值),变成可读写的,
    • 查询-设置这个属性为true,只能读不能写)

这些事务的定义信息,都可以在配置文件中配置和定制。

3.2.1. IsolationLevel事务的隔离级别
03_Spring_注解AOP编程&jdbcTemplate&声明事务_第10张图片
img46.png

Spring使用默认的defalut:根据数据库的默认隔离级别确定

* MySQL的默认隔离级别是Repeatable_Read,可重复读
* Oracle的默认隔离级别是Read_Commit,读已提交
3.2.2. 事务的传播行为Transaction Propagation Behavior

事务传播行为用于解决两个被事务管理的方法互相调用问题

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第11张图片
img47.png

业务层两个方法面临的事务问题:有些时候需要处于同一个事务,有些时候不能在同一个事务 !

事务的传播行为的7种类型:

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第12张图片
img48.png

有关事务传播行为的详细解释:

PROPAGATION_REQUIRED(默认值)
A中有事务,使用A中的事务.如果A中没有,B就会开启一个新的事务,将A包含进来.(保证A,B在同一个事务中),默认值!!
PROPAGATION_SUPPORTS 
A中有事务,使用A中的事务.如果A中没有事务.那么B也不使用事务.
PROPAGATION_MANDATORY
A中有事务,使用A中的事务.如果A没有事务.抛出异常.
PROPAGATION_REQUIRES_NEW
A中有事务,将A中的事务挂起.B创建一个新的事务.(保证A,B没有在一个事务中)
PROPAGATION_NOT_SUPPORTED
A中有事务,将A中的事务挂起.
PROPAGATION_NEVER
A中有事务,抛出异常.
PROPAGATION_NESTED
嵌套事务.当A执行之后,就会在这个位置设置一个保存点.如果B没有问题.执行通过.如果B出现异常,运行客户根据需求回滚

为方便记忆,将Spring的事务传播行为分为三大类:

**PROPAGATION_REQUIRED(默认值)**、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY
支持当前事务, A调用B,如果A事务存在,B和A处于同一个事务 。
事务默认传播行为 REQUIRED。最常用的。


**PROPAGATION_REQUIRES_NEW**、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER 
不会支持原来的事务 ,A调用B, 如果A事务存在, B肯定不会和A处于同一个事务。
常用的事务传播行为:PROPAGATION_REQUIRES_NEW


**PROPAGATION_NESTED**
嵌套事务 ,只对DataSourceTransactionManager有效 ,底层使用JDBC的SavePoint机制,允许在同一个事务设置保存点,回滚保存点


面试题: REQUIRED、REQUIRES_NEW、NESTED 区分?
    REQUIRED 一个事务(默认,推荐)
    REQUIRES_NEW 两个事务 
    NESTED 一个事务,事务可以设置保存点,回滚到保存点 ,选择提交或者回滚

3.3. TransactionStatus 事务状态

  • flush(),给hibernate使用,底层发出sql的
  • hasSavepoint():判断是否有保留点
  • isCompleted():判断事务是否结束
  • isNewTransaction():判断当前事务是否是新开的一个事务。
  • isRollbackOnly():判断事务是否只能回滚
  • setRollbackOnly():设置事务是否回滚

数据库操作中,如果只是回滚,后面不操作,数据库在关闭连接的时候,自动发出了commit。commit结束事务。


三个事务超级接口对象之间的关系:

  1. 用户管理事务,需要先配置TransactionDefinition(事务定义信息、事务的管理方案);
  2. 然后根据TransactionDefinition,通过TransactionManager(事务管理器)进行事务管理;
  3. 事务运行过程中,每个时刻都可以通过获取TransactionStatus(事务状态)来了解事务的运行状态。

3.4. Spring事务管理两种方式

  • 编程式的事务管理(有侵入性,很少用)
  • 使用XML或注解配置声明式事务【开发中使用】
    • Spring的声明事务通过AOP实现(环绕通知)
3.4.1 使用XML配置声明式事务

示例:

需求:
    银行转账:转出、转入,使用Spring的声明式事务

-------------------------------------------------------------------------

步骤:
1. 搭建环境
    1. 导入jar包
        Spring基本包:4+2 : beans,context,core,expression.  +  apache logging,log4j

        spring测试包: test

        spring事务包: tx,jdbc(jdbc与tx有耦合,使用其中一个包必须导入另一个)

        spring的事务依赖AOP(2+2):apache-aop联盟,aspect.  spring对两个包的整合包

        数据库驱动:mysql-converter

        c3p0连接池的jar包
    --------------------------------------------------------------------

    2. 导入log4j.properties配置文件,applicationContext.xml配置文件
    3. 完成数据库建表以及模拟的两个用户
    4. 根据表编写实体类User


2. 完成功能
    1. 编写UserDAO的转入转出数据库操作(使用JdbcTemplate)
    2. 编写UserService完成业务逻辑操作
    3. 完成事务增强
        1. 确定目标类,注册bean
        2. 注册增强类
        3. 配置切面和切入点(切入点和通知之间的关联)


3. 测试


===========================================================================

1. 搭建环境:

jar包:

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第13张图片
img49.png
    
    创建数据库表user:
    CREATE DATABASE spring;
    
    CREATE TABLE USER(
        uid INTEGER PRIMARY KEY AUTO_INCREMENT,
        username VARCHAR(20),
        acount DOUBLE
    );
    
    INSERT INTO USER VALUES(NULL,'tom',1000),(NULL,'jack',1000);


    创建实体类User:
    //User的实体类
    public class User {
    
        private Integer uid;
        private String username;
        private Double acount;
    
        public Integer getUid() {
            return uid;
        }
    
        public void setUid(Integer uid) {
            this.uid = uid;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public Double getAcount() {
            return acount;
        }
    
        public void setAcount(Double acount) {
            this.acount = acount;
        }
    
        @Override
        public String toString() {
            return "User [uid=" + uid + ", username=" + username + ", acount=" + acount + "]";
        }
    }

    
    导入log4j.properties,applicationContext.xml核心配置文件。

2. 业务实现:


    UserDAO:

    //模拟银行转账
    public class UserDAO extends JdbcDaoSupport {
    
        // 注入dataSource父类会自动创建一个配置了注入数据源的JdbcTemplate
    
        // 转出
        public void out(String outname, Double money) {
            String sql = "update user set acount=acount-? where username=?";
            getJdbcTemplate().update(sql, money, outname);
        }
    
        // 转入
        public void in(String inname, Double money) {
            String sql = "update user set acount=acount+? where username=?";
            getJdbcTemplate().update(sql, money, inname);
        }
    }

    UserService:

    public class UserService {
    
        // 注入userDAO
        private UserDAO userDAO;
    
        // 提供setter方法
        public void setUserDAO(UserDAO userDAO) {
            this.userDAO = userDAO;
        }
    
        public void transfer(String outname, String inname, Double money) {
            //开启事务
            //转出
            userDAO.out(outname, money);
            
            //制造异常
            //int i = 1/0;
            //转入
            userDAO.in(inname, money);
            //关闭事务
        }
    }

    ---------------------------------------------------------------------------

    配置applicationContext.xml完成Spring的声明式事务:

    头约束:
        因为JdbcTemplate和事务管理器都需要导入数据源,因此配置了c3p0连接池,抽取了变量另外配置。
        为了能引入外部配置,使用context..placeholder,需要引入context头约束。

        因为要使用事务,因此需要导入aop和tx的头约束(spring的事务依赖aop的环绕通知完成)

    
        
    外部数据源的配置(db.properties):
        jdbc.dataSourceClass=com.mchange.v2.c3p0.ComboPooledDataSource
        jdbc.driverClassName=com.mysql.jdbc.Driver
        jdbc.url=jdbc:mysql://localhost:3306/spring
        jdbc.username=root
        jdbc.password=root


    核心配置文件(applicationContext.xml):
    
    
    
    
    
        
        
        
        
        
            
            
            
            
        
        
        
        
            
            
        
        
        
        
            
        
        
        
        
        
            
            
        
        
        
        
         
        
              
            
                
                
            
        
        
        
        
            
            
            
            
        
    


    ---------------------------------------------------------------------------

    测试:
    @RunWith(SpringJUnit4ClassRunner.class)//整合Spring进行测试
    @ContextConfiguration(locations="classpath:applicationContext.xml")//为Spring容器指定配置文件
    public class SpringTest {
        
        @Autowired
        //注入测试bean
        private UserService userService;
    
        @Test
        public void testTransfer() {
            //tom向jack转账1000元
            userService.transfer("tom", "jack", 100D);
            System.out.println("转账成功.............");
        }
    }

3.4.2 使用注解配置声明式事务

步骤:

1. 确定目标类,方法(要被增强的方法,即切入点),注册bean
2. 确定增强类(这里要完成事务管理),Spring已经帮我们实现了
3. 配置切面关系(注解实现),在需要管理事务的方法或者类上面 添加@Transactional 注解  
4. 配置注解驱动事务管理(事务管理注解生效的作用)(需要配置对特定持久层框架使用的事务管理器)

-----------------------------------------------------------------------------

1. @Service将目标类 UserService注册成bean
2. Spring已经帮我们实现了事务管理的增强类,无需手动实现并注册了
3. 配置切面关系,@Transactional写在需要增强的方法或类上

@Service("userService")
public class UserService {

    // 注入userDAO
    @Autowired
    @Qualifier("userDAO")
    private UserDAO userDAO;

    //在需要管理事务的方法或类上面用@Transactional管理
    @Transactional
    public void transfer(String outname, String inname, Double money) {
        //转出
        userDAO.out(outname, money);
        
        //制造异常
        //int i = 1/0;
        //转入
        userDAO.in(inname, money);
    }
}

----------------------------------------------------------------------------

需要将UserDAO也注册成bean,将DataSource数据源传入,调用父类setDoataSource方法,创建一个JdbcTemplate,
这样UserDAO就可以直接获取父类的JdbcTemplate使用了。

但是,如果将@Autowired直接注解声明的对象上,Spring会自动创建一个setDataSource()方法,与父类的final冲突。
因此自己定义一个set方法接收DataSource,调用父类的setDSataSource方法。

@Repository("userDAO") // 将其注册成bean
public class UserDAO extends JdbcDaoSupport {

    //使用注解@Autowired注入DataSource数据源会默认生成setter方法,setDataSource(Datasource ds)注入
    //但是父类的setDataSource是用final修饰的,不能重写,而且这样也无法将dataSource传入到setDataSource方法中
    //这样就无法生成配置了注入数据源的JdbcTemplate,因此我们随便写一个set方法,参数类型写DataSource调用父类的setter方法
    //@Autowired
    //private DataSource dataSource;
    
    @Autowired
    public void setSuperDataSource(DataSource dataSource) {
        super.setDataSource(dataSource);
    }
    

    // 转出
    public void out(String outname, Double money) {
        String sql = "update user set acount=acount-? where username=?";
        getJdbcTemplate().update(sql, money, outname);
    }

    // 转入
    public void in(String inname, Double money) {
        String sql = "update user set acount=acount+? where username=?";
        getJdbcTemplate().update(sql, money, inname);
    }
}

4. 配置注解驱动事务管理器






    
    

    
    

    
    
        
        
        
        
    

    
    
        
    
    
    
    

    

-------------------------------------------------------------------------

1. 事务的相关策略:
    isolation,read-only...可以直接在@Transactional后配置

2. 如果在类上标识@Transactional,那么会对该类中的所有public方法都包装事务,等同于所有的public方法上面
都添加了@Transactional。
如果某个方法需要单独的事务定义,需要在方法上加@Transactional来覆盖类上的标注声明。
03_Spring_注解AOP编程&jdbcTemplate&声明事务_第14张图片
img50.png

xml和注解的选择:

XML方式,集中式维护。使用XML进行事务管理 属性集中配置,便于管理和维护

注解方式,使用@Transactional注解进行事务管理,配置太分散

因此优选使用XML进行事务管理。


事务的策略通配符设置:

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第15张图片
img51.png

xml方式与注解方式管理事务小结:

03_Spring_注解AOP编程&jdbcTemplate&声明事务_第16张图片
img52.png
03_Spring_注解AOP编程&jdbcTemplate&声明事务_第17张图片
img53.png

今日总结:

以后直接使用即可。

1. 注解AOP编程:

1. 确定目标类bean
2. 确定增强(通知)类,bean
3. 使用@Aspect定义切面,使用@Pointcut定义切入点。
在对应的增强方法上使用@Before,@Around等完成切入点与通知的关联

applicationContext.xml中:




    
    

    
    


这里如果想使用cglib的代理,还可以在 内设置proxy-target-class="true"

2. JdbcTemplate

为简化数据库操作,Spring提供了JdbcTemplate模板类,为了简化JdbcTemplate的开发,Spring提供了JdbcDaoSupport。
Dao层继承该类,传入对应数据源就可以直接获取对应的Template操作数据库。
JdbcTemplate/HibernateTemplate...

---------------------------------------------------------------------------
1. db.properties外部配置文件
        jdbc.dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
        jdbc.driverClass=com.mysql.jdbc.Driver
        jdbc.jdbcUrl=jdbc:mysql://localhost:3306/spring
        jdbc.username=root
        jdbc.password=root

    2. 在applicationContext.xml中读取抽取出去的变量
     
    首先要使用context:property-placeholder标签引入外界配置文件。

        1. 因此首先要导入,context有关的schema约束。
        2. 引入外界配置文件设置
        3. 引入变量

    
    
    
    
        
        
        
        
            
            
            
            
        
        ............................
    

3. 声明式事务管理

两种方式:

  • XML配置进行事务管理
  • 注解配置事务管理

注解配置进行事务管理:

1. 将目标类定义成bean即可.增强类我们不需自己实现。
2. 在需要进行事务管理的方法/类上使用@Transactional(这里可以配置事务策略)
3. 配置注解事务管理器

applicationContext.xml配置注解驱动事务管理器:






    
    

    
    

    
    
        
        
        
        
    

    
    
        
    

    
    


XML配置事务管理:【推荐】

1. 确定目标类,注册bean 
2. 确定增强类,注册bean(Spring已经实现,只需注册该则增强类即可)
3. 配置事务管理器(Spring实现的增强类需要注入事务管理器)
4. 配置切面(切入点与通知的关联)


外部配置文件db.properties:
jdbc.dataSourceClass=com.mchange.v2.c3p0.ComboPooledDataSource
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=root
    

applicationContext.xml配置文件最终版:






    
    
        
        
        
        
    
    
    
    
    
        
            
            
            
            
            
        
    
    
    
    
        
    
    
    
    
        
        
        
        
        
    
    
    
    
        
    
    
    
    
        
    

你可能感兴趣的:(03_Spring_注解AOP编程&jdbcTemplate&声明事务)