美文网首页
Spring(三):JDBC Template,声明式事务

Spring(三):JDBC Template,声明式事务

作者: YMeng_Zhang | 来源:发表于2021-09-28 17:31 被阅读0次

    Spring 的 JdbcTemplate

    • JdbcTemplate 是什么?

    JdbcTemplate 是 spring 框架中提供的一个模板对象,是对原始繁琐的 JDBC API 对象的简单封装。

    核心对象:

    JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSource dataSource);
    

    核心方法:

    int update(); // 执行增、删、改语句
    List<T>  query(); // 查询多个
    T queryForObject(); // 查询一个
        new BeanPropertyRowMapper<>(); // 实现 ORM 映射封装
    

    查询数据库所有账户信息到 Account 实体中:

    public class JdbcTemplateTest {
        @Test
        public void testFindAll() throws Exception {
            // 创建核心对象
            JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());
            // 编写 sql
            String sql = "select * from account";
            // 执行 sql
            List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Account.class));
        }
    }
    
    • Spring 整合 JdbcTemplate

    需求:基于 Spring 的 xml 配置实现账户的 CRUD 案例
    步骤分析:

    1. 创建 java 项目,导入坐标
    2. 编写 Account 实体类
    3. 编写 AccountDao 接口和实现类
    4. 编写 AccountService 接口和实现类
    5. 编写 spring 核心配置文件
    6. 编写测试代码

    1. 创建 java 项目,导入坐标

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.11</java.version>
        <maven.compiler.source>1.11</maven.compiler.source>
        <maven.compiler.target>1.11</maven.compiler.target>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <!-- jdbcTemplate 相关的两个依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
    </dependencies>
    

    2. 编写 Account 实体类

    public class Account {
    
        private Integer id;
        private String name;
        private Double money;
    
        @Override
        public String toString() {
            return "Account{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", 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;
        }
    
    }
    

    3. 编写 AccountDao 接口和实现类

    public interface AccountDao {
    
        /**
         * 查询所有账户
         */
        List<Account> findAll();
    
        /**
         * 根据 ID 查询账户
         */
        Account findById(Integer id);
    
        /**
         * 添加账户
         */
        void save(Account account);
    
        /**
         * 更新账户信息
         */
        public void update(Account account);
    
        /**
         * 根据 ID 删除账户
         */
        public void delete(Integer id);
    
    }
    
    
    
    @Repository
    public class AccountDaoImpl implements AccountDao {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        /**
         * 查询所有账户
         */
        @Override
        public List<Account> findAll() {
            String sql = "select * from account";
            return jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class));
        }
    
        /**
         * 根据 ID 查询账户
         */
        @Override
        public Account findById(Integer id) {
            String sql = "select * from account where id = ?";
            return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class), id);
        }
    
        /**
         * 添加账户
         */
        @Override
        public void save(Account account) {
            String sql = "insert into account values(null,?,?)";
            jdbcTemplate.update(sql, account.getName(), account.getMoney());
        }
    
        /**
         * 更新账户信息
         */
        @Override
        public void update(Account account) {
            String sql = "update account set money = ? where name = ?";
            jdbcTemplate.update(sql, account.getMoney(), account.getName());
        }
    
        /**
         * 根据 ID 删除账户
         */
        @Override
        public void delete(Integer id) {
            String sql = "delete from account where id = ?";
            jdbcTemplate.update(sql, id);
        }
    
    }
    

    4. 编写 AccountService 接口和实现类

    public interface AccountService {
    
        /**
         * 查询所有账户
         */
        List<Account> findAll();
    
        /**
         * 根据 ID 查询账户
         */
        Account findById(Integer id);
    
        /**
         * 添加账户
         */
        void save(Account account);
    
        /**
         * 更新账户信息
         */
        void update(Account account);
    
        /**
         * 根据 ID 删除账户
         */
        void delete(Integer id);
    
    }
    @Service
    public class AccountSerivceImpl implements AccountService {
    
        @Autowired
        private AccountDao accountDao;
    
        /**
         * 查询所有账户
         */
        @Override
        public List<Account> findAll() {
            return accountDao.findAll();
        }
    
        /**
         * 根据 ID 查询账户
         */
        @Override
        public Account findById(Integer id) {
            return accountDao.findById(id);
        }
    
        /**
         * 添加账户
         */
        @Override
        public void save(Account account) {
            accountDao.save(account);
        }
    
        /**
         * 更新账户信息
         */
        @Override
        public void update(Account account) {
            accountDao.update(account);
        }
    
        /**
         * 根据 ID 删除账户
         */
        @Override
        public void delete(Integer id) {
            accountDao.delete(id);
        }
    
    }
    

    5. 编写 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"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- IOC 注解扫描 -->
        <context:component-scan base-package="com.zm"/>
    
        <!-- 引入 properties -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!-- 数据源 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <constructor-arg name="dataSource" ref="dataSource"/>
        </bean>
    
    </beans>
    

    6. 编写测试代码

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({"classpath:applicationContext.xml"})
    public class AccountServiceTest {
    
        @Autowired
        private AccountService accountService;
    
        // 测试保存
        @Test
        public void testSave() {
            Account account = new Account();
            account.setName("lucy");
            account.setMoney(1000d);
            accountService.save(account);
        }
    
        // 测试查询所有
        @Test
        public void testFindAll() {
            List<Account> all = accountService.findAll();
            for (Account account : all) {
                System.out.println(account);
            }
        }
    
        // 测试根据 ID 进行查询
        @Test
        public void testFindById() {
            Account account = accountService.findById(1);
            System.out.println(account);
    
        }
    
        // 测试账户修改
        @Test
        public void testUpdate() {
            Account account = new Account();
            account.setName("tom");
            account.setMoney(1000d);
            accountService.update(account);
        }
    
        // 测试根据 ID 删除账户
        @Test
        public void testDelete() {
            accountService.delete(4);
        }
    
    }
    
    • 实现转账案例

    步骤分析:

    1. 创建 java 项目,导入坐标
    2. 编写 Account 实体类
    3. 编写 AccountDao 接口和实现类
    4. 编写 AccountService 接口和实现类
    5. 编写 spring 核心配置文件
    6. 编写测试代码

    1. 创建 java 项目,导入坐标

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.11</java.version>
        <maven.compiler.source>1.11</maven.compiler.source>
        <maven.compiler.target>1.11</maven.compiler.target>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
    </dependencies>
    

    2. 编写 Account 实体类

    public class Account {
        private Integer id;
        private String name;
        private Double money;
        // setter getter....
    }
    

    3. 编写 AccountDao 接口和实现类

    public interface AccountDao {
        /**
         * 减钱:转出操作
         */
        void out(String outUser, Double money);
    
        /**
         * 加钱:转入操作
         */
        void in(String inUser, Double money);
    }
    
    
    
    @Repository
    public class AccountDaoImpl implements AccountDao {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Override
        public void out(String outUser, Double money) {
            String sql = "update account set money = money - ? where name = ?";
            jdbcTemplate.update(sql, money, outUser);
        }
    
        @Override
        public void in(String inUser, Double money) {
            String sql = "update account set money = money + ? where name = ?";
            jdbcTemplate.update(sql, money, inUser);
        }
    }
    

    4. 编写 AccountService 接口和实现类

    public interface AccountService {
        /**
         * 转账方法
         */
        void transfer(String outUser, String inUser, Double money);
    }
    
    
    
    
    @Service
    public class AccountServiceImpl implements AccountService {
        @Autowired
        private AccountDao accountDao;
    
        @Override
        public void transfer(String outUser, String inUser, Double money) {
            accountDao.out(outUser,money);
            // 模拟出错
            // int i = 1/0;
            accountDao.in(inUser,money);
        }
    }
    

    5. 编写 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"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:aop="http://www.springframework.org/schema/aop"
           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/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
    ">
    
        <!-- IOC 注解扫描 -->
        <context:component-scan base-package="com.zm"/>
    
        <!-- 加载 jdbc 配置文件 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!-- 把数据库连接池交给 IOC 容器 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <!-- 把 JdbcTemplate 交给 IOC 容器 -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <constructor-arg name="dataSource" ref="dataSource"/>
        </bean>
    
    </beans>
    

    6. 编写测试代码

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class AccountServiceTest {
    
        @Autowired
        private AccountService accountService;
    
        @Test
        public void testTransfer() {
            accountService.transfer("tom", "jerry", 100d);
        }
    
    }
    

    Spring 的事务

    • Spring 中的事务控制方式

    Spring 的事务控制可以分为编程式事务控制声明式事务控制。

    编程式:开发者直接把事务的代码和业务代码耦合到一起,在实际开发中不用。

    声明式:开发者采用配置的方式来实现的事务控制,业务代码与事务代码实现解耦合,使用的 AOP 思想。

    • 编程式事务控制相关对象

    PlatformTransactionManager

    PlatformTransactionManager 接口,是 spring 的事务管理器,里面提供了常用的操作事务的方法。

    方法 说明
    TransactionStatus getTransaction(TransactionDefinition definition) 获取事务的状态信息
    void commit(TransactionStatus status) 提交事务
    void rollback(TransactionStatus status) 回滚事务
    • PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类。

    • Dao 层技术是 jdbcTemplate 或 mybatis 时:
                 DataSourceTransactionManager

    • Dao 层技术是 hibernate 时:
                 HibernateTransactionManager

    • Dao 层技术是 JPA 时:
                 JpaTransactionManager

    TransactionDefinition

    TransactionDefinition 接口提供事务的定义信息(事务隔离级别、事务传播行为等等)

    方法 说明
    int getIsolationLevel() 获得事务的隔离级别
    int getPropogationBehavior() 获得事务的传播行为
    boolean isReadOnly() 是否只读

    事务隔离级别:
    设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读(幻读)。

    • ISOLATION_DEFAULT                               使用数据库默认级别
    • ISOLATION_READ_UNCOMMITTED         读未提交
    • ISOLATION_READ_COMMITTED              读已提交
    • ISOLATION_REPEATABLE_READ             可重复读
    • ISOLATION_SERIALIZABLE                       串行化

    事务传播行为:
    事务传播行为指的就是当一个业务方法“被”另一个业务方法调用时,应该如何进行事务控制。

    参数 说明
    REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到 这个事务中;即当前被调用的方法必须要进行事务控制;一般的选择(默认值)
    SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务);当前被调用的方法有没有事务都可以执行
    MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常
    REQUEST_NEW 新建事务,如果当前在事务中,把当前事务挂起
    NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
    NEVER 以非事务方式运行,如果当前存在事务,抛出异常
    NESTED 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行 REQUIRED 类似的操作
    • read-only(是否只读):建议查询时设置为只读
    • timeout(超时时间):默认值是-1,没有超时限制。如果有,以秒为单位进行设置
    TransactionStatus

    TransactionStatus 接口提供的是事务具体的运行状态。

    方法 说明
    boolean isNewTransaction() 是否是新事务
    boolean hasSavepoint() 是否是回滚点
    boolean isRollbackOnly() 事务是否回滚
    boolean isCompleted() 事务是否完成

    可以简单的理解三者的关系:事务管理器通过读取事务定义参数进行事务管理,然后会产生一系列的事务状态。

    实现代码

    配置文件

    <!-- 事务管理器对象交给 IOC 容器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    

    业务层代码

    @Service
    public class AccountServiceImpl implements AccountService {
        @Autowired
        private AccountDao accountDao;
        @Autowired
        private PlatformTransactionManager transactionManager;
        @Override
        public void transfer(String outUser, String inUser, Double money) {
            // 创建事务定义对象
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    
            // 设置是否只读,false 支持事务
            def.setReadOnly(false);
    
            // 设置事务隔离级别,可重复读 mysql 默认级别
            def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
    
            // 设置事务传播行为,必须有事务
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    
            // 配置事务管理器
            TransactionStatus status = transactionManager.getTransaction(def);
    
            try {
                // 转账
                accountDao.out(outUser, money);
                accountDao.in(inUser, money);
    
                // 提交事务
                transactionManager.commit(status);
            } catch (Exception e) {
                e.printStackTrace();
    
                // 回滚事务
                transactionManager.rollback(status);
            }
        }
    }
    
    小结

    Spring 中的事务控制主要就是通过这三个 API 实现的

    • PlatformTransactionManager 负责事务的管理,它是个接口,其子类负责具体工作
    • TransactionDefinition 定义了事务的一些相关参数
    • TransactionStatus 代表事务运行的一个实时状态

    三者的关系:事务管理器通过读取事务定义参数进行事务管理,然后会产生一系列的事务状态。

    • 基于 XML 的声明式事务控制

    在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务,底层采用 AOP 思想来实现的。

    声明式事务控制明确事项:

    • 核心业务代码(目标对象)- 确定切入点
    • 事务增强代码(Spring 已提供事务管理器)- 确定通知
    • 切面配置
    快速入门

    需求:使用 spring 声明式事务控制转账业务。

    步骤分析:

    1. 引入 tx 命名空间
    2. 事务管理器通知配置
    3. 事务管理器 AOP 配置
    4. 测试事务控制转账业务代码

    1. 引入 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:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:aop="http://www.springframework.org/schema/aop"
           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/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>
    

    2. 事务管理器通知配置

        <!-- 事务管理器对象交给 IOC 容器 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!-- 通知增强 -->
        <!-- 定义事务的一些属性:符号 * 表示当前任意名称的方法都走默认配置 -->
        <!--
            name:切点方法名称
            isolation:事务的隔离级别
            propagation:事务的传播行为
            read-only:是否只读
            timeout:超时时间
        -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="*"/>
            </tx:attributes>
        </tx:advice>
    

    3. 事务管理器 AOP 配置

    <!-- aop 配置:配置切面 -->
    <aop:config>
        <!-- 切面配置 -->
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.zm.service.impl.AccountServiceImpl.*(..))"/>
    </aop:config>
    
    事务参数的配置详解
    <!-- 通知增强 -->
    <!-- 定义事务的一些属性:符号 * 表示当前任意名称的方法都走默认配置 -->
    <!--
            name:切点方法名称
            isolation:事务的隔离级别
            propagation:事务的传播行为
            read-only:是否只读
            timeout:超时时间
    -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <!-- CRUD 常用配置 -->
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    

    小结

    • 平台事务管理器配置
    • 事务通知的配置
    • 事务 aop 织入的配置
    • 基于注解的声明式事务控制

    常用注解

    步骤分析:

    1. 修改 service 层,增加事务注解
    2. 修改 spring 核心配置文件,开启事务注解支持

    1. 修改 service 层,增加事务注解

    @Service
    // @Transactional // 方法都走默认配置
    public class AccountServiceImpl implements AccountService {
        @Autowired
        private AccountDao accountDao;
    
        /**
         * 转账方法
         */
        @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ, timeout = -1, readOnly = false)
        @Override
        public void transfer(String outUser, String inUser, Double money) {
            accountDao.out(outUser, money);
            // 模拟出错
            // int i = 1/0;
            accountDao.in(inUser, money);
        }
    }
    

    2. 修改 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"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:aop="http://www.springframework.org/schema/aop"
           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/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
    ">
    
        ...
    
        <!-- 事务的注解支持 -->
        <tx:annotation-driven/>
    
    </beans>
    
    纯注解

    核心配置类

    @Configuration // 声明该类为核心配置类
    @ComponentScan("com.renda") // IOC 注解扫描
    @Import(DataSourceConfig.class) // 导入其他配置类
    @EnableTransactionManagement // 事务的注解驱动
    public class SpringConfig {
    
        @Bean
        public JdbcTemplate getJdbcTemplate(@Autowired DataSource dataSource){
            JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
            return jdbcTemplate;
        }
    
        @Bean
        public PlatformTransactionManager getPlatformTransactionManager(@Autowired DataSource dataSource){
            DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
            return dataSourceTransactionManager;
        }
    

    数据源配置类

    @PropertySource("classpath:jdbc.properties") //引入 properties 文件
    public class DataSourceConfig {
    
        @Value("${jdbc.driverClassName}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
        @Bean // 把当前方法的返回值对象放进 IOC 容器中
        public DataSource getDataSource(){
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setDriverClassName(driver);
            druidDataSource.setUrl(url);
            druidDataSource.setUsername(username);
            druidDataSource.setPassword(password);
            return druidDataSource;
        }
    
    }
    

    测试代码

    @RunWith(SpringJUnit4ClassRunner.class)
    // @ContextConfiguration("classpath:applicationContext.xml")
    @ContextConfiguration(classes = SpringConfig.class)
    public class AccountServiceTest {
    
        @Autowired
        private AccountService accountService;
    
        @Test
        public void testTransfer() {
            accountService.transfer("tom", "jerry", 100d);
        }
    
    }
    

    小结

    • 平台事务管理器配置(xml 方式或者注解方式)
    • 事务通知的配置(@Transactional 注解配置)
    • 事务注解驱动的配置 <tx:annotation-driven/> 或者 @EnableTransactionManagement

    Spring 集成 web 环境

    • ApplicationContext 应用上下文获取方式

    应用上下文对象是通过 new ClasspathXmlApplicationContext(spring 配置文件) 方式获取的,但是每次从容器中获得 Bean 时都要编写 new ClasspathXmlApplicationContext(spring 配置文件),这样的弊端是配置文件加载多次,应用上下文对象创建多次。

    解决思路分析:在 Web 项目中,可以使用ServletContextListener监听 Web 应用的启动,我们可以在 Web 应用启动时,就加载 Spring 的配置文件,创建应用上下文对象 ApplicationContext,在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。

    • Spring 提供获取应用上下文的工具

    上面的分析不用手动实现,Spring 提供了一个监听器 ContextLoaderListener 就是对上述功能的封装,该监听器内部加载 Spring 配置文件,创建应用上下文对象,并存储到 ServletContext域中,提供了一个客户端工具 WebApplicationContextUtils 供使用者获得应用上下文对象。

    需要做的只有两件事:

    1. 在 web.xml 中配置 ContextLoaderListener 监听器(导入spring-web 坐标)
    2. 使用 WebApplicationContextUtils 获得应用上下文对象 ApplicationContext
    • 实现

    1. 导入 Spring 集成 web 的坐标

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.11</java.version>
        <maven.compiler.source>1.11</maven.compiler.source>
        <maven.compiler.target>1.11</maven.compiler.target>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>9.0.34</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    

    2. 配置 ContextLoaderListener 监听器

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <servlet>
            <servlet-name>AccountServlet</servlet-name>
            <servlet-class>com.zm.servlet.AccountServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>AccountServlet</servlet-name>
            <url-pattern>/AccountServlet</url-pattern>
        </servlet-mapping>
    
        <!-- 全局参数:指定 applicationContext.xml 文件的路径 -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </context-param>
    
        <!-- spring 的监听器 contextLoaderListener -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
    </web-app>
    

    3. 通过工具获得应用上下文对象

    public class AccountServlet extends HttpServlet {
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // classPathXmlApplicationContext 对象存到 servletContext 域中
    //        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    //        Account account = (Account) classPathXmlApplicationContext.getBean("account");
    //        System.out.println(account);
    
            ApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(req.getServletContext());
            if (webApplicationContext != null) {
                System.out.println((Account) webApplicationContext.getBean("account"));
            }
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doPost(req, resp);
        }
    }
    

    相关文章

      网友评论

          本文标题:Spring(三):JDBC Template,声明式事务

          本文链接:https://www.haomeiwen.com/subject/wrlrnltx.html