博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Spring - 09JdbcTemplate及事务

Posted on 2020-11-19 20:58  Kingdomer  阅读(475)  评论(0)    收藏  举报

Spring - 09JdbcTemplate及事务

(1)JdbcTemplate基本使用

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

例如:操作关系数据库的JdbcTemplate和HibernateTemplate,操作Redis的RedisTemplate,操作消息队列的JmsTemplate等等。

(1.1)导入spring-jdbc和spring-tx坐标

导入spring-context、spring-test、junit、mysql-connector-java、c3p0、druid

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.10.RELEASE</version>
        </dependency>

(1.2)创建数据库表和实体

public class Account {
    private String name;
    private double money;
    // 省略getter/setter/toString
}

(1.3)创建JdbcTemplate对象

    @Test
    public void test1(){
        DruidDataSource dataSource = new DruidDataSource();       // 创建数据源对象
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/dev");
        dataSource.setUsername("root");
        dataSource.setPassword("Mysql2020");

        JdbcTemplate jdbcTemplate = new JdbcTemplate();         
        jdbcTemplate.setDataSource(dataSource);                   // 设置数据源对象,知道数据库在哪
        int row = jdbcTemplate.update("insert into account values(?,?)", "tom", 5000.00);
        System.out.println(row);
    }

(1.4)执行数据库操作

        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        int row = jdbcTemplate.update("insert into account values(?,?)", "tom", 5000.00);
        System.out.println(row);

(1.5)Spring产生JdbcTemplate对象

我们可以将JdbcTemplate的创建权交给Spring,将数据源DataSource的创建权也交给Spring,在Spring容器内部将数据源DataSource注入到JdbcTemplate模板对象中。

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

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">     // 数据源对象
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">   // jdbc模板对象
        <property name="dataSource" ref="dataSource"></property>
    </bean>

 

    @Test
    public void test2() throws PropertyAccessException {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        JdbcTemplate jdbcTemplate = app.getBean(JdbcTemplate.class);
        int row = jdbcTemplate.update("insert into account values(?,?)", "xiaoming", 4000);
        System.out.println(row);
    }

 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTemplateTest {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void testQueryCount(){     
        Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
        System.out.println(count);
    }

    @Test
    public void testQueryOne(){
        Account account = jdbcTemplate.queryForObject("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class),"tom");
        System.out.println(account);
    }

    @Test
    public void testQueryAll(){
        List<Account> accountList = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
        System.out.println(accountList);
    }

    @Test
    public void testDelete(){
        int row = jdbcTemplate.update("delete from account where name = ?",  "aaa");
        System.out.println(row);
    }

    @Test
    public void testUpdate(){
        int row = jdbcTemplate.update("update account set money = ? where name = ?",  8000,"xiaoming");
        System.out.println(row);
    }
}

 

 

(2)编程式事务控制对象

(2.1)PlatformTransactionManager

PlatformTransactionManager接口是Spring的事务管理,它里面提供了我们常用的操作事务的方法。

public interface PlatformTransactionManager extends TransactionManager {
    // 获取事务的状态信息
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;   
    // 提交事务
    void commit(TransactionStatus status) throws TransactionException;
    // 回滚事务
    void rollback(TransactionStatus status) throws TransactionException;
}

PlatformTransactionManager 是接口类型,不同的Dao层技术有不同的实现类。

Dao层技术为jdbc或mybatis时, 为 org.springframework.jdbc.datasource.DataSourceTransactionManager

Dao层技术为hibernate时, 为 org.springframework.orm.hibernate5.HibernateTransactionManager

(2.2)TransactiionDefinition

TransactionDefinition 是事务的定义信息对象,里面的方法如下:

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

 

 

 

 

 

 

 

事务隔离级别: 

1> 解决事务并发产生的问题,如脏读、不可重复读和幻读。

2> ISOLATION_DEFAULT、ISOLATION_READ_UNCOMMITTED、ISOLATION_READ_COMMITTED、ISOLATION_REPEATABLE_READ、ISOLATION_SERIALIZABLE

事务传播行为

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

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

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

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

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

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

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

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

是否只读: 建议查询设置为只读。

(2.3)TransactionStatus

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

public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
    // 是否是新事务
    @Override
    boolean isNewTransaction();
    // 是否存储回滚点
    boolean hasSavepoint();
    
    @Override
    void setRollbackOnly();
    // 事务是否回滚
    @Override
    boolean isRollbackOnly();

    void flush();
    // 事务是否完成
    @Override
    boolean isCompleted();
}

 

(3)基于XML的声明式事务控制

(3.1)什么是声明式事务控制

采用声明的方式来处理事务。声明是指在配置文件中声明,用在Spring配置文件中声明式的处理事务来代替代码式的处理事务。

   > 事务管理不侵入开发的组件。业务逻辑对象不会意识到正在事务管理之中。事务管理是属于系统层面的服务,不是业务逻辑的一部分。

   > 在不需要事务管理时,只要在配置文件上修改一下,即可移去事务管理服务,无需改变代码重新编辑,维护方便。

   > 注意: Spring声明式事务控制底层就是AOP

(3.2)引入坐标

     <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.10.RELEASE</version>
        </dependency>

 (3.3)编写业务代码

public class AccountDaoImpl implements AccountDao {

    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
        this.jdbcTemplate = jdbcTemplate;
    }

    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
    }

    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
    }
}

 

public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao){
        this.accountDao = accountDao;
    }

    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
// int i = 1/0; // 此处抛出异常,造成outMan的money 减少,而inMan的money 没有增加 accountDao.in(inMan,money); } }

 

public class AccountController {
    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = app.getBean(AccountService.class) ;
        accountService.transfer("tom","xiaoming",500);
    }
}

 (3.4)配置事务

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

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

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="accountDao" class="com.bearpx.spring.tx.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <bean id="accountService" class="com.bearpx.spring.tx.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

 

    // 配置平台事务管理器
<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="findAll" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"/> <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"/> <tx:method name="*"></tx:method> </tx:attributes> </tx:advice> // 配置事务的aop织入 <aop:config> <!--<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.bearpx.spring.tx.service.impl.*.*(..))"></aop:advisor>--> <aop:pointcut id="txPointcut" expression="execution(* com.bearpx.spring.tx.service.impl.*.*(..))"></aop:pointcut> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config>

 

(4)基于注解的声明式事务控制

(4.1)修改注解方式

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
    }

    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
    }
}

 

@Service("accountService")
@Transactional(isolation = Isolation.REPEATABLE_READ)        // 在类上设置事务属性
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;
    // 在方法上设置事务熟悉
//    @Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED)
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
//        int i = 1/0;
        accountDao.in(inMan,money);
    }
}

(4.2)修改配置文件

    <context:component-scan base-package="com.bearpx"/>   //组件扫描

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

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

    <bean id="jdbcTempalte" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    // 平台事务管理器
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
   // 事务的注解驱动
    <tx:annotation-driven transaction-manager="transactionManager"/>