从零开始学习Spring - JdbcTemplate、Spring事务

1. jdbcTemplate

1.1 基本概念

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

  • 核心对象

    JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSource dataSource);
    
  • 核心方法

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

1.2 Spring整合

1.2.1 Maven配置

  <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.15</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.15</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.15</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.15</version>
        </dependency>
    </dependencies>

1.2.2 实体类

  • Account

    public class Account {
    
        private Integer id;
        private String name;
        private Double money;
        
    }
    

1.2.3 Dao层接口

  • AccountDao

    package cn.knightzz.dao;
    
    import cn.knightzz.entity.Account;
    
    import java.util.List;
    
    public interface AccountDao {
    
        /**
         * 查询所有账户
         * @return
         */
        public List<Account> findAll();
    
        /**
         * 根据id查询账户
         * @param id
         * @return
         */
        public Account findById(Integer id);
    
        /**
         * 添加账户信息
         * @param account
         */
        public void save(Account account);
    
        /**
         * 更新账户信息
         * @param account
         */
        public void update(Account account);
    
        /**
         * 删除账户信息
         * @param id
         */
        public void delete(Integer id);
    }
    
    
  • AccountDaoImpl

    package cn.knightzz.dao.impl;
    
    import cn.knightzz.dao.AccountDao;
    import cn.knightzz.entity.Account;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    /**
     * @author 王天赐
     * @title: AccountDaoImpl
     * @projectName mybatis-apply-06
     * @description:
     * @website http://knightzz.cn/
     * @github https://github.com/knightzz1998
     * @date 2022/1/27 14:07
     */
    @Repository("accountDao")
    public class AccountDaoImpl implements AccountDao {
    
        @Resource
        JdbcTemplate jdbcTemplate;
    
        @Override
        public List<Account> findAll() {
    
            String sql = "select * from account";
            List<Account> accountList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Account.class));
            return accountList;
        }
    
        @Override
        public Account findById(Integer id) {
    
            String sql = "select * from account where id = ?";
            Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), id);
            return account;
        }
    
        @Override
        public void save(Account account) {
            String sql = "insert into account(id, name ,money) values(null ,  ?,  ?)";
            jdbcTemplate.update(sql, account.getName(), account.getMoney());
        }
    
        @Override
        public void update(Account account) {
            String sql = "update account set name = ?, money = ? where id = ?";
            jdbcTemplate.update(sql, account.getName(), account.getMoney(), account.getId());
        }
    
        @Override
        public void delete(Integer id) {
            String sql = "delete from account where id = ?";
            jdbcTemplate.update(sql, id);
        }
    }
    
    
    

1.3.4 Service 层接口

  • AccountService接口

    package cn.knightzz.service;
    
    import cn.knightzz.entity.Account;
    
    import java.util.List;
    
    public interface AccountService {
        /**
         * 查询所有账户
         * @return
         */
        public List<Account> findAll();
    
        /**
         * 根据id查询账户
         * @param id
         * @return
         */
        public Account findById(Integer id);
    
        /**
         * 添加账户信息
         * @param account
         */
        public void save(Account account);
    
        /**
         * 更新账户信息
         * @param account
         */
        public void update(Account account);
    
        /**
         * 删除账户信息
         * @param id
         */
        public void delete(Integer id);
    
    
    }
    
  • AccountServiceImpl

    package cn.knightzz.service.impl;
    
    import cn.knightzz.dao.AccountDao;
    import cn.knightzz.entity.Account;
    import cn.knightzz.service.AccountService;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service("accountService")
    public class AccountServiceImpl implements AccountService {
    
        @Resource
        AccountDao accountDao;
    
        @Override
        public List<Account> findAll() {
            return accountDao.findAll();
        }
    
        @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);
        }
    
        @Override
        public void delete(Integer id) {
            accountDao.delete(id);
        }
    }
    
    

1.3.5 测试代码

  • AccountServiceTest

    package cn.knightzz.service;
    
    import cn.knightzz.config.SpringConfig;
    import cn.knightzz.entity.Account;
    import junit.framework.TestCase;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {SpringConfig.class})
    public class AccountServiceTest extends TestCase {
    
        @Resource
        AccountService accountService;
    
        @Test
        public void testFindAll() {
            List<Account> accountList = accountService.findAll();
            for (Account account : accountList) {
                System.out.println(account);
            }
        }
    
        @Test
        public void testFindById() {
            Account account = accountService.findById(1);
            System.out.println(account);
        }
    
        @Test
        public void testSave() {
            Account accout = new Account();
            accout.setId(3);
            accout.setName("kiss");
            accout.setMoney(300d);
            accountService.save(accout);
        }
    
        @Test
        public void testUpdate() {
            Account accout = new Account();
            accout.setId(3);
            accout.setName("knightzz");
            accout.setMoney(400d);
            accountService.update(accout);
        }
    
        public void testDelete() {
        }
    }
    

2. Spring事务

2.1 Spring事务控制方式

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

2.1.1 编程式事务

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

2.1.2 声明式事务

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

2.2 编程式事务控制对象

2.2.1 不同的Dao层实现对象

  • PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类。
  • Dao层技术是jdbcTemplatemybatis时: DataSourceTransactionManager
  • Dao层技术是hibernate时: HibernateTransactionManager
  • Dao层技术是JPA时: JpaTransactionManager

2.2.2 PlatformTransactionManager

  • PlatformTransactionManager接口,是spring的事务管理器,

  • 里面提供了我们常用的操作事务的方法

  • 常用方法

    从零开始学习Spring - JdbcTemplate、Spring事务

2.2.3 TransactionDefinition

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

  • 常用方法

    从零开始学习Spring - JdbcTemplate、Spring事务

2.2.4 事务隔离级别

  • 脏写、脏读、不可重复读和幻读

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

  • ISOLATION_DEFAULT 使用数据库默认级别

  • ISOLATION_READ_UNCOMMITTED 读未提交

  • ISOLATION_READ_COMMITTED 读已提交

  • ISOLATION_REPEATABLE_READ 可重复读

  • ISOLATION_SERIALIZABLE 串行化

2.2.5 事务传播行为

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

    从零开始学习Spring - JdbcTemplate、Spring事务
  • read-only(是否只读):建议查询时设置为只读

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

2.2.6 TransactionStatus

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

    从零开始学习Spring - JdbcTemplate、Spring事务
  • 可以简单的理解三者的关系:事务管理器通过读取事务定义参数进行事务管理,然后会产生一系列的事
    务状态。

2.2.7 代码实现

  • SpringConfig

    @Bean("transactionManager")
    public DataSourceTransactionManager getDataSourceTransactionManager(@Autowired DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
    
  • Service

     @Resource
        private PlatformTransactionManager transactionManager;
    
        /**
         * 转账操作
         *
         * @param outUser
         * @param inUser
         * @param money
         */
        @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);
                }
        }
    

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

2.3.1 开发步骤

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

2.3.2 引入tx命名空间

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w2.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/s chema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    
</beans>

2.3.3 事务管理器通知配置

<!--事务管理器--> 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="dataSource"></property> 
</bean> <!--通知增强--> 
<tx:advice id="txAdvice" transaction-manager="transactionManager"> 
    <!--定义事务的属性--> 	
    <tx:attributes> 
    <tx:method name="*"/> 
	</tx:attributes> 
</tx:advice>

2.3.4 事务管理器AOP配置

<!--aop配置--> 
<aop:config> <!--切面配置--> 
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lagou.serivce..*.*(..))"> </aop:advisor> 
</aop:config>

2.3.5 事务参数的配置

<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
  • name:切点方法名称

  • isolation:事务的隔离级别

  • propogation:事务的传播行为

  • timeout:超时时间

  • read-only:是否只读

2.3.6 CRUD事务常用配置

<tx:attributes> 
    <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>

2.4 基于注解的声明式事务控制

2.4.1 基本步骤

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

2.4.2 开启事务配置

  • SpringConfig : @EnableTransactionManagement 开启事务

    @Configuration
    @ComponentScan("cn.knightzz")
    @EnableAspectJAutoProxy
    @EnableTransactionManagement
    @Import(DataSourceConfig.class)
    public class SpringConfig {
    

2.4.3 Service添加注解

  • 注意 : 事务注解的代码不要使用try catch 包裹, 否则无法捕获异常, 并进行回滚

        @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);
        }
    
上一篇:2019icpc西安邀请赛 J And And And (树形dp)


下一篇:JdbcTemplate基础【项目demo】【基础知识】【简单明了,一眼就会】