SpringJDBC
数据源
数据源是数据库连接的来源,通过 DataSource 接口获取
类型
- 通用数据源
- javax.sql.DataSource
- 分布式数据源
- javax.sql.XADatasource
- 嵌入式数据源
- org.springframework.jdbc.datasource.embedded.EmbeddedDatabase
SpringBoot 实际使用场景
在 Spring Boot 2.0+,如果应用采用 Spring Web MVC 作为 Web 服务, 默认情况下,使用 嵌入式 Tomcat。
如果采用Spring Web Flux,默认情况下,使用 Netty Web Server(嵌入式)
从 Spring Boot 1.4 支持 FailureAnalysisReporter 实现
WebFlux
Mono : 0 - 1 Publisher(类似于Java 8 中的 Optional)
Flux: 0 - N Publisher(类似于Java 中的 List)
传统的 Servlet 采用 HttpServletRequest、HttpServletResponse
WebFlux 采用:ServerRequest、ServerResponse(不再限制于 Servlet 容器,可以选择自定义实现,比如 Netty Web Server)
单数据源的场景
数据连接池技术
Apache Commons DBCP
- commons-dbcp2
- 依赖:commons-pool2
- commons-dbcp(老版本)
- 依赖:commons-pool
Tomcat DBCP
WebMVC 和 WebFlux
项目结构

配置数据源
spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://172.16.11.125:3306/test spring.datasource.username=root spring.datasource.password=123456定义实体类 User
public class User { private Long id; private String name; // get&set @Override public String toString() { return "User{id=" + id + ", name='" + name + '\'' + '}'; } }定义 UserRepository
/** * 用户仓储(SQL、或NOSQL、或内存型) */ @Repository public class UserRepository { private final DataSource dataSource; // 推荐使用构造注入 public UserRepository(DataSource dataSource) { this.dataSource = dataSource; } public boolean save(User user) { System.err.println("save user: " + user); System.err.println(""); return true; } public Collection<User> findAll() { return Collections.emptyList(); } }WebMVC 写法
@RestController public class UserController { private final ExecutorService executorService = Executors.newFixedThreadPool(5); private final UserRepository userRepository; public UserController(UserRepository userRepository) { this.userRepository = userRepository; } @RequestMapping("/web/mvc/user/save") public Boolean save(@RequestBody User user) throws ExecutionException, InterruptedException { // Future<Boolean> future // = executorService.submit(() -> {userRepository.save(user);}); // return future.get(); return userRepository.save(user); } }WebFlux 写法
UserHandler
@Component public class UserHandler { private final UserRepository userRepository; public UserHandler(UserRepository userRepository) { this.userRepository = userRepository; } public Mono<ServerResponse> save(ServerRequest serverRequest) { // 在 Spring Web MVC 中使用 @RequestBody // 在 Spring Web Flux 中使用 ServerRequest // Mono<User> 类似于 Optional<User> Mono<User> userMono = serverRequest.bodyToMono(User.class); // map 相当于转化工作 Mono<Boolean> booleanMono = userMono.map(userRepository::save); return ServerResponse.ok().body(booleanMono, Boolean.class); } }WebFluxConfiguration
@Configuration public class WebFluxConfiguration { @Bean public RouterFunction<ServerResponse> saveUser(UserHandler userHandler) { // route: 静态导入 return route(POST("/web/flux/user/save"), userHandler::save); } }
postman 测试
webmvc:/web/mvc/user/save

webflux:/web/flux/user/save

多数据源场景
配置多个数据源
新增数据源配置类,MultiDataSourceConfiguration
@Configuration public class MultiDataSourceConfiguration { @Bean @Primary public DataSource masterDataSource() { DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create(); DataSource dataSource = dataSourceBuilder .driverClassName("com.mysql.jdbc.Driver") .url("jdbc:mysql://172.16.11.125:3306/test") .username("root") .password("123456").build(); return dataSource; } @Bean public DataSource slaveDataSource() { DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create(); DataSource dataSource = dataSourceBuilder .driverClassName("com.mysql.jdbc.Driver") .url("jdbc:mysql://172.16.11.125:3306/test2") .username("root") .password("123456").build(); return dataSource; } }根据 Bean 注入不同数据源
注入的时候通过 @Qualifier 来区分不同数据源
@Repository public class UserRepository { private final DataSource dataSource; private final DataSource masterDataSource; private final DataSource slaveDataSource; public UserRepository(DataSource dataSource, @Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaveDataSource") DataSource slaveDataSource) { this.dataSource = dataSource; this.masterDataSource = masterDataSource; this.slaveDataSource = slaveDataSource; } public boolean save(User user) { System.err.println("save user: " + user); return true; } public Collection<User> findAll() { return Collections.emptyList(); } }
事务
事务用于提供数据完整性,并在并发访问下确保数据视图的一致性。
重要概念
自动提交模式(Auto-commit mode)
jdbc 自动提交
public boolean save(User user) { Connection connection = null; boolean success = false; try { connection = dataSource.getConnection(); PreparedStatement prepareStatement = connection.prepareStatement("INSERT INTO user(name) values (?);"); prepareStatement.setString(1, user.getName()); success = prepareStatement.executeUpdate() > 0; prepareStatement.close(); } catch (SQLException e) { e.printStackTrace(); } finally { if (null != connection) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } return success; }jdbc 手动提交
public boolean save(User user) { Connection connection = null; boolean success = false; try { connection = dataSource.getConnection(); connection.setAutoCommit(false); PreparedStatement prepareStatement = connection.prepareStatement("INSERT INTO user(name) values (?);"); prepareStatement.setString(1, user.getName()); success = prepareStatement.executeUpdate() > 0; prepareStatement.close(); } catch (SQLException e) { e.printStackTrace(); } finally { if (null != connection) { try { connection.commit(); connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } return success; }
事务隔离级别(Transaction isolation levels)
Java JDBC 隔离级别,javax.sql.Connection

- TRANSACTION_READ_UNCOMMITTED
- TRANSACTION_READ_COMMITTED
- TRANSACTION_REPEATABLE_READ
- TRANSACTION_SERIALIZABLE
从上至下,级别越高,性能越差
Spring JDBC 隔离
Spring Transaction 实现重用了 JDBC API
Isolation -> TransactionDefinition


- ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED
- ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED
- ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ
- ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE
保护点(Savepoints)
public boolean savePoint(User user) { boolean success = false; Connection connection = null; try { connection = dataSource.getConnection(); connection.setAutoCommit(false); // 创建还原点 Savepoint savepoint = connection.setSavepoint("T1"); try { // 其他事务操作 transactionSave(user); } catch (Exception e) { // 发生异常之后,回滚到还原点 connection.rollback(savepoint); } PreparedStatement prepareStatement = connection.prepareStatement("INSERT INTO user(name) values (?);"); prepareStatement.setString(1, user.getName()); success = prepareStatement.executeUpdate() > 0; prepareStatement.close(); connection.commit(); connection.releaseSavepoint(savepoint); } catch (SQLException e) { e.printStackTrace(); } finally { if (null != connection) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } return success; }
Spring 事务控制
@Annotation 驱动
private final JdbcTemplate jdbcTemplate;
public UserRepository(DataSource dataSource,
@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource,
JdbcTemplate jdbcTemplate) {
this.dataSource = dataSource;
this.masterDataSource = masterDataSource;
this.slaveDataSource = slaveDataSource;
this.jdbcTemplate = jdbcTemplate;
}
@Transactional
public boolean save(User user) {
System.err.println("save user: " + user);
return jdbcTemplate.execute("INSERT INTO user(name) values (?);",
(PreparedStatementCallback<Boolean>) preparedStatement -> {
preparedStatement.setString(1, user.getName());
return preparedStatement.executeUpdate() > 0;
});
}
@Transaction
通过动态代理来控制事务 -
TransactionInterceptor
可以控制 rollback 的异常粒度:rollbackFor() 以及 noRollbackFor()

可以执行 事务管理器:transactionManager()

API 驱动
private final PlatformTransactionManager platformTransactionManager;
public UserRepository(DataSource dataSource,
@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource,
JdbcTemplate jdbcTemplate,
PlatformTransactionManager platformTransactionManager) {
this.dataSource = dataSource;
this.masterDataSource = masterDataSource;
this.slaveDataSource = slaveDataSource;
this.jdbcTemplate = jdbcTemplate;
this.platformTransactionManager = platformTransactionManager;
}
public boolean save(User user) {
Boolean success = false;
DefaultTransactionDefinition transactionDefinition =
new DefaultTransactionDefinition();
TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
try {
success = jdbcTemplate.execute("INSERT INTO user(name) values (?);",
(PreparedStatementCallback<Boolean>) preparedStatement -> {
preparedStatement.setString(1, user.getName());
return preparedStatement.executeUpdate() > 0;
});
platformTransactionManager.commit(transaction);
} catch (DataAccessException e) {
platformTransactionManager.rollback(transaction);
e.printStackTrace();
}
return success;
}
事务传播级别
在外层方法开启了事务,如果配置了传播级别,里面的方法也会拥有事务,这个事务类型和传播级别有关。
@Transactional
save(){
// insert
save1(); //如果save1没有配置Transactional,那么 save1 的事务跟传播级别有关
//如果配置了事务传播,那么 save1 也会有事务
}
Propagation

TransactionDefinition

用reactive web,原来mvc的好多东西都不能用了?
答:不是, Reactive Web 还是能够兼容 Spring WebMVC
开个线程池事务控制用API方式?比如开始写的Excutor.fixExcutor(5)
答:TransactionSynchronizationManager 使用大量的ThreadLocal 来实现的
假设一个service方法给了@Transaction标签,在这个方法中还有其他service 的某个方法,这个方法没有加@Transaction,那么如果内部方法报错,会回滚吗?
答:会的,当然可以过滤掉一些不关紧要的异常noRollbackFor()
spring 分布式事务 生产环境实现方式有哪些?
答:https://docs.spring.io/spring-boot/docs/2.0.0.M5/reference/htmlsingle/#boot-features-jta
