Spring事务管理与模板对象

1.事务管理

1.事务回顾

事务指数据库中多个操作合并在一起形成的操作序列

事务的作用

当数据库操作序列中个别操作失败时,提供一种方式使数据库状态恢复到正常状态(A),保障数据库即使在异常状态下仍能保持数据一致性(C)(要么操作前状态,要么操作后状态)。

当出现并发访问数据库时,在多个访问间进行相互隔离,防止并发访问操作结果互相干扰(I

事务的特征(ACID)

原子性(Atomicity)指事务是一个不可分割的整体,其中的操作要么全执行或全不执行

一致性(Consistency)事务前后数据的完整性必须保持一致

隔离性(Isolation)事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离

持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

事务的隔离级

脏读:允许读取未提交的信息

原因:Read uncommitted

解决方案: (表级读锁)

不可重复读:读取过程中单个数据发生了变化

解决方案: Repeatable read (行级写锁)

幻读:读取过程中数据条目发生了变化

解决方案: Serializable(表级写锁)

2.Spring事务核心对象

J2EE开发使用分层设计的思想进行,对于简单的业务层转调数据层的单一操作,事务开启在业务层或者数据层并无太大差别,当业务中包含多个数据层的调用时,需要在业务层开启事务,对数据层中多个操作进行组合并归属于同一个事务进行处理

Spring为业务层提供了整套的事务解决方案

        PlatformTransactionManager

        TransactionDefinition

        TransactionStatus

PlatformTransactionManager(平台事务管理器)

这是一个接口

平台事务管理器实现类:

DataSourceTransactionManager   适用于Spring JDBC或MyBatis

HibernateTransactionManager   适用于Hibernate3.0及以上版本

JpaTransactionManager   适用于JPA

JdoTransactionManager   适用于JDO

JtaTransactionManager   适用于JTA

JPA(Java Persistence API)Java EE 标准之一,为POJO提供持久化标准规范,并规范了持久化开发的统一API,符合JPA规范的开发可以在不同的JPA框架下运行

JDO(Java Data Object )是Java对象持久化规范,用于存取某种数据库中的对象,并提供标准化API。与JDBC相比,JDBC仅针对关系数据库进行操作,JDO可以扩展到关系数据库、文件、XML、对象数据库(ODBMS)等,可移植性更强

JTA(Java Transaction API)Java EE 标准之一,允许应用程序执行分布式事务处理。与JDBC相比,JDBC事务则被限定在一个单一的数据库连接,而一个JTA事务可以有多个参与者,比如JDBC连接、JDO 都可以参与到一个JTA事务中

此接口定义了事务的基本操作

获取事务

TransactionStatus getTransaction(TransactionDefinition definition)

提交事务 

void commit(TransactionStatus status) 

回滚事务 

void rollback(TransactionStatus status)

TransactionDefinition(事务定义的接口)

此接口定义了事务的基本信息

获取事务定义名称

String getName()

获取事务的读写属性

boolean isReadOnly()

获取事务隔离级别

int getIsolationLevel()

获事务超时时间

int getTimeout()

获取事务传播行为特征

int getPropagationBehavior()

TransactionStatus(事务状态的接口)

此接口定义了事务在执行过程中某个时间点上的状态信息及对应的状态操作

获取事务是否处于新开启事务状态

boolean isNewTransaction()

获取事务是否处于已完成状态

boolean isCompleted()

获取事务是否处于回滚状态

boolean isRolbackOnly()

刷新事务状态

void flush()

获取事务是否具有回滚存储点

boolean hasSavepoint()

设置事务处于回滚状态

void setRollbackOnly()

3.事务控制方式

编程式

声明式(XML)

声明式(注解)

4.案例环境

银行转账业务说明

银行转账操作中,涉及从A账户到B账户的资金转移操作。数据层仅提供单条数据的基础操作,未设计多账户间的业务操作

package com.dao;

import org.apache.ibatis.annotations.Param;


public interface AccountDao {
    /*
    * 入账操作
    * name  入账用户名
    * money  入账金额
    */
    void inMoney(@Param("name") String name, @Param("money") Double money);

    /*
     * 入账操作
     * name  出账用户名
     * money  出账金额
     */
    void outMoney(@Param("name") String name, @Param("money") Double money);

}
package com.domain;

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

public interface AccountService {
    /*
    * 转账操作
    * outName  出账用户名
    * inName  入账用户名
    * money   转账金额
    */
    public void transfer(String outName, String inName, Double money);
}
package com.service.impl;

import com.dao.AccountDao;
import com.service.AccountService;

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;

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

    @Override
    public void transfer(String outName, String inName, Double money) {
        accountDao.inMoney(outName,money);
        accountDao.outMoney(inName,money);
    }
}

<?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 https://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder location="classpath:*.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <bean id="accountService" class="com.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.domain"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.dao"/>
    </bean>
</beans>
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD约束-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.AccountDao">
    <update id="inMoney">
        update account set money = money + #{money} where name = #{name}
    </update>

    <update id="outMoney">
        update account set money = money - #{money} where name = #{name}
    </update>
</mapper>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
jdbc.username=root
jdbc.password=123456

5.使用AOP控制事务 (XML)

将业务层的事务处理功能抽取出来制作成AOP通知,利用环绕通知运行期动态织入

范例

public Object tx(ProceedingJoinPoint pjp) throws Throwable {
    DataSourceTransactionManager dstm = new DataSourceTransactionManager();
    dstm.setDataSource(dataSource);
    TransactionDefinition td = new DefaultTransactionDefinition();
    TransactionStatus ts = dstm.getTransaction(td);
    Object ret = pjp.proceed(pjp.getArgs());
    dstm.commit(ts);
    
    return ret;
}
<bean id="txAdvice" class="com.aop.TxAdvice">
	<property name="dataSource" ref="dataSource"/>
</bean>

使用aop:advisor在AOP配置中引用事务专属通知类

<aop:config>
    <aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
    <aop:aspect ref="txAdvice">
        <aop:around method="tx" pointcut-ref="pt"/>
    </aop:aspect>
</aop:config>

样例

package com.service.impl;

import com.dao.AccountDao;
import com.service.AccountService;
import javafx.application.Platform;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.sql.DataSource;

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    /*private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }*/

    @Override
    public void transfer(String outName, String inName, Double money) {
       /* //开启事务
        PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
        //事务定义
        TransactionDefinition td = new DefaultTransactionDefinition();
        //事务状态
        TransactionStatus ts = ptm.getTransaction(td);*/
        accountDao.inMoney(outName,money);
        //int i = 1/0;
        accountDao.outMoney(inName,money);
        /*ptm.commit(ts);*/
    }
}

package com.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.sql.DataSource;

public class TxAdvice {
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Object transactionManager(ProceedingJoinPoint pjp) throws  Throwable{

        //开启事务
        PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
        //事务定义
        TransactionDefinition td = new DefaultTransactionDefinition();
        //事务状态
        TransactionStatus ts = ptm.getTransaction(td);
        Object ret = pjp.proceed(pjp.getArgs());
        ptm.commit(ts);
        return ret;
    }
}
 <bean id="txAdvice" class="com.aop.TxAdvice">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* *..taransfer(..))"/>
        <aop:aspect ref="txAdvice">
            <aop:around method="transactionManager" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>

6.声明式事务(XML)

tx配置----tx:advice

名称:tx:advice

类型:标签

归属:beans标签

作用:专用于声明事务通知

格式

<beans>
    <tx:advice id="txAdvice" transaction-manager="txManager">
    </tx:advice>
</beans>

基本属性:

id :用于配置aop时指定通知器的id

transaction-manager :指定事务管理器bean

tx配置----tx:attributes

名称:tx:attributes

类型:标签

归属:tx:advice标签

作用:定义通知属性

格式

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
    </tx:attributes>
</tx:advice>

基本属性:无

tx配置----tx:method

名称:tx:method

类型:标签

归属:tx:attribute标签

作用:设置具体的事务属性

格式

<tx:attributes>
    <tx:method name="*" read-only="false" />
    <tx:method name="get*" read-only="true" />
</tx:attributes>

说明:通常事务属性会配置多个,包含一个读写的全事务属性,一个只读的查询类事务属性 

样例

开启tx命名空间 

<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: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/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--定义事务管理的通知类-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!--定义控制的事务-->
        <tx:attributes>
            <tx:method name="*" read-only="false"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find" read-only="true"/>
            <tx:method name="transfer" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.service.*Service.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
    </aop:config>

 此时aop.TxAdvice就可以删除了

<!--<bean id="txAdvice" class="com.aop.TxAdvice">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
        <aop:aspect ref="txAdvice">
            <aop:around method="transactionManager" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>-->

此段也要删除

aop:advice与aop:advisor区别

aop:advice配置的通知类可以是普通java对象,不实现接口,也不使用继承关系

aop:advisor配置的通知类必须实现通知接口

        MethodBeforeAdvice

        AfterReturningAdvice

        ThrowsAdvice

        ……

7.tx:method属性

<tx:method
        name="*"			待添加事务的方法名表达式(支持*号通配符)
        read-only="false"	设置事务的读写属性,true为只读,false为读写
        timeout="-1"		设置事务超时时长,单位秒
        isolation="DEFAULT"	设置事务隔离级别,该隔离级别设定是基于Spring的设定,非数据库端
        no-rollback-for="java.lang.ArithmeticException"	设置事务中不回滚的异常,多个异常间使用,分割
        rollback-for=""			设置事务中必回滚的异常,多个异常间使用,分割
        propagation="REQUIRED"	设置事务的传播行为
        />

8.事务传播行为 

事务传播行为描述的是事务协调员对事务管理员所携带事务的处理态度

企业开发过程中,发现同属于同一个事务控制的各个业务中,如果某个业务与其他业务隔离度较高,拥有差异化的数据业务控制情况,通常使用事务传播行为对其进行控制

9.声明式事务(注解)

@Transactional

名称:@Transactional

类型:方法注解,类注解,接口注解

位置:方法定义上方,类定义上方,接口定义上方

作用:设置当前类/接口中所有方法或具体方法开启事务,并指定相关事务属性

范例

@Transactional(
    readOnly = false,
    timeout = -1,
    isolation = Isolation.DEFAULT,
    rollbackFor = {ArithmeticException.class, IOException.class},
    noRollbackFor = {},
    propagation = Propagation.REQUIRES_NEW
)

tx:annotation-driven

名称:tx:annotation-driven

类型:标签

归属:beans标签

作用:开启事务注解驱动,并指定对应的事务管理器

范例

<tx:annotation-driven transaction-manager="txManager"/>

样例

<?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: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/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:property-placeholder location="classpath:*.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <bean id="accountService" class="com.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <!--<property name="dataSource" ref="dataSource"/>-->
    </bean>
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.domain"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.dao"/>
    </bean>

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:annotation-driven transaction-manager="txManager"/>

   <!-- <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="*" read-only="false"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find" read-only="true"/>
            <tx:method name="transfer" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.service.*Service.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
    </aop:config>-->
</beans>

package com.service;

import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional(isolation = Isolation.DEFAULT)
public interface AccountService {
    /*
    * 转账操作
    * outName  出账用户名
    * inName  入账用户名
    * money   转账金额
    */
    @Transactional(
            readOnly = false,
            timeout = -1,
            isolation = Isolation.DEFAULT,
            rollbackFor = {},  //java.lang.ArithmeticException.class, IOException.class
            noRollbackFor = {},
            propagation = Propagation.REQUIRED
    )
    public void transfer(String outName, String inName, Double money);
}

10.声明式事务(纯注解驱动)

名称:@EnableTransactionManagement

类型:类注解

位置:Spring注解配置类上方

作用:开启注解驱动,等同XML格式中的注解驱动

范例

@Configuration
@ComponentScan("com.lichee")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class,TransactionManagerConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}
public class TransactionManagerConfig {
    @Bean
    public PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}

样例

package com.domain;

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

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;


public interface AccountDao {
    @Update("update account set money = money + #{money} where name = #{name}")
    void inMoney(@Param("name") String name, @Param("money") Double money);


    @Update("update account set money = money - #{money} where name = #{name}")
    void outMoney(@Param("name") String name, @Param("money") Double money);

}
package com.service;

import org.springframework.transaction.annotation.Transactional;
@Transactional
public interface AccountService {
    public void transfer(String outName, String inName, Double money);
}
package com.service.impl;

import com.dao.AccountDao;
import com.service.AccountService;
import org.apache.ibatis.annotations.Arg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    public void transfer(String outName, String inName, Double money) {
        accountDao.inMoney(outName,money);
        //int i = 1/0;
        accountDao.outMoney(inName,money);
    }
}
package com.config;

import org.springframework.context.annotation.*;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan("com")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}
package com.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

public class JDBCConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;
    @Bean("dataSource")
    public DataSource getDataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
    public PlatformTransactionManager gerTransactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}
package com.config;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MyBatisConfig {
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }
    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.dao");
        return msc;
    }
}

jdbc.properties 

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
jdbc.username=root
jdbc.password=123456
package com.service;

import com.config.SpringConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


//设定spring专用的类加载器
@RunWith(SpringJUnit4ClassRunner.class)
//设定加载的spring上下文对应的配置
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {
    @Autowired
    private AccountService accountService;
    @Test
    public void testTransfer(){
       accountService.transfer("Jock1","Jock2",100D);
    }
}

2.模板对象

1.Spring模板对象

TransactionTemplate

JdbcTemplate

RedisTemplate

RabbitTemplate

JmsTemplate

HibernateTemplate

RestTemplate

2.JdbcTemplate

提供标准的sql语句提供API

public void save(Account account) {
    String sql = "insert into account(name,money)values(?,?)";
    jdbcTemplate.update(sql,account.getName(),account.getMoney());
}

3.NamedParameterJdbcTemplate 

提供标准的sql语句提供API

public void save(Account account) {
    String sql = "insert into account(name,money)values(:name,:money)";
    Map pm = new HashMap();
    pm.put("name",account.getName());
    pm.put("money",account.getMoney());
    jdbcTemplate.update(sql,pm);
}

4.RedisTemplate

RedisTemplate对象结构

public void changeMoney(Integer id, Double money) {
    redisTemplate.opsForValue().set("account:id:"+id,money);
}
public Double findMondyById(Integer id) {
    Object money = redisTemplate.opsForValue().get("account:id:" + id);
    return new Double(money.toString());
}

3.事务底层原理解析

策略模式(Strategy Pattern)使用不同策略的对象实现不同的行为方式,策略对象的变化导致行为的变化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/433585.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Netty权威指南——基础篇5 IO基础 备份

1 什么是I/O 在UNIX世界里一切皆是文件&#xff0c;而文件是什么&#xff1f;文件就是一串二进制流而已&#xff0c;其实不管是Socket&#xff0c;还是FIFO&#xff08;先进先出队列&#xff09;、管道、终端。对计算机来说&#xff0c;一切都是文件&#xff0c;一切都是流。在…

JSP实现数据传递与保存

1.HTML页面转换JSP页面 直接再HTML页面最顶端添加page指令&#xff0c;修改文件后缀名&#xff1b;反之&#xff1b; 2.JSP内置对象 对象 描述 request 每当客户端请求JSP页面时&#xff0c;JSP引擎会制造一个新的request对象来代表这个请求。 response 当服务器创建req…

内存溢出(OOM)-汇总指南

目录 java.lang.OutOfMemoryError:Java heap space 原因分析 示例 解决方案 java.lang.OutOfMemoryError:GC overhead limit exceeded 原因分析 示例 解决方案 java.lang.OutOfMemoryError:Permgen space 原因分析 示例 解决方案 java.lang.OutOfMemoryError:Metas…

Learn C the hard way :一些简单问题处理

Preface&#xff1a;本次学习主要是以复习态度而来&#xff0c;意在加强C语言的理解&#xff0c;本文主要针对于网站中出现的一些问题 我的简单Makefile&#xff1a; CFLAGS-Wall cc gcc cflag -Wall -g allfiles ex1.c ex2.c ex3.c ex4.c ex5.c ex4-1.c objs $(allfiles:.c…

Claude 3击败GPT-4,地表最强AI易主,坐等翻车

北京时间3月4日晚22点00分&#xff0c;Claude 3发布了&#xff0c;一举超越了GPT-4&#xff01; 震惊&#xff0c;居然有人能超越霸主地位&#xff0c;赶紧进官网详细的看一下&#xff1a; https://www.anthropic.com/news/claude-3-family 刷到第一的成绩单 Claude3 Opus的…

MyBatis操作数据库(SQL注入)

本文主要来讲解6大标签&#xff0c;以便更好的MyBatis操作数据库&#xff01; <if>标签<trim>标签<where>标签<set>标签<foreach>标签<include>标签 前提需求&#xff1a; MyBatis是一个持久层框架&#xff0c;和Spring没有任何关系&…

java VR全景商城 saas商城 b2b2c商城 o2o商城 积分商城 秒杀商城 拼团商城 分销商城 短视频商城 小程序商城搭建

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

Nodejs 第五十章(lua的基本使用)

lua基本使用 全局变量局部变量 全局变量是在全局作用域中定义的变量&#xff0c;可以在脚本的任何地方访问。全局变量在定义时不需要使用关键字&#xff0c;直接赋值即可。 xiaoman xmzsprint(xiaoman)局部变量是在特定作用域内定义的变量&#xff0c;只能在其所属的作用域…

腾讯云4核8G12M轻量服务器优惠价格446元一年,646元15个月

腾讯云4核8G12M轻量服务器优惠价格446元一年&#xff0c;646元15个月&#xff0c;180GB SSD云硬盘&#xff0c;2000GB月流量。 一张表看懂腾讯云服务器租用优惠价格表&#xff0c;一目了然&#xff0c;腾讯云服务器分为轻量应用服务器和云服务器CVM&#xff0c;CPU内存配置从2核…

【笔记】Android ServiceStateTracker 网络状态变化逻辑及SPN更新影响

业务简介 在网络状态变化的时候&#xff08;数据或WiFi&#xff09;&#xff0c;会更新SPN。 基于Android U的代码分析。 分类&#xff1a;SPN Data_Dic-的博客-CSDN博客 功能逻辑 状态说明 飞行模式下注册上WFC的话&#xff0c;注册状态MD上报 regState: NOT_REG_MT_NOT…

SpringBoot 自定义映射规则resultMap collection一对多

介绍 collection是封装一对多关系的&#xff0c;通常情况下是一个列表&#xff0c;association是一对一&#xff0c;通常情况是一个对象。例如&#xff1a;查询班级下所有的学生&#xff0c;一个班级可以有多个学生&#xff0c;这就是一对多。 案例 有一个学生表&#xff0c…

NineData云原生智能数据管理平台新功能发布|2024年2月版

SQL开发&#xff1a;全功能支持百度云 GaiaDB 介绍&#xff1a;支持通过 SQL 开发所有能力管理 GaiaDB 实例。更多信息&#xff0c;请参见&#xff1a;真香&#xff01;NineData SQL 开发全面适配 GaiaDB 场景&#xff1a;企业使用 GaiaDB 管理企业数据&#xff0c;需要一个一…

第五篇:组件更新:完整的 DOM diff 流程是怎样的?(下)

下面我们来继续讲解上节课提到的核心 diff 算法。 新子节点数组相对于旧子节点数组的变化&#xff0c;无非是通过更新、删除、添加和移动节点来完成&#xff0c;而核心 diff 算法&#xff0c;就是在已知旧子节点的 DOM 结构、vnode 和新子节点的 vnode 情况下&#xff0c;以较…

Sora核心之一:可变时长、分辨率、尺寸

Overview 一、总览二、摘要三、引言四、方法4.1、架构改动4.2、训练改变4.3、NaViT的效率 NaViT 一、总览 题目: Patch n’ Pack: NaViT, a Vision Transformer for any Aspect Ratio and Resolution 机构&#xff1a;Google DeepMind 论文: https://arxiv.org/pdf/2307.06304…

python72-Python的函数入门,为函数提供文档

之前介绍过可以使用Python内置的help()函数查看其他函数的帮助文档,我们也经常通过help()函数查看指定函数的帮助信息&#xff0c;这对于Python开发者来说非常重要。 我们还可以为函数编写说明文档一只要把一段字符串放在函数声明之后、函数体之前&#xff0c;这段字符串将被作…

LVGL:切换页面

static lv_obj_t *contanier1 NULL; static lv_obj_t *contanier2 NULL;static void win_btn_event_callback(lv_event_t* e) {lv_event_code_t code lv_event_get_code(e);if (code LV_EVENT_CLICKED){lv_obj_t * obj lv_event_get_target(e);//按钮if(lv_obj_get_child(co…

回溯算法03-电话号码的字母组合(Java)

3.电话号码的字母组合 题目描述 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;d…

#define MODIFY_REG(REG, CLEARMASK, SETMASK)

#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) 这个宏 MODIFY_REG 是在嵌入式编程中&#xff0c;它用于修改一个寄存器的特定位&#xff0c;而不影响其他位。这个宏接受三个参数&#xff…

onav_rim 复现记录

onav_rim 复现记录 任务复现过程克隆项目&#xff0c;创建环境源码安装habitat-sim从github上安装CLIP环境配置收尾工作数据集下载模型评估其他问题训练训练模型 任务 上次复现one4all失败&#xff0c;但我就是想看看我的电脑能不能做end2end的视觉导航任务。这次看到了《Obje…

Java多线程——信号量Semaphore是啥

目录 引出信号量Semaphore &#xff1f;Redis冲冲冲——缓存三兄弟&#xff1a;缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出 Java多线程——信号量Semaphore是啥 信号量Semaphore &#xff1f; Semaphore 通常我们叫它信号量&#xff0c; 可以用来控制同时访问特…