Let's take a look at Spring's support for JDBC

1.JdbcTemplate

In order to make JDBC easier to use, Spring defines an abstraction layer on the JDBC API to build a JDBC access framework. As the core of the Spring JDBC framework, JdbcTemplate is designed to provide template methods for different types of JDBC operations. A template method can control the entire process and allow specific tasks in the process to be covered. In this way, the workload of database access can be minimized while retaining flexibility as much as possible.

1.1 Use JdbcTemplate to update the database

db.properties

jdbc.user=root
jdbc.password=0404
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3303/extra

jdbc.initPoolSize=5
jdbc.maxPoolSize=10

Configure in applicationContext.xml:

<!-- 导入资源文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置C3P0数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
  <property name="user" value="${jdbc.user}"/>
  <property name="password" value="${jdbc.password}"/>
  <property name="driverClass" value="${jdbc.driverClass}"/>
  <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
  <property name="initialPoolSize" value="${jdbc.initPoolSize}"/>
  <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>
</bean>
<!-- 配置spring的JdbcTemplate -->
<bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  <property name="dataSource" ref="dataSource"></property>
</bean>

Customer.java

package com.java.spring.jdbc;

public class Customer {
	private String ID;
	private String Name;
	private String EMAIL;
	private String BIRTH;
	public String getID() {
		return ID;
	}
	public void setID(String iD) {
		ID = iD;
	}
	public String getName() {
		return Name;
	}
	public void setName(String name) {
		Name = name;
	}
	public String getEMAIL() {
		return EMAIL;
	}
	public void setEMAIL(String eMAIL) {
		EMAIL = eMAIL;
	}
	public String getBIRTH() {
		return BIRTH;
	}
	public void setBIRTH(String bIRTH) {
		BIRTH = bIRTH;
	}
	@Override
	public String toString() {
		return "Customer [ID=" + ID + ", Name=" + Name + ", EMAIL=" + EMAIL + ", BIRTH=" + BIRTH + "]";
	}
}

Use JdbcTemplate to update the database:

private JdbcTemplate  jdbcTemplate=null;
jdbcTemplate=(JdbcTemplate) ctx.getBean("JdbcTemplate");
@Test
public void testUpdate(){
	String sql="UPDATE CUSTOMER SET EMAIL=? WHERE ID=?";
	jdbcTemplate.update(sql,"[email protected]",1);
	System.out.println("update successful!");
}

1.2 Use JdbcTemplate for batch modification

@Test
public void testBatchUpdate(){
	String sql="INSERT INTO CUSTOMER(ID,NAME,EMAIL,BIRTH) VALUES(?,?,?,?)";
	List<Object[]> batchArgs=new ArrayList<Object[]>();
	batchArgs.add(new Object[]{"7","A","[email protected]","1993-11-12"});
	batchArgs.add(new Object[]{"8","B","[email protected]","1995-10-03"});
	batchArgs.add(new Object[]{"9","C","[email protected]","1997-9-04"});
	batchArgs.add(new Object[]{"10","D","[email protected]","1992-12-08"});
	jdbcTemplate.batchUpdate(sql, batchArgs);
}

1.3 Get a record from the database and actually get a corresponding object

@Test
public void testQueryForObject(){
	String sql = "SELECT NAME,EMAIL,BIRTH FROM CUSTOMER WHERE ID=?";
	RowMapper<Customer> rowMapper = new BeanPropertyRowMapper<>(Customer.class);
	List<Customer> customer = jdbcTemplate.query(sql, rowMapper,5);
	System.out.println(customer);
}

1.4 Find a collection of entity classes

@Test
public void testQueryForList(){
	String sql="SELECT NAME,EMAIL,BIRTH FROM CUSTOMER WHERE ID>?";
	RowMapper<Customer> rowMapper = new BeanPropertyRowMapper<>(Customer.class);
	List<Customer> customers=jdbcTemplate.query(sql, rowMapper,"5");
	System.out.println(customers);
}

1.5 Get the value of a single column, or do a statistical query

@Test
public void testQueryForObject2(){
	String sql="SELECT COUNT(ID) FROM CUSTOMER";
	long count=jdbcTemplate.queryForObject(sql, Long.class);
	System.out.println(count);
}

2. Use named parameters in the JDBC template

In classic JDBC usage, SQL parameters are represented by placeholders? And are limited by location. The problem with positioning parameters is that once the order of the parameters changes, the parameter binding must be changed.

In the Spring JDBC framework, another option for binding SQL parameters is to use named parameters. Named parameters mean that SQL is specified by name (beginning with a colon) instead of by position. This method is easier to maintain, Also improves readability. Named parameters are only supported in NamedParameterJdbcTemplate.

Configure in xml:

<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>

 Update the database with named parameters:

private NamedParameterJdbcTemplate namedParameterJdbcTemplate=null;
namedParameterJdbcTemplate = ctx.getBean(NamedParameterJdbcTemplate.class);
@Test
public void testNamedParameterJdbcTemplate(){
	String sql="INSERT INTO CUSTOMER(NAME,EMAIL,BIRTH) VALUES(:name,:eMAIL,:bIRTH)";
	Customer customer=new Customer();
	customer.setName("D");
	customer.setEMAIL("[email protected]");
	customer.setBIRTH("1993-02-12");
	SqlParameterSource paramSource=new BeanPropertySqlParameterSource(customer);
	namedParameterJdbcTemplate.update(sql,paramSource);
}

3.Spring declarative transaction

3.1 Introduction

Transaction management is an indispensable technology in enterprise-level application development, used to ensure the integrity and consistency of data. Transactions are a series of actions, they are regarded as a single unit of work. These actions are all completed, or all Does not work. The four key attributes of the transaction (ACID):

  • Atomicity (atomicity): A transaction is an atomic operation consisting of a series of actions. The atomicity of a transaction ensures that the actions are either completed or completely inoperative.
  • Consistency: Once all transaction actions are completed, the transaction is committed. The data and resources are in a consistent state that meets business rules.
  • Isolation: There may be many transactions that process the same data at the same time, so every transaction should be isolated from other transactions to prevent data corruption.
  • Durability: Once the transaction is completed, no matter what system error occurs, its results should not be affected. Normally, the results of the transaction are written to persistent storage.

3.2 Transaction management in Spring

As an enterprise-level application framework, Spring defines an abstraction layer on top of different transaction management APIs. Application developers do not need to understand the underlying transaction management API to use Spring's transaction management mechanism. Spring supports both programmatic transactions Management, also supports declarative transaction management.

Programmatic transaction management: Embed transaction management code into business methods to control the commit and rollback of transactions. When programmatically managing transactions, you must include additional transaction management code in each transaction operation.

Declarative transaction management: In most cases, it is better than programmatic transaction management. It separates transaction management code from business methods and implements transaction management in a declarative manner. Transaction management as a cross-cutting concern can be Modularization through the AOP approach. Spring supports declarative transaction management through the Spring AOP framework.

3.3 Examples

If you need to achieve the following requirements: if you want to realize the user purchases the book, the book inventory and the user's account balance are updated correctly.

There are three data tables as follows: ISBN-> Book list number; STOCK-> Inventory; BALANCE-> Account balance; PRICE-> Book unit price;

Customer.java

package com.java.spring.jdbc;

public class Customer {
	private String ID;
	private String Name;
	private String EMAIL;
	private String BIRTH;
	public String getID() {
		return ID;
	}
	public void setID(String iD) {
		ID = iD;
	}
	public String getName() {
		return Name;
	}
	public void setName(String name) {
		Name = name;
	}
	public String getEMAIL() {
		return EMAIL;
	}
	public void setEMAIL(String eMAIL) {
		EMAIL = eMAIL;
	}
	public String getBIRTH() {
		return BIRTH;
	}
	public void setBIRTH(String bIRTH) {
		BIRTH = bIRTH;
	}
	@Override
	public String toString() {
		return "Customer [ID=" + ID + ", Name=" + Name + ", EMAIL=" + EMAIL + ", BIRTH=" + BIRTH + "]";
	}
}

BookShopDao.java

package com.java.spring.tx;

public interface BookShopDao {
	//根据书号获取书单价
	public int findBookPriceByIsbn(String isbn);
	//更新书的库存
	public void updateBookStock(String isbn);
	//更新用户的账户余额
	public void UserAccount(String username,int price);
}

BookShopDaoImpl.java

package com.java.spring.tx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("bookShopDao")
public class BookShopDaoImpl implements BookShopDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
	@Override
	public int findBookPriceByIsbn(String isbn) {
		String sql="SELECT PRICE FROM BOOK WHERE ISBN=?";
		return jdbcTemplate.queryForObject(sql,Integer.class,isbn);
	}

	@Override
	public void updateBookStock(String isbn) {
		String sql2="SELECT STOCK FROM BOOK_STOCK WHERE ISBN=?";
		int book_stock=jdbcTemplate.queryForObject(sql2,Integer.class,"1001");
		if(book_stock==0){
			throw new BookStockException("库存不足");
		}
		String sql="UPDATE BOOK_STOCK SET STOCK=STOCK-1 WHERE ISBN=?";
		jdbcTemplate.update(sql,isbn);
	}

	@Override
	public void UserAccount(String username, int price) {
		String sql2="SELECT BALANCE FROM ACCOUNT WHERE USERNAME=?";
		int balance=jdbcTemplate.queryForObject(sql2,Integer.class,"A");
		if(balance<price){
			throw new UserAccountException("余额不足");
		}
		String sql="UPDATE ACCOUNT SET BALANCE=BALANCE-? WHERE USERNAME=?";
		jdbcTemplate.update(sql,price,username);
	}

}

BookStockException.java

package com.java.spring.tx;

public class BookStockException extends RuntimeException{

	public BookStockException() {
		super();
	}

	public BookStockException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}

	public BookStockException(String message, Throwable cause) {
		super(message, cause);
	}

	public BookStockException(String message) {
		super(message);
	}

	public BookStockException(Throwable cause) {
		super(cause);
	}
	
}

UserAccountException.java

package com.java.spring.tx;

public class UserAccountException extends RuntimeException{

	public UserAccountException() {
		super();
	}

	public UserAccountException(String message, Throwable cause, boolean enableSuppression,
			boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}

	public UserAccountException(String message, Throwable cause) {
		super(message, cause);
	}

	public UserAccountException(String message) {
		super(message);
	}

	public UserAccountException(Throwable cause) {
		super(cause);
	}
	
}

BookShopServiceDao.java

package com.java.spring.tx;

public interface BookShopServiceDao {
	public void bookShopService(String username,String isbn);
}

BookShopServiceDaoImpl.java

package com.java.spring.tx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository("bookShopServiceDao")
public class BookShopServiceDaoImpl implements BookShopServiceDao {
	@Autowired
	private BookShopDao bookShopDao;
	@Transactional
	@Override
	public void bookShopService(String username, String isbn) {
		//查找书的价格
		int price=bookShopDao.findBookPriceByIsbn(isbn);
		//更新书的库存
		bookShopDao.updateBookStock(isbn);
		//更新用户余额
		bookShopDao.UserAccount(username, price);
	}

}

SpringTransactionTest.java

package com.java.spring.tx;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTransactionTest {
    private ApplicationContext ctx=null;
    private BookShopDaoImpl bookShopdao=null;
    private BookShopServiceDao bookShopServiceDao;
    {
    	ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
    	bookShopdao=ctx.getBean(BookShopDaoImpl.class);
    	bookShopServiceDao=(BookShopServiceDao) ctx.getBean("bookShopServiceDao");
    }
    @Test
    public void testBookShopService(){
    	bookShopServiceDao.bookShopService("A","1001");
    }
	@Test
	public void testFindBookPriceByIsbn() {
		System.out.println(bookShopdao.findBookPriceByIsbn("1001"));
	}

	@Test
	public void testUpdateBookStock() {
		bookShopdao.updateBookStock("1001");
		System.out.println("更新成功");
	}

	@Test
	public void testUserAccount() {
		bookShopdao.UserAccount("A",30);
	}

}

applicationContext.xml 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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 导入资源文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置C3P0数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
  <property name="user" value="${jdbc.user}"/>
  <property name="password" value="${jdbc.password}"/>
  <property name="driverClass" value="${jdbc.driverClass}"/>
  <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
  <property name="initialPoolSize" value="${jdbc.initPoolSize}"/>
  <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>
</bean>
<!-- 配置spring的JdbcTemplate -->
<bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  <property name="dataSource" ref="dataSource"></property>
</bean>
<context:component-scan base-package="com.java.spring"></context:component-scan>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

In the above code:

(1) An interface is defined in BookShopDao.java, which defines three methods, which are used to achieve the price of the book according to the book number, update the book inventory according to the book number, and update the user's account balance; Java is the implementation class of this interface, using JdbcTemplate to achieve database access operations, and call a custom exception method;

(2) An interface is defined in BookShopServiceDao.java, and the methods defined in the interface are used to provide account update operations for users to purchase books. BookShopServiceDaoImpl.java is the implementation class of this interface;

(3) @Transactional annotation is used to manage transactions declaratively in BookShopServiceDaoImpl.java. In order to define the method as supporting transaction processing, you can add @Transactional annotation to the method. According to the Spring AOP based proxy mechanism, only public methods can be marked. You can add the @Transactional annotation at the class level. When this annotation is applied to a class, all public methods in this class will be defined to support transaction processing. Configure the transaction manager in the Bean configuration file and enable <tx: Annotation-driven> element, and specify a transaction manager for it.

<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

(4) Test in SpringTransactionTest.java, when the user's account balance is not enough for the unit price of the book, an exception com.java.spring.tx.UserAccountException is thrown: the balance is insufficient.

If you do not add a declarative transaction method to the bookShopService method, when the user's account balance is insufficient, an exception com.java.spring.tx.UserAccountException is thrown: The balance is insufficient. Check the database at this time:

When the normal balance is insufficient, the book inventory cannot be reduced. As long as the bookShopService method is called, the method of updating the book inventory will be realized, that is, before determining whether the user balance is insufficient, the book inventory has been reduced by one (although the user has not This book), causing the user's information to be updated incorrectly. Therefore, transaction management needs to be added to ensure the integrity and consistency of the transaction. That is to say, "transactions are a series of actions, which are regarded as a single unit of work. These actions are either completed or all do not work."

public void bookShopService(String username, String isbn) {
		//查找书的价格
		int price=bookShopDao.findBookPriceByIsbn(isbn);
		//更新书的库存
		bookShopDao.updateBookStock(isbn);
		//更新用户余额
		bookShopDao.UserAccount(username, price);
	}

3.4 Communication behavior of affairs

When a transaction method is called by another transaction method, you must specify how the transaction should be propagated. For example: The method may continue to run in an existing transaction, or it may start a new transaction and run it in its own transaction. The propagation behavior of a transaction can be Specified by the propagation attribute. Spring defines 7 types of propagation behavior.

1267092-20181226105339836-1843464238.pngTransfer failed, re-upload canceled1267092-20181226105339836-1843464238.png Transfer failed, re-upload canceled1267092-20181226105339836-1843464238.png Transfer failed, re-upload canceled

3.4.1 Examples

Modify the database as follows so that the user balance is only enough to buy one book, not enough to pay for the second book:

Define Cashier interface, used to represent the user's checkout operation.

Cashier.java

package com.java.spring.tx;

import java.util.List;

public interface Cashier {
	public void checkout(String username,List<String> isbns);
}

CashierImpl.java

package com.java.spring.tx;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("cashier")
public class CashierImpl implements Cashier {
	private BookShopServiceDao bookShopService;
	@Transactional
	@Override
	public void checkout(String username, List<String> isbns) {
		for(String isbn:isbns){
			bookShopService.bookShopService(username, isbn);
		}
	}
}

Test in SpringTransactionTest.java, after running it once:

@Test
public void testCashier(){
	cashier.checkout("A",Arrays.asList("1001","1002"));
}

The account balance is not enough to pay the price of two books:

Test again, because the account balance is insufficient, so throw an exception: com.java.spring.tx.UserAccountException: insufficient balance.

Look at the database again:

It can be found that even if the balance is sufficient for the price of one of the books, the payment is not successful. This is because when bookServiceService () method of bookService is called by another transaction method checkout (), it will run in the existing transaction by default. The default propagation behavior is REQUIRED. Therefore, at the beginning and end of checkout () method There is only one transaction within the boundary. This transaction is only committed when the checkout () method ends, and as a result the user cannot buy a book.

Another common propagation behavior is REQUIRES_NEW. It means that the method must start a new transaction and run it in its own transaction (bookShopService ()). If there is a transaction (checkout ()) running, it should be suspended first . The transaction propagation attribute can be defined in the propagation attribute annotated by @Transactional, as shown below, the new transaction is opened in bookShopService,

@Transactional(propagation=Propagation.REQUIRES_NEW)
@Override
public void bookShopService(String username, String isbn) {
	//查找书的价格
	int price=bookShopDao.findBookPriceByIsbn(isbn);
	//跟新书的库存
	bookShopDao.updateBookStock(isbn);
	//更新用户余额
	bookShopDao.UserAccount(username, price);
}

After testing the database, the user's account balance paid for the first book.

3.5 The isolation level of the transaction

In theory, transactions should be completely isolated from each other to avoid problems caused by concurrent transactions. However, that would have a great impact on performance, because transactions must be run in order. In actual development, in order to improve performance, transactions will Run at a lower isolation level. The isolation level of a transaction can be specified through the isolation transaction attribute.

3.5.1 Transaction isolation level supported by Spring

1267092-20181226142447452-1509232326.pngDump failed weight

3.5.2 Set isolation transaction attributes

You can set the isolation level in the isolation property of @Transactional when managing transactions declaratively with the @Transactional annotation.

3.5.3 Set rollback transaction attributes

By default, only unchecked exceptions (RuntimeException and Error type exceptions) will cause the transaction to roll back. The checked exceptions will not. The transaction rollback rules can be defined by the @Transactional annotated rollbackFor and noRollbackFor attributes. These two attributes It is declared as Class [], so you can specify multiple exception classes for these two properties.

  • rollbackFor: rollback must be performed when encountered
  • noRollbackFor: a set of exception classes that must not be rolled back when encountered

3.5.4 Timeout and read-only attributes

Because transactions can acquire locks on rows and tables, long transactions consume resources and have an impact on overall performance. If a transaction only reads data but does not modify it, the database engine can optimize the transaction.

  • Timeout transaction attribute: How long a transaction can be kept before it is forced to roll back. This prevents long-running transactions from occupying resources
  • Read-only transaction attribute: indicates that this transaction only reads data but does not update data, which can help the database engine optimize transactions.

The timeout and read-only attributes can be defined in the @Transactional annotation. The timeout attribute is calculated in seconds.

@Transactional(propagation=Propagation.REQUIRES_NEW,readOnly=true,timeout=30)

 

wx search for "programmer koala", focus on the field of java, a public number that grows with you!

 

Published 13 original articles · Likes2 · Visits 572

Guess you like

Origin blog.csdn.net/weixin_40273144/article/details/105659786