Spring JDBCTemplate & declarative transaction

1. Spring's JdbcTemplate

1.1 What is JdbcTemplate?

JdbcTemplate is a template object provided in the spring framework, which is a simple encapsulation of primitive and cumbersome Jdbc API objects.

Core object

JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSource dataSource);

Core method

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

1.2 Spring integration of JdbcTemplate to realize transfer case

Step analysis

1. 创建java项目,导入坐标
2. 编写Account实体类
3. 编写AccountDao接口和实现类
4. 编写AccountService接口和实现类
5. 编写spring核心配置文件
6. 编写测试代码
1) Create a java project and import coordinates
<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>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>
2) Write the Account entity class
public class Account {
    private Integer id;
    private String name;
    private Double money;
    // setter getter....
}
3) Write AccountDao interface and implementation class
public interface AccountDao {
    // 转出操作
    public void out(String outUser, Double money);
    // 转入操作
    public void in(String inUser, Double money);
}
@Repository
public class AccountDaoImpl implements AccountDao {
	
    @Autowired
    private JdbcTemplate jdbcTemplate;

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

    public void in(String inUser, Double money) {
        String sql = "update account set money = money + ? where name = ?";
        jdbcTemplate.update(sql,money,inUser);
    }
}
4) Write AccountService interface and implementation class
public interface AccountService {
	public 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);       
        accountDao.in(inUser, money);
    }
}
5) Write the spring core configuration file
<?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">


    <!--开启注解扫描-->
    <context:component-scan base-package="com.lagou"></context:component-scan>


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


    <!--配置DataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>


    <!--把JdbcTemplate交给IOC容器-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    	<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>
    
</beans>
6) Write test code
Spring integrates Junit

In ordinary test classes, developers need to manually load the configuration file and create the Spring container, and then obtain the Bean instance through the Spring related API; if this is not done, the object cannot be obtained from the container.

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = applicationContext.getBean(AccountService.class);

We can make SpringJunit responsible for creating the Spring container to simplify this operation. Developers can directly inject the Bean instance into the test class; but need to tell it the name of the configuration file.

Step analysis

1. 导入spring集成Junit的坐标
	<artifactId>spring-test<artifactId>
	<artifactId>junit<artifactId>
2. 使用@Runwith注解替换原来的运行器
3. 使用@ContextConfiguration指定配置文件或配置类
4. 使用@Autowired注入需要测试的对象
5. 创建测试方法进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml") // 加载spring核心配置文件
// @ContextConfiguration(classes = {SpringConfig.class}) // 加载spring核心配置类
public class AccountServiceTest {
    
    @Autowired
    private AccountService accountService;
    
    @Test
    public void testTransfer() throws Exception {
    	accountService.transfer("tom", "jerry", 100d);
    }
}

problem

The transfer-out and transfer-in operations are all an independent transaction. If an exception occurs in the middle, the transfer will be wrong (deducted money successfully but failed to receive money)

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void transfer(String outUser, String inUser, Double money) {

        // 调用了减钱方法
        accountDao.out(outUser,money);

        int i= 1/0; // 异常

        // 调用了加钱方法
        accountDao.in(inUser,money);
    }
}

analysis

The above code transaction is in the dao layer, and the transfer-out and transfer-in operations are all independent transactions, but in actual development, the business logic should be controlled in one transaction, so the transaction should be moved to the service layer.

2. Spring's affairs

2.1 Transaction control method in Spring

Spring's transaction control can be divided into programmatic transaction control and declarative transaction control.

Programmatic : The developer directly couples the transaction code and business code together, and does not need to be used in actual development.

Declarative : The developer uses configuration to achieve transaction control, business code and transaction code are decoupled, and the AOP idea used.

2.2 XML-based declarative transaction control

Declarative transaction processing in the Spring configuration file instead of code processing transaction. The bottom layer is implemented using AOP ideas.

Declarative transaction control clear matters :

  • Core business code (target object) (Who is the entry point?)
  • Transaction enhancement code (Spring has provided a transaction manager) (Who is notified?)
  • Aspect configuration (how to configure the aspect?)

2.2.1 Use spring declarative transaction to control transfer business

Step analysis

1. 引入tx命名空间
2. 事务管理器通知配置
3. 事务管理器AOP配置
4. 测试事务控制转账业务代码
1) Introduce tx namespace
<?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) Transaction manager notification configuration
<!--事务管理器-->
<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>
3) Transaction manager AOP configuration
<!--aop配置-->
<aop:config>
    <!--切面配置-->
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lagou.serivce..*.*(..))">
    </aop:advisor>
</aop:config>
4) Test transaction control transfer business code
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:applicationContext.xml"})
public class AccountServiceImplTest {

    @Autowired
    private AccountSerivce accountSerivce;

    @Test
    public void testTransfer(){
        accountSerivce.transfer("tom","jerry",100d);
    }
}

2.2.2 Detailed configuration of transaction parameters

<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
* name:切点方法名称
* isolation:事务的隔离级别
* propogation:事务的传播行为
* timeout:超时时间 默认值-1 没有超时限制;如果有 以秒为单位进行设置
* read-only:是否只读 建议查询时设置为只读
Transaction isolation level

Setting the isolation level can solve the problems caused by transaction concurrency, such as dirty reads, non-repeatable reads, and virtual reads (phantom reads).

Transaction and transaction isolation level

problem

Dirty read

The so-called dirty read, refers to the Transaction A Transaction B reads the data has not been submitted . For example, when the bank withdraws money, transaction A opens the transaction, then it switches to transaction B, transaction B opens the transaction -> withdraw 100 yuan, then switches back to transaction A, transaction A must read the original data in the database, because the transaction B took away 100 yuan and did not submit it. The accounting balance in the database must still be the original balance, which is a dirty read.

Non-repeatable

The so-called non-repeatable read means that a certain data is read twice in a transaction, and the data read is inconsistent . Take the bank withdrawal as an example. Transaction A opens the transaction -> the bank card balance is found to be 1000 yuan, then switch to transaction B, transaction B opens the transaction -> transaction B takes away 100 yuan -> submit, the balance in the database becomes 900 yuan, switch back to transaction A at this time, transaction A checks again to find out that the account balance is 900 yuan, so for transaction A, the account balance data read twice in the same transaction is inconsistent, which is non-repeatable reading.

Phantom reading

The so-called phantom read refers to the discovery of unoperated data during an operation in a transaction . For example, for student information, transaction A opens transaction -> modify the sign-in status of all students on the day to false, then switch to transaction B, transaction B opens transaction -> transaction B inserts a piece of student data, then switches back to transaction A, and transaction A submits When I found a piece of data that I had not modified, this is a phantom reading, as if an illusion occurred. The premise for the appearance of phantom reading is that there are transactions that have inserted and deleted operations in concurrent transactions.

Isolation level

DEFAULT default isolation level

The transaction isolation level supported by each database is different. If Spring configures the transaction to set isolation to this value, then the default transaction isolation level of the underlying database will be used. MySQL defaults to REPEATABLE_READ, Oracle defaults to READ_COMMITED.

READ_UNCOMMITTED read uncommitted

That is, data that has not been submitted can be read, so it is obvious that this level of isolation mechanism cannot solve any of dirty reads, non-repeatable reads, and phantom reads, so it is rarely used.

READ_COMMITED read has been submitted

That is, to be able to read the data that has been submitted, it can naturally prevent dirty reads, but cannot restrict non-repeatable reads and phantom reads.

REPEATABLE_READ repeated read

That is to add a lock after the data is read, similar to "select * from XXX for update", it is clear that the data is read out for updating, so a lock should be added to prevent others from modifying it. The meaning of REPEATABLE_READ is similar. Once a piece of data is read, this transaction cannot be ended, and other transactions cannot change this record. This solves the problem of dirty reads and non-repeatable reads, but the problem of phantom reads still cannot be solved.

SERLALIZABLE serialization

The highest transaction isolation level, no matter how many transactions, all sub-transactions of one transaction can be executed after all sub-transactions of one transaction are run one by one. This solves the problems of dirty reads, non-repeatable reads and phantom reads, but The efficiency is too low.

Transaction propagation behavior

Transaction propagation behavior refers to how transaction control should be performed when a business method is called by another business method.

  • REQUIRED : (default value) If there is no transaction currently, create a new transaction, if there is already a transaction, join it.
  • SUPPORTS : support the current transaction, if there is no transaction currently, it will be executed in a non-transactional manner.
CRUD common configuration

save*: Unified configuration starting with save

<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>