第一章:Spring对JDBC的封装

1.1 JdbcTemplate

1.1.1 概述

1.1.1.1 基本介绍

  • Spring对数据库的操作在JDBC上面做了基本的封装,让开发者在操作数据库的时候只需要关注SQL语句和查询结果处理器,即可完成功能。
  • 在配合Spring的IOC功能,可以把DataSource注册到JdbcTemplate中,同时利用Spring基于AOP的事务即可完成简单的数据库的CRUD操作。

1.1.1.2 源码

  • JdbcTemplate:
  1. public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
  2. private static final String RETURN_RESULT_SET_PREFIX = "#result-set-";
  3. private static final String RETURN_UPDATE_COUNT_PREFIX = "#update-count-";
  4. /** If this variable is false, we will throw exceptions on SQL warnings. */
  5. private boolean ignoreWarnings = true;
  6. /**
  7. * If this variable is set to a non-negative value, it will be used for setting the
  8. * fetchSize property on statements used for query processing.
  9. */
  10. private int fetchSize = -1;
  11. /**
  12. * If this variable is set to a non-negative value, it will be used for setting the
  13. * maxRows property on statements used for query processing.
  14. */
  15. private int maxRows = -1;
  16. /**
  17. * If this variable is set to a non-negative value, it will be used for setting the
  18. * queryTimeout property on statements used for query processing.
  19. */
  20. private int queryTimeout = -1;
  21. /**
  22. * If this variable is set to true, then all results checking will be bypassed for any
  23. * callable statement processing. This can be used to avoid a bug in some older Oracle
  24. * JDBC drivers like 10.1.0.2.
  25. */
  26. private boolean skipResultsProcessing = false;
  27. /**
  28. * If this variable is set to true then all results from a stored procedure call
  29. * that don't have a corresponding SqlOutParameter declaration will be bypassed.
  30. * All other results processing will be take place unless the variable
  31. * {@code skipResultsProcessing} is set to {@code true}.
  32. */
  33. private boolean skipUndeclaredResults = false;
  34. /**
  35. * If this variable is set to true then execution of a CallableStatement will return
  36. * the results in a Map that uses case insensitive names for the parameters.
  37. */
  38. private boolean resultsMapCaseInsensitive = false;
  39. /**
  40. * Construct a new JdbcTemplate for bean usage.
  41. * <p>Note: The DataSource has to be set before using the instance.
  42. * @see #setDataSource
  43. */
  44. public JdbcTemplate() {
  45. }
  46. /**
  47. * Construct a new JdbcTemplate, given a DataSource to obtain connections from.
  48. * <p>Note: This will not trigger initialization of the exception translator.
  49. * @param dataSource the JDBC DataSource to obtain connections from
  50. */
  51. public JdbcTemplate(DataSource dataSource) {
  52. setDataSource(dataSource);
  53. afterPropertiesSet();
  54. }
  55. /**
  56. * Construct a new JdbcTemplate, given a DataSource to obtain connections from.
  57. * <p>Note: Depending on the "lazyInit" flag, initialization of the exception translator
  58. * will be triggered.
  59. * @param dataSource the JDBC DataSource to obtain connections from
  60. * @param lazyInit whether to lazily initialize the SQLExceptionTranslator
  61. */
  62. public JdbcTemplate(DataSource dataSource, boolean lazyInit) {
  63. setDataSource(dataSource);
  64. setLazyInit(lazyInit);
  65. afterPropertiesSet();
  66. }
  67. //略
  68. }

1.1.1.3 方法说明

  • JdbcTemplate主要提供以下的方法:
    • execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句。
    • update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句。
    • query方法及queryForXxx方法:用于执行查询相关语句。
    • call方法:用于执行存储过程、函数相关语句。

1.1.2 入门案例

  • 导入相关jar包的Maven坐标:
  1. <properties>
  2. <!-- 5.2.7.RELEASE -->
  3. <spring.version>5.2.7.RELEASE</spring.version>
  4. </properties>
  5. <dependencies>
  6. <dependency>
  7. <groupId>org.springframework</groupId>
  8. <artifactId>spring-context</artifactId>
  9. <version>${spring.version}</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.springframework</groupId>
  13. <artifactId>spring-web</artifactId>
  14. <version>${spring.version}</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.springframework</groupId>
  18. <artifactId>spring-webmvc</artifactId>
  19. <version>${spring.version}</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework</groupId>
  23. <artifactId>spring-aspects</artifactId>
  24. <version>${spring.version}</version>
  25. </dependency>
  26. <dependency>
  27. <groupId>org.springframework</groupId>
  28. <artifactId>spring-aop</artifactId>
  29. <version>${spring.version}</version>
  30. </dependency>
  31. <dependency>
  32. <groupId>com.alibaba</groupId>
  33. <artifactId>druid</artifactId>
  34. <version>1.1.23</version>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.springframework</groupId>
  38. <artifactId>spring-jdbc</artifactId>
  39. <version>${spring.version}</version>
  40. </dependency>
  41. <dependency>
  42. <groupId>org.springframework</groupId>
  43. <artifactId>spring-test</artifactId>
  44. <version>${spring.version}</version>
  45. </dependency>
  46. <dependency>
  47. <groupId>mysql</groupId>
  48. <artifactId>mysql-connector-java</artifactId>
  49. <version>8.0.19</version>
  50. </dependency>
  51. <!-- 导入YAML解析工厂坐标 -->
  52. <dependency>
  53. <groupId>org.yaml</groupId>
  54. <artifactId>snakeyaml</artifactId>
  55. <version>1.26</version>
  56. </dependency>
  57. <dependency>
  58. <groupId>junit</groupId>
  59. <artifactId>junit</artifactId>
  60. <version>4.13</version>
  61. <scope>test</scope>
  62. </dependency>
  63. <dependency>
  64. <groupId>javax.inject</groupId>
  65. <artifactId>javax.inject</artifactId>
  66. <version>1</version>
  67. </dependency>
  68. <dependency>
  69. <groupId>javax.annotation</groupId>
  70. <artifactId>javax.annotation-api</artifactId>
  71. <version>1.3.2</version>
  72. </dependency>
  73. <dependency>
  74. <groupId>javax.servlet</groupId>
  75. <artifactId>javax.servlet-api</artifactId>
  76. <version>4.0.1</version>
  77. </dependency>
  78. </dependencies>
  • sql脚本:
  1. DROP TABLE IF EXISTS `account`;
  2. CREATE TABLE `account` (
  3. `id` int(11) NOT NULL AUTO_INCREMENT,
  4. `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  5. `money` double NULL DEFAULT NULL,
  6. PRIMARY KEY (`id`) USING BTREE
  7. ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
  • 日志文件log4j2.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
  3. <!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
  4. <configuration status="INFO">
  5. <!--先定义所有的appender-->
  6. <appenders>
  7. <!--输出日志信息到控制台-->
  8. <console name="Console" target="SYSTEM_OUT">
  9. <!--控制日志输出的格式-->
  10. <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
  11. </console>
  12. </appenders>
  13. <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
  14. <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
  15. <loggers>
  16. <root level="info">
  17. <appender-ref ref="Console"/>
  18. </root>
  19. </loggers>
  20. </configuration>
  • jdbc.yml
  1. jdbc:
  2. driver: com.mysql.cj.jdbc.Driver
  3. password: 123456
  4. url: jdbc:mysql://192.168.2.112:3306/spring5?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
  5. user: root
  • 自定义YAMLPropertySourceFactory:
  1. package com.sunxiaping.spring5.factory;
  2. import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
  3. import org.springframework.core.env.PropertiesPropertySource;
  4. import org.springframework.core.env.PropertySource;
  5. import org.springframework.core.io.support.EncodedResource;
  6. import org.springframework.core.io.support.PropertySourceFactory;
  7. import java.io.IOException;
  8. import java.util.Properties;
  9. /**
  10. * 自定义YAMLPropertySourceFactory
  11. */
  12. public class YAMLPropertySourceFactory implements PropertySourceFactory {
  13. @Override
  14. public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
  15. YamlPropertiesFactoryBean bean = new YamlPropertiesFactoryBean();
  16. bean.setResources(encodedResource.getResource());
  17. Properties properties = bean.getObject();
  18. return (name != null ? new PropertiesPropertySource(name, properties) : new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties));
  19. }
  20. }
  • 编写实体类:
  1. package com.sunxiaping.spring5.domain;
  2. import java.io.Serializable;
  3. public class Account implements Serializable {
  4. private Integer id;
  5. private String name;
  6. private Double money;
  7. public Integer getId() {
  8. return id;
  9. }
  10. public void setId(Integer id) {
  11. this.id = id;
  12. }
  13. public String getName() {
  14. return name;
  15. }
  16. public void setName(String name) {
  17. this.name = name;
  18. }
  19. public Double getMoney() {
  20. return money;
  21. }
  22. public void setMoney(Double money) {
  23. this.money = money;
  24. }
  25. @Override
  26. public String toString() {
  27. return "Account{" +
  28. "id=" + id +
  29. ", name='" + name + '\'' +
  30. ", money=" + money +
  31. '}';
  32. }
  33. }
  • JdbcConfig.java
  1. package com.sunxiaping.spring5.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import com.sunxiaping.spring5.factory.YAMLPropertySourceFactory;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.beans.factory.annotation.Value;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.context.annotation.PropertySource;
  9. import org.springframework.jdbc.core.JdbcTemplate;
  10. import javax.sql.DataSource;
  11. @Configuration
  12. @PropertySource(value = "classpath:jdbc.yml", factory = YAMLPropertySourceFactory.class)
  13. public class JdbcConfig {
  14. @Value("${jdbc.driver}")
  15. private String driver;
  16. @Value("${jdbc.url}")
  17. private String url;
  18. @Value("${jdbc.user}")
  19. private String user;
  20. @Value("${jdbc.password}")
  21. private String password;
  22. /**
  23. * 配置数据源
  24. *
  25. * @return 数据源
  26. */
  27. @Bean
  28. public DataSource dataSource() {
  29. DruidDataSource dataSource = new DruidDataSource();
  30. dataSource.setDriverClassName(driver);
  31. dataSource.setUrl(url);
  32. dataSource.setUsername(user);
  33. dataSource.setPassword(password);
  34. return dataSource;
  35. }
  36. /**
  37. * 配置JdbcTemplate
  38. *
  39. * @param dataSource 数据源
  40. * @return JdbcTemplate
  41. */
  42. @Bean
  43. public JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource) {
  44. return new JdbcTemplate(dataSource);
  45. }
  46. }
  • SpringConfig.java
  1. package com.sunxiaping.spring5.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.Import;
  5. /**
  6. * Spring的配置类
  7. */
  8. @Configuration
  9. @ComponentScan(value = "com.sunxiaping.spring5")
  10. @Import(value = JdbcConfig.class)
  11. public class SpringConfig {
  12. }
  • AccountDao.java
  1. package com.sunxiaping.spring5.dao;
  2. import com.sunxiaping.spring5.domain.Account;
  3. import java.util.List;
  4. public interface AccountDao {
  5. /**
  6. * 保存账户信息
  7. *
  8. * @param account
  9. */
  10. void saveAccount(Account account);
  11. /**
  12. * 更新账户信息
  13. *
  14. * @param account
  15. */
  16. void updateAccount(Account account);
  17. /**
  18. * 删除账户信息
  19. *
  20. * @param id
  21. */
  22. void deleteAccount(Integer id);
  23. /**
  24. * 根据id查询账户信息
  25. *
  26. * @param id
  27. * @return
  28. */
  29. Account findById(Integer id);
  30. /**
  31. * 查询全部账户信息
  32. *
  33. * @return
  34. */
  35. List<Account> findAll();
  36. /**
  37. * 查询数量
  38. *
  39. * @return
  40. */
  41. Long findCount();
  42. }
  • AccountDaoImpl.java
  1. package com.sunxiaping.spring5.dao.impl;
  2. import com.sunxiaping.spring5.dao.AccountDao;
  3. import com.sunxiaping.spring5.domain.Account;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.jdbc.core.BeanPropertyRowMapper;
  6. import org.springframework.jdbc.core.JdbcTemplate;
  7. import org.springframework.jdbc.core.SingleColumnRowMapper;
  8. import org.springframework.stereotype.Repository;
  9. import java.util.List;
  10. @Repository
  11. public class AccountDaoImpl implements AccountDao {
  12. @Autowired
  13. private JdbcTemplate jdbcTemplate;
  14. @Override
  15. public void saveAccount(Account account) {
  16. jdbcTemplate.update(" INSERT INTO `account` (`name`,`money`) VALUES (?,?) ", account.getName(), account.getMoney());
  17. }
  18. @Override
  19. public void updateAccount(Account account) {
  20. jdbcTemplate.update(" UPDATE `account` SET `name` =?,`money` =? WHERE id = ? ", account.getName(), account.getMoney(), account.getId());
  21. }
  22. @Override
  23. public void deleteAccount(Integer id) {
  24. jdbcTemplate.update(" DELETE FROM `account` WHERE id = ? ", id);
  25. }
  26. @Override
  27. public Account findById(Integer id) {
  28. return jdbcTemplate.queryForObject("SELECT * FROM `account` WHERE id= ?", new BeanPropertyRowMapper<>(Account.class), id);
  29. }
  30. @Override
  31. public List<Account> findAll() {
  32. return jdbcTemplate.query("SELECT * FROM `account`",new BeanPropertyRowMapper<>(Account.class));
  33. }
  34. @Override
  35. public Long findCount() {
  36. return jdbcTemplate.queryForObject(" SELECT count(*) FROM `account` ",new SingleColumnRowMapper<>());
  37. }
  38. }
  • 测试:
  1. package com.sunxiaping.spring5;
  2. import com.sunxiaping.spring5.config.SpringConfig;
  3. import com.sunxiaping.spring5.dao.AccountDao;
  4. import com.sunxiaping.spring5.domain.Account;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.test.context.ContextConfiguration;
  9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  10. import java.util.List;
  11. @RunWith(SpringJUnit4ClassRunner.class)
  12. @ContextConfiguration(classes = SpringConfig.class)
  13. public class Spring5Test {
  14. @Autowired
  15. private AccountDao accountDao;
  16. @Test
  17. public void testSaveAccount(){
  18. Account account = new Account();
  19. account.setName("张三");
  20. account.setMoney(1.00);
  21. accountDao.saveAccount(account);
  22. }
  23. @Test
  24. public void testUpdateAccount(){
  25. Account account = accountDao.findById(1);
  26. account.setMoney(2.00);
  27. accountDao.updateAccount(account);
  28. }
  29. @Test
  30. public void testDeleteAccount(){
  31. Account account = accountDao.findById(1);
  32. if(null != account){
  33. accountDao.deleteAccount(1);
  34. }
  35. }
  36. @Test
  37. public void testFindById(){
  38. Account account = accountDao.findById(1);
  39. System.out.println("account = " + account);
  40. }
  41. @Test
  42. public void testFindAll(){
  43. List<Account> accountList = accountDao.findAll();
  44. System.out.println("accountList = " + accountList);
  45. }
  46. @Test
  47. public void testFindCount(){
  48. Long count = accountDao.findCount();
  49. System.out.println("count = " + count);
  50. }
  51. }

1.2 NamedParameterJdbcTemplate

1.2.1 概述

1.2.1.1 基本介绍

  • 在经典的JDBC用法中,SQL的参数是用占位符?表示的,并且受到位置的限制。定位参数的问题在于,一旦参数的位置发生改变,就必须改变参数绑定。在Spring JDBC框架中,绑定SQL参数的另一种选择是使用具名参数。
  • 具名参数:SQL按名称而不是按照位置进行指定。具名参数更易于维护,也提升了可读性,具名参数由框架类在运行时用占位符取代。

1.2.1.2 源码

  • NamedParameterJdbcTemplate:
  1. public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations {
  2. /** Default maximum number of entries for this template's SQL cache: 256. */
  3. public static final int DEFAULT_CACHE_LIMIT = 256;
  4. /** The JdbcTemplate we are wrapping. */
  5. private final JdbcOperations classicJdbcTemplate;
  6. private volatile int cacheLimit = DEFAULT_CACHE_LIMIT;
  7. /** Cache of original SQL String to ParsedSql representation. */
  8. @SuppressWarnings("serial")
  9. private final Map<String, ParsedSql> parsedSqlCache =
  10. new LinkedHashMap<String, ParsedSql>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
  11. @Override
  12. protected boolean removeEldestEntry(Map.Entry<String, ParsedSql> eldest) {
  13. return size() > getCacheLimit();
  14. }
  15. };
  16. /**
  17. * Create a new NamedParameterJdbcTemplate for the given {@link DataSource}.
  18. * <p>Creates a classic Spring {@link org.springframework.jdbc.core.JdbcTemplate} and wraps it.
  19. * @param dataSource the JDBC DataSource to access
  20. */
  21. public NamedParameterJdbcTemplate(DataSource dataSource) {
  22. Assert.notNull(dataSource, "DataSource must not be null");
  23. this.classicJdbcTemplate = new JdbcTemplate(dataSource);
  24. }
  25. //略
  26. }

1.2.2 入门案例

  • JdbcConfig.java
  1. package com.sunxiaping.spring5.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import com.sunxiaping.spring5.factory.YAMLPropertySourceFactory;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.beans.factory.annotation.Value;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.context.annotation.PropertySource;
  9. import org.springframework.jdbc.core.JdbcTemplate;
  10. import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
  11. import javax.sql.DataSource;
  12. @Configuration
  13. @PropertySource(value = "classpath:jdbc.yml", factory = YAMLPropertySourceFactory.class)
  14. public class JdbcConfig {
  15. @Value("${jdbc.driver}")
  16. private String driver;
  17. @Value("${jdbc.url}")
  18. private String url;
  19. @Value("${jdbc.user}")
  20. private String user;
  21. @Value("${jdbc.password}")
  22. private String password;
  23. /**
  24. * 配置数据源
  25. *
  26. * @return 数据源
  27. */
  28. @Bean
  29. public DataSource dataSource() {
  30. DruidDataSource dataSource = new DruidDataSource();
  31. dataSource.setDriverClassName(driver);
  32. dataSource.setUrl(url);
  33. dataSource.setUsername(user);
  34. dataSource.setPassword(password);
  35. return dataSource;
  36. }
  37. /**
  38. * 配置JdbcTemplate
  39. *
  40. * @param dataSource 数据源
  41. * @return JdbcTemplate
  42. */
  43. @Bean
  44. public JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource) {
  45. return new JdbcTemplate(dataSource);
  46. }
  47. /**
  48. * 配置NamedParameterJdbcTemplate
  49. *
  50. * @param jdbcTemplate
  51. * @return
  52. */
  53. @Bean
  54. public NamedParameterJdbcTemplate namedParameterJdbcTemplate(JdbcTemplate jdbcTemplate) {
  55. return new NamedParameterJdbcTemplate(jdbcTemplate);
  56. }
  57. }
  • SpringConfig.java
  1. package com.sunxiaping.spring5.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.Import;
  5. /**
  6. * Spring的配置类
  7. */
  8. @Configuration
  9. @ComponentScan(value = "com.sunxiaping.spring5")
  10. @Import(value = JdbcConfig.class)
  11. public class SpringConfig {
  12. }
  • 测试:
  1. package com.sunxiaping.spring5;
  2. import com.sunxiaping.spring5.config.SpringConfig;
  3. import com.sunxiaping.spring5.domain.Account;
  4. import org.junit.Test;
  5. import org.junit.runner.RunWith;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.cglib.beans.BeanMap;
  8. import org.springframework.jdbc.core.BeanPropertyRowMapper;
  9. import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
  10. import org.springframework.test.context.ContextConfiguration;
  11. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  12. import java.util.HashMap;
  13. import java.util.Map;
  14. @RunWith(SpringJUnit4ClassRunner.class)
  15. @ContextConfiguration(classes = SpringConfig.class)
  16. public class Spring5Test {
  17. @Autowired
  18. private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
  19. @Test
  20. public void testSaveAccount() {
  21. Account account = new Account();
  22. account.setName("李四");
  23. account.setMoney(1d);
  24. BeanMap beanMap = BeanMap.create(account);
  25. namedParameterJdbcTemplate.update(" INSERT INTO `account` (`name`,`money`) VALUES (:name,:money) ",beanMap);
  26. }
  27. @Test
  28. public void testFindById(){
  29. Map<String,Object> map = new HashMap<>();
  30. map.put("id",1);
  31. Account account = namedParameterJdbcTemplate.queryForObject(" SELECT * FROM `account` WHERE id = :id ", map, new BeanPropertyRowMapper<>(Account.class));
  32. System.out.println("account = " + account);
  33. }
  34. }

第二章:Spring中的事务

2.1 API介绍

2.1.1 PlatformTransactionManager和其实现类

  • 作用:此接口是Spring的事务管理器的核心接口。Spring本身并不支持事务实现,只是负责提供标准,应用底层支持什么样的事务,需要提供具体实现类。在Spring框架中,也为我们内置了一些具体策略,例如:DatasourceTransactionManager、HibernateTransactionManager、jpaTransactionManager等。

  • 类图:

PlatformTransactionManager和其实现类.png

  • PlatformTransactionManager:
  1. public interface PlatformTransactionManager extends TransactionManager {
  2. /**
  3. * 获取事务状态信息
  4. */
  5. TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
  6. throws TransactionException;
  7. /**
  8. * 提交事务
  9. */
  10. void commit(TransactionStatus status) throws TransactionException;
  11. /**
  12. * 回滚事务
  13. */
  14. void rollback(TransactionStatus status) throws TransactionException;
  15. }

2.1.2 TransactionDefinition

  • 作用:此接口是Spring中事务可控属性的顶层接口,里面定义了事务的一些属性以及获取属性的方法。例如:事务的传播行为、事务的隔离级别、事务的只读、事务的超时等待。通常情况下,我们在开发洪都可以配置这些属性,以求达到最佳效果。

  • 类图:

TransactionDefinition.png

  • TransactionDefinition:
  1. public interface TransactionDefinition {
  2. /**
  3. * REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
  4. */
  5. int PROPAGATION_REQUIRED = 0;
  6. /**
  7. * SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
  8. */
  9. int PROPAGATION_SUPPORTS = 1;
  10. /**
  11. * MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
  12. */
  13. int PROPAGATION_MANDATORY = 2;
  14. /**
  15. * REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
  16. */
  17. int PROPAGATION_REQUIRES_NEW = 3;
  18. /**
  19. * NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
  20. */
  21. int PROPAGATION_NOT_SUPPORTED = 4;
  22. /**
  23. * NEVER:以非事务方式运行,如果当前存在事务,抛出异常
  24. */
  25. int PROPAGATION_NEVER = 5;
  26. /**
  27. * NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。
  28. */
  29. int PROPAGATION_NESTED = 6;
  30. /**
  31. * 事务的隔离级别默认值,当取值-1时,会采用下面的4个值其中一个。
  32. * 事务的隔离级别默认值,当取值-1时,会采用下面的4个值其中一个。
  33. */
  34. int ISOLATION_DEFAULT = -1;
  35. /**
  36. * 事务隔离级别为:读未提交
  37. * 事务隔离级别为:读未提交
  38. */
  39. int ISOLATION_READ_UNCOMMITTED = 1;
  40. /**
  41. * 事务隔离级别为:读已提交
  42. * 可以防止脏读的发生,但是无法防住不可重复读和幻读的发生
  43. */
  44. int ISOLATION_READ_COMMITTED = 2;
  45. /**
  46. * 事务隔离级别为:可重复读
  47. * 可以防止脏读和不可重复读的发生,但是无法防住幻读的发生
  48. */
  49. int ISOLATION_REPEATABLE_READ = 4;
  50. /**
  51. * 事务隔离级别为:串行化
  52. * 此时所有错误情况均可防住,但是由于事务变成了独占模式(排他模式),因此效率最低
  53. */
  54. int ISOLATION_SERIALIZABLE = 8;
  55. /**
  56. * 超时限制。默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
  57. */
  58. int TIMEOUT_DEFAULT = -1;
  59. /**
  60. * 获取事务传播行为
  61. */
  62. default int getPropagationBehavior() {
  63. return PROPAGATION_REQUIRED;
  64. }
  65. /**
  66. * 获取事务隔离级别
  67. */
  68. default int getIsolationLevel() {
  69. return ISOLATION_DEFAULT;
  70. }
  71. /**
  72. * 获取事务超时时间
  73. */
  74. default int getTimeout() {
  75. return TIMEOUT_DEFAULT;
  76. }
  77. /**
  78. * 获取事务是否只读
  79. */
  80. default boolean isReadOnly() {
  81. return false;
  82. }
  83. /**
  84. * 获取事务名称
  85. */
  86. @Nullable
  87. default String getName() {
  88. return null;
  89. }
  90. // Static builder methods
  91. /**
  92. * Return an unmodifiable {@code TransactionDefinition} with defaults.
  93. * <p>For customization purposes, use the modifiable
  94. * {@link org.springframework.transaction.support.DefaultTransactionDefinition}
  95. * instead.
  96. * @since 5.2
  97. */
  98. static TransactionDefinition withDefaults() {
  99. return StaticTransactionDefinition.INSTANCE;
  100. }
  101. }

2.1.3 TransactionStatus

  • 作用:此接口是事务运行状态表示的顶层接口,里面定义着获取事务运行状态的一些方法。

  • 类图:

TransactionStatus.png

  • TransactionStatus:
  1. public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
  2. /**
  3. * 是否包含存储点
  4. */
  5. boolean hasSavepoint();
  6. /**
  7. * 刷新事务
  8. */
  9. @Override
  10. void flush();
  11. }

2.2 入门案例

  • JdbcConfig.java
  1. package com.sunxiaping.spring5.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import com.sunxiaping.spring5.factory.YAMLPropertySourceFactory;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.beans.factory.annotation.Value;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.context.annotation.PropertySource;
  9. import org.springframework.jdbc.core.JdbcTemplate;
  10. import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
  11. import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  12. import org.springframework.transaction.TransactionManager;
  13. import javax.sql.DataSource;
  14. @Configuration
  15. @PropertySource(value = "classpath:jdbc.yml", factory = YAMLPropertySourceFactory.class)
  16. public class JdbcConfig {
  17. @Value("${jdbc.driver}")
  18. private String driver;
  19. @Value("${jdbc.url}")
  20. private String url;
  21. @Value("${jdbc.user}")
  22. private String user;
  23. @Value("${jdbc.password}")
  24. private String password;
  25. /**
  26. * 配置数据源
  27. *
  28. * @return 数据源
  29. */
  30. @Bean
  31. public DataSource dataSource() {
  32. DruidDataSource dataSource = new DruidDataSource();
  33. dataSource.setDriverClassName(driver);
  34. dataSource.setUrl(url);
  35. dataSource.setUsername(user);
  36. dataSource.setPassword(password);
  37. return dataSource;
  38. }
  39. /**
  40. * 配置JdbcTemplate
  41. *
  42. * @param dataSource 数据源
  43. * @return JdbcTemplate
  44. */
  45. @Bean
  46. public JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource) {
  47. return new JdbcTemplate(dataSource);
  48. }
  49. /**
  50. * 配置NamedParameterJdbcTemplate
  51. *
  52. * @param jdbcTemplate
  53. * @return
  54. */
  55. @Bean
  56. public NamedParameterJdbcTemplate namedParameterJdbcTemplate(JdbcTemplate jdbcTemplate) {
  57. return new NamedParameterJdbcTemplate(jdbcTemplate);
  58. }
  59. /**
  60. * 配置事务管理器
  61. *
  62. * @param dataSource
  63. * @return
  64. */
  65. @Bean
  66. public TransactionManager transactionManager(@Autowired DataSource dataSource) {
  67. DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
  68. dataSourceTransactionManager.setDataSource(dataSource);
  69. return dataSourceTransactionManager;
  70. }
  71. }
  • SpringConfig.java
  1. package com.sunxiaping.spring5.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.Import;
  5. import org.springframework.transaction.annotation.EnableTransactionManagement;
  6. /**
  7. * Spring的配置类
  8. */
  9. @Configuration
  10. @ComponentScan(value = "com.sunxiaping.spring5")
  11. @Import(value = JdbcConfig.class)
  12. @EnableTransactionManagement
  13. public class SpringConfig {
  14. }
  • AccountDao.java
  1. package com.sunxiaping.spring5.dao;
  2. import com.sunxiaping.spring5.domain.Account;
  3. import java.util.List;
  4. public interface AccountDao {
  5. /**
  6. * 保存账户信息
  7. *
  8. * @param account
  9. */
  10. void saveAccount(Account account);
  11. /**
  12. * 更新账户信息
  13. *
  14. * @param account
  15. */
  16. void updateAccount(Account account);
  17. /**
  18. * 删除账户信息
  19. *
  20. * @param id
  21. */
  22. void deleteAccount(Integer id);
  23. /**
  24. * 根据id查询账户信息
  25. *
  26. * @param id
  27. * @return
  28. */
  29. Account findById(Integer id);
  30. /**
  31. * 查询全部账户信息
  32. *
  33. * @return
  34. */
  35. List<Account> findAll();
  36. /**
  37. * 查询数量
  38. *
  39. * @return
  40. */
  41. Long findCount();
  42. /**
  43. * 根据名称查询账户信息
  44. *
  45. * @param name
  46. * @return
  47. */
  48. Account findByName(String name);
  49. }
  • AccountDaoImpl.java
  1. package com.sunxiaping.spring5.dao.impl;
  2. import com.sunxiaping.spring5.dao.AccountDao;
  3. import com.sunxiaping.spring5.domain.Account;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.jdbc.core.BeanPropertyRowMapper;
  6. import org.springframework.jdbc.core.JdbcTemplate;
  7. import org.springframework.jdbc.core.SingleColumnRowMapper;
  8. import org.springframework.stereotype.Repository;
  9. import org.springframework.util.CollectionUtils;
  10. import org.springframework.util.StringUtils;
  11. import java.util.List;
  12. @Repository
  13. public class AccountDaoImpl implements AccountDao {
  14. @Autowired
  15. private JdbcTemplate jdbcTemplate;
  16. @Override
  17. public void saveAccount(Account account) {
  18. jdbcTemplate.update(" INSERT INTO `account` (`name`,`money`) VALUES (?,?) ", account.getName(), account.getMoney());
  19. }
  20. @Override
  21. public void updateAccount(Account account) {
  22. jdbcTemplate.update(" UPDATE `account` SET `name` =?,`money` =? WHERE id = ? ", account.getName(), account.getMoney(), account.getId());
  23. }
  24. @Override
  25. public void deleteAccount(Integer id) {
  26. jdbcTemplate.update(" DELETE FROM `account` WHERE id = ? ", id);
  27. }
  28. @Override
  29. public Account findById(Integer id) {
  30. return jdbcTemplate.queryForObject("SELECT * FROM `account` WHERE id= ?", new BeanPropertyRowMapper<>(Account.class), id);
  31. }
  32. @Override
  33. public List<Account> findAll() {
  34. return jdbcTemplate.query("SELECT * FROM `account`", new BeanPropertyRowMapper<>(Account.class));
  35. }
  36. @Override
  37. public Long findCount() {
  38. return jdbcTemplate.queryForObject(" SELECT count(*) FROM `account` ", new SingleColumnRowMapper<>());
  39. }
  40. @Override
  41. public Account findByName(String name) {
  42. if (StringUtils.isEmpty(name)) {
  43. return null;
  44. }
  45. List<Account> accountList = jdbcTemplate.query("SELECT * FROM `account` WHERE `name` =?", new BeanPropertyRowMapper<>(Account.class), name);
  46. if (CollectionUtils.isEmpty(accountList)) {
  47. return null;
  48. }
  49. if (accountList.size() > 1) {
  50. throw new RuntimeException(name + "在数据库中不止一个");
  51. }
  52. return accountList.get(0);
  53. }
  54. }
  • AccountService.java
  1. package com.sunxiaping.spring5.service;
  2. public interface AccountService {
  3. /**
  4. * 转账
  5. *
  6. * @param sourceName 转出人
  7. * @param targetName 转入人
  8. * @param money 金额
  9. */
  10. void transfer(String sourceName, String targetName, Double money);
  11. }
  • AccountServiceImpl.java
  1. package com.sunxiaping.spring5.service.impl;
  2. import com.sunxiaping.spring5.dao.AccountDao;
  3. import com.sunxiaping.spring5.domain.Account;
  4. import com.sunxiaping.spring5.service.AccountService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Service;
  7. import org.springframework.transaction.annotation.Transactional;
  8. import org.springframework.util.ObjectUtils;
  9. @Service
  10. @Transactional
  11. public class AccountServiceImpl implements AccountService {
  12. @Autowired
  13. private AccountDao accountDao;
  14. @Override
  15. public void transfer(String sourceName, String targetName, Double money) {
  16. Account source = accountDao.findByName(sourceName);
  17. Account target = accountDao.findByName(targetName);
  18. if (!ObjectUtils.isEmpty(source) && !ObjectUtils.isEmpty(target)) {
  19. source.setMoney(source.getMoney() - money);
  20. target.setMoney(target.getMoney() + money);
  21. accountDao.updateAccount(source);
  22. //模拟异常
  23. int num = 10 / 0;
  24. accountDao.updateAccount(target);
  25. }
  26. }
  27. }
  • 测试:
  1. package com.sunxiaping.spring5;
  2. import com.sunxiaping.spring5.config.SpringConfig;
  3. import com.sunxiaping.spring5.service.AccountService;
  4. import org.junit.Test;
  5. import org.junit.runner.RunWith;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.test.context.ContextConfiguration;
  8. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  9. @RunWith(SpringJUnit4ClassRunner.class)
  10. @ContextConfiguration(classes = SpringConfig.class)
  11. public class Spring5Test {
  12. @Autowired
  13. private AccountService accountService;
  14. @Test
  15. public void test() {
  16. accountService.transfer("张三","李四",100d);
  17. }
  18. }