JdbcTemplate in Spring
JdbcTemplate overview
It is an object provided in the spring framework and a simple encapsulation of the original Jdbc API object. The spring framework provides us with many operation template classes.
- Operation of relational data:
- JdbcTemplate
- HibernateTemplate
- To operate nosql database:
- RedisTemplate
- Operation message queue:
- JmsTemplate
Environment construction
jar package to be imported (version can be selected):
- spring-jdbc-5.1.9.RELEASE.jar(JDBC)
- spring-tx-5.1.9.RELEASE.jar (about transactions)
- spring-context-5.1.9.RELEASE.jar(IOC)
Write 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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
Configure data source (Spring built-in data source)
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"></property> <property name="username" value="root"></property> <property name="password" value="password"></property> </bean>
Configure JdbcTemplate
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean>
Operation example
//Entity class public class UserManager { String username, password; double money; @Override public String toString() { return "UserManager{" + "username='" + username + '\'' + ", password='" + password + '\'' + ", money=" + money + '}'; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } }
//Get Spring container ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //Get bean object based on id JdbcTemplate jt = (JdbcTemplate)ac.getBean("jdbcTemplate"); //query //BeanPropertyRowMapper encapsulates each row of the query into an object and puts it into the collection //Here, the queried username, password and money attributes are encapsulated into a UserManager class List<UserManager> userManagers = jt.query("select * from user", new BeanPropertyRowMapper<UserManager(UserManager.class)); //delete jt.update("delete from user where username = ?","aaa"); //insert jt.update("insert into user(username,password,money)values(?,?)","aaa","fff",5000); //to update jt.update("update user set money = money-? where username = ?",300,"aaa");
Using JdbcTemplate in DAO
The entity class is shown above
The first way
Define the JdbcTemplate in the DAO class
DAO class of UserManager
public class UserManagerDao implements IUserManagerDao { JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public UserManager FindUserByName(String username) { List<UserManager> userManagers = jdbcTemplate.query("select * from user where username=?", new BeanPropertyRowMapper<UserManager>(UserManager.class), username); return userManagers.isEmpty()?null:userManagers.get(0); } @Override public List<UserManager> FindAll() { return jdbcTemplate.query("select * from user", new BeanPropertyRowMapper<UserManager>(UserManager.class)); } @Override public void UpdateUserByName(UserManager userManager) { jdbcTemplate.update("update user set password=?, money=? where username=?", userManager.getPassword(), userManager.getMoney(), userManager.getUsername()); } }
Configure the DAO class through the xml file:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"></property> <property name="username" value="root"></property> <property name="password" value="password"></property> </bean> <bean id="userManagerDao" class="com.dao.Impl.UserManagerDao"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean>
There is a small problem. When there are too many DAO classes, the following code will have a lot of duplicate code:
JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; }
The second way
Let DAO class inherit JdbcDaoSupport
JdbcDaoSupport is a class provided to us by the spring framework. A jdbctemplate object is defined in this class, which can be obtained and used directly. We only need to inherit this class. Moreover, at this time, we don't need to inject JDBC template into xml file. Instead, we can directly inject a dataSource object into UserManagerDao class (because JdbcDaoSupport class has the set method of dataSource, it will automatically create jdbctemplate with dataSource). You can see the xml file configuration method
public class UserManagerDao extends JdbcDaoSupport implements IUserManagerDao { @Override public UserManager FindUserByName(String username) { List<UserManager> userManagers = super.getJdbcTemplate().query("select * from user where username=?", new BeanPropertyRowMapper<UserManager>(UserManager.class), username); return userManagers.isEmpty()?null:userManagers.get(0); } @Override public List<UserManager> FindAll() { return super.getJdbcTemplate().query("select * from user", new BeanPropertyRowMapper<UserManager>(UserManager.class)); } @Override public void UpdateUserByName(UserManager userManager) { super.getJdbcTemplate().update("update user set password=?, money=? where username=?", userManager.getPassword(), userManager.getMoney(), userManager.getUsername()); } }
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"></property> <property name="username" value="root"></property> <property name="password" value="password"></property> </bean> <bean id="userManagerDao" class="com.dao.Impl.UserManagerDao"> <property name="dataSource" ref="dataSource"></property> </bean>
contrast
When using the second method, the code redundancy is indeed reduced. However, when using annotation configuration, because the JdbcDaoSupport class is a Spring class, we cannot modify it. Therefore, the second method can only be used when using xml file configuration, and the first method can be used when using annotation.
Transaction control in Spring
Overview of transaction control in Spring
Transaction control is to keep the transactions composed of a series of actions atomic, consistent, isolated and persistent.
- Atomicity: a transaction is an atomic operation consisting of a series of actions. The atomicity of the transaction ensures that the action is either fully completed or completely ineffective.
- Consistency: once the transaction is completed (whether successful or failed), the system must ensure that the business it models is in a consistent state, rather than partial completion and partial failure. In reality, data should not be destroyed.
- Isolation: many transactions may process the same data at the same time, so each transaction should be isolated from other transactions to prevent data corruption.
- Durability: once the transaction is completed, no matter what system error occurs, its result should not be affected, so it can recover from any system crash. Typically, the result of a transaction is written to persistent storage.
A classic example is bank transfer, which is divided into several steps: the transfer starts, the account deducts money, the account adds money, and the transfer ends. However, once the account has been deducted, some abnormal transfer behavior occurs in the system and cannot continue. If there is no transaction control, the transfer will be forcibly ended, and the transaction control will maintain its consistency, start the rollback transaction, and restore all information to the state before the transfer.
Spring's transaction control is based on AOP, which is divided into two types: declarative transaction control (i.e. implemented by configuration) and programmatic transaction control (i.e. implemented by programming).
Introduction to Spring transaction control API
PlatformTransactionManager
Transaction manager
TransactionStatus getTransaction();//Get the status information of the transaction void commit();//Commit transaction void rollback();//Rollback transaction
TransactionDefinition
Definition information object of transaction
String getName();//Get transaction object name int getIsolationLevel();//Get transaction isolation level int getPropagationBehavior();//Get transaction propagation behavior int getTimeout();//Get transaction timeout boolean isReadOnly();//Gets whether the transaction is read-only
- Read write transactions: add, delete, and modify
- Read only transactions: queries
Isolation level of transaction
Propagation behavior of transactions
- REQUIRED: if there is no transaction at present, create a new transaction. If there is already a transaction, join it. General selection (default)
- SUPPORTS: SUPPORTS the current transaction. If there is no transaction, it will be executed in a non transactional manner (no transaction)
- MANDATORY: use the current transaction. If there is no transaction, an exception will be thrown
- REQUERS_NEW: create a new transaction. If it is currently in a transaction, suspend the current transaction.
- NOT_SUPPORTED: perform the operation in a non transactional manner. If there is a current transaction, suspend the current transaction
- NEVER: run in non transaction mode. If there is a transaction, throw an exception
- NESTED: if a transaction currently exists, it is executed within a NESTED transaction. If there is no current transaction, perform an operation similar to REQUIRED.
Timeout
The default value is - 1 and there is no timeout limit. If yes, set it in seconds.
Is it a read-only transaction
It is recommended that the query be set to read-only.
TransactionStatus
Describes the state information of fifteen objects at a certain point in time
void flush();//Refresh transaction boolean hasSavepoint();//Gets whether a storage point exists boolean isCompleted();//Gets whether the transaction is complete boolean isNewTransaction();//Gets whether the transaction is a new transaction boolean isRollbackOnly();//Gets whether the transaction is rolled back void setRollbackOnly();//Set transaction rollback
Declarative transaction control based on XML
Environment construction
Set packaging method and package Guide
First set the packaging method as jar package and import jar package:
- spring-jdbc
- spring-tx
- spring-context
- aspectjweaver
- Database driven
Create Spring configuration files and import constraints (you need to import the namespaces of tx and aop here)
<?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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> </beans>
Prepare database and entity classes
create table account( id int primary key auto_increment, name varchar(40), money float )character set utf8 collate utf8_general_ci;
public class Account implements Serializable { private Integer id; private String name; private Float money; @Override public String toString() { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + 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 Float getMoney() { return money; } public void setMoney(Float money) { this.money = money; } }
Prepare Dao layer interface and implementation class
Interface:
public interface IAccountDao { Account findAccountById(Integer id); Account findAccountByName(String name); void updateAccount(Account account); }
Implementation class:
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao { public Account findAccountById(Integer id) { List<Account> list = getJdbcTemplate().query("select * from account where id = ?", new BeanPropertyRowMapper<Account>(Account.class), id); return list.isEmpty()?null:list.get(0); } public Account findAccountByName(String name) { List<Account> list = getJdbcTemplate().query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class), name); if (list.isEmpty()){ return null; } if (list.size()>1){ throw new RuntimeException("The result is not unique"); } return list.get(0); } public void updateAccount(Account account) { getJdbcTemplate().update("update account set money = ? where id = ?", account.getMoney(), account.getId()); } }
Prepare business layer interfaces and implementation classes
Interface:
public interface IAccountService { Account findAccountById(Integer id); void transfer(String sourceName, String targetName, Float money); }
Implementation class:
public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao; public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } public void transfer(String sourceName, String targetName, Float money) { Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targetName); source.setMoney(source.getMoney()-money); target.setMoney(target.getMoney()+money); accountDao.updateAccount(source); accountDao.updateAccount(target); } }
Configure the data source, business layer and persistence layer in the configuration file
<!--Configure data source--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"></property> <property name="username" value="root"></property> <property name="password" value="password"></property> </bean> <!--to configure Dao--> <bean id="accountDao" class="com.dao.Impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!--to configure service--> <bean id="accountService" class="com.service.Impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean>
XML based configuration steps
Configure transaction manager
<!--Configure transaction manager--> <bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
Configure notification and properties of transactions
<!--Configure notifications for transactions--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--Configure the properties of the transaction name Is the name of the core method of the business. Wildcards can be used*replace find*That is to say find The method at the beginning has higher priority than using wildcards alone --> <tx:attributes> <tx:method name="*" read-only="false" propagation="REQUIRED"/> <tx:method name="find*" read-only="true" propagation="SUPPORTS"></tx:method> </tx:attributes> </tx:advice>
- Read only: whether it is a read-only transaction. The default is false and not read-only.
- Isolation: Specifies the isolation level of the transaction. The default value is to use the default isolation level of the database.
- Propagation: Specifies the propagation behavior of the transaction.
- Timeout: Specifies the timeout. The default value is: - 1. Never time out.
- Rollback for: used to specify an exception. When the exception is generated during execution, the transaction is rolled back. If other exceptions are generated, the transaction will not be rolled back. There is no default value and any exceptions are rolled back.
- No rollback for: used to specify an exception. When this exception occurs, the transaction will not be rolled back. When other exceptions occur, the transaction will be rolled back. There is no default value and any exceptions are rolled back.
Configure AOP pointcut expressions and the corresponding relationship between pointcut expressions and transaction notifications
<aop:config> <aop:pointcut id="pt1" expression="execution(* com.service.Impl.*.*(..))"/> <!--Establish the corresponding relationship between pointcut expression and transaction notification--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor> </aop:config>
Test code
public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); IAccountService accountService = (IAccountService) ac.getBean("accountService"); accountService.transfer("a", "b", 100f); }
Annotation based declarative transaction control
Environment construction
Set packaging method and package Guide
Same as XML based configuration
Create spring's configuration file, import constraints, and configure scanned packages
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--Configure packages to scan--> <context:component-scan base-package="com"></context:component-scan>
Create database tables and entity classes
Same as XML based configuration
Create Dao interfaces and implementation classes and use annotations to let spring manage
Note: when using annotations, you can no longer inherit JdbcDaoSupport. You must manually create the JdbcTemplate class and inject it with @ autowritten, and also in the bean Configure the JdbcTemplate in the XML file.
@Repository("accountDao") public class AccountDaoImpl implements IAccountDao { @Autowired private JdbcTemplate jdbcTemplate; public Account findAccountById(Integer id) { List<Account> list = jdbcTemplate.query("select * from account where id = ?", new BeanPropertyRowMapper<Account>(Account.class), id); return list.isEmpty()?null:list.get(0); } public Account findAccountByName(String name) { List<Account> list = jdbcTemplate.query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class), name); if (list.isEmpty()){ return null; } if (list.size()>1){ throw new RuntimeException("The result is not unique"); } return list.get(0); } public void updateAccount(Account account) { jdbcTemplate.update("update account set money = ? where id = ?", account.getMoney(), account.getId()); } }
Create business layer interfaces and implementation classes and use annotations to let spring manage
Remove the set method and use autowritten automatic injection
@Service("accountService") public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao; public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } public void transfer(String sourceName, String targetName, Float money) { Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targetName); source.setMoney(source.getMoney()-money); target.setMoney(target.getMoney()+money); accountDao.updateAccount(source); accountDao.updateAccount(target); } }
Other xml configurations
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!--Configure data source--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"></property> <property name="username" value="root"></property> <property name="password" value="password"></property> </bean>
Annotation based configuration steps
Configure transaction manager
<!--Configure transaction manager--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
Use @ Transactional annotation in the business layer
@Service("accountService") @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao; public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void transfer(String sourceName, String targetName, Float money) { Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targetName); source.setMoney(source.getMoney()-money); target.setMoney(target.getMoney()+money); accountDao.updateAccount(source); accountDao.updateAccount(target); } }
The attribute of this annotation has the same meaning as the attribute in xml. This annotation can appear on interfaces, classes and methods.
- It appears on the interface, indicating that all implementation classes of the interface have transaction support.
- Appears on the class, indicating that all methods in the class have transaction support
- Appears on the method, indicating that the method has transaction support.
Priority of the above three positions: method > class > interface
Enable spring's support for annotation transactions in the configuration file
<!--open spring Support for annotation transactions--> <tx:annotation-driven transaction-manager="transactionManager"/>
Test code
ditto
Some new features of spring 5
JDK upgrade
spring5.0 released its GA (Universal) version in September 2017. This version is written based on jdk8, so the following versions of jdk8 cannot be used. At the same time, it can be compatible with jdk9 version. tomcat version requires 8.5 and above.
jdk1. The running time of version 8 (JDK8) is as follows:
When switching to jdk1 After version 7, the running time is as follows:
Objects created in a frame are usually created using reflection.
Update of core container
Spring Framework 5.0 now supports candidate component indexes as an alternative to classpath scanning. This feature has been added to the classpath scanner to simplify the step of adding candidate component identification.
The application build task can define its own meta-inf / Spring. For the current project Components file. At compile time, the source model is self-contained, and JPA entities and Spring components are marked.
Reading entities from the index instead of scanning the classpath is no significant difference for small projects with less than 200 classes. However, it has a great impact on large projects. The cost of loading component indexes is lower. Therefore, as the number of classes increases, the start time of index reading will remain unchanged.
The cost of loading component indexes is cheap. Therefore, when the number of classes continues to grow and the start-up time of index building can still maintain a constant, but for component scanning, the start-up time will increase significantly.
What this means for our developers in large Spring projects is that the startup time of the application will be greatly reduced. Although 20 or 30 seconds may seem like nothing, if you have to board hundreds of times a day, it's enough for you. Using component index can help you live more efficiently every day.
You can do it in Spring Jira Learn more about component indexes on.
end
There's probably only so much about the Spring framework...