支持
- JDBCTemple
- 声明式事务
- 支持REQUIRED传播模式
首先配置DataSource
因为我们先前已经实现了读取xml和yaml文件的方法,所以,对于JDBC的配置,我们只需要从properties中拿东西就好了
在我们的配置文件中配置文件内容
# application.properties summer.datasource.url=jdbc:mysql://localhost:3306/summer_jdbc?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC summer.datasource.username=root summer.datasource.password=your_password summer.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
然后实现一个由HikariCP连接池库支持的DataSource
@Configuration public class JdbcConfiguration {
@Bean(destroyMethod = "close") DataSource dataSource( // properties: @Value("${summer.datasource.url}") String url, @Value("${summer.datasource.username}") String username, @Value("${summer.datasource.password}") String password, @Value("${summer.datasource.driver-class-name:}") String driver, @Value("${summer.datasource.maximum-pool-size:20}") int maximumPoolSize, @Value("${summer.datasource.minimum-pool-size:1}") int minimumPoolSize, @Value("${summer.datasource.connection-timeout:30000}") int connTimeout ) { var config = new HikariConfig(); config.setAutoCommit(false); config.setJdbcUrl(url); config.setUsername(username); config.setPassword(password); if (driver != null) { config.setDriverClassName(driver); } config.setMaximumPoolSize(maximumPoolSize); config.setMinimumIdle(minimumPoolSize); config.setConnectionTimeout(connTimeout); return new HikariDataSource(config); } }
|
定义JdbcTemplate,基于Template模式,提供了大量以回调作为参数的模板方法,其中以execute(ConnectionCallback)为基础
public <T> T execute(ConnectionCallback<T> action) { try (Connection newConn = dataSource.getConnection()) { T result = action.doInConnection(newConn); return result; } catch (SQLException e) { throw new DataAccessException(e); } }
|
基本上都是实现execute的
实现声明式事务
首先定义声明式事务
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface Transactional { String value() default "platformTransactionManager"; }
|
我们需要一个类来协助bean管理事务
定义一个接口PlatformTransactionManager,来标识管理的类型。
public interface PlatformTransactionManager { }
|
然后定义TransactionStatus来管理事务状态,将来如果扩展,则可以将事务的传播模式存储在里面。
public class TransactionStatus { final Connection connection;
public TransactionStatus(Connection connection) { this.connection = connection; } }
|
最后加上一个DataSourceTransactionManager,他用的是ThreadLocal存储的TransactionStatus,和一个DataSource
public class DataSourceTransactionManager implements PlatformTransactionManager, InvocationHandler { static final ThreadLocal<TransactionStatus> transactionStatus = new ThreadLocal<>(); final DataSource dataSource;
public DataSourceTransactionManager(DataSource dataSource) { this.dataSource = dataSource; } }
|
这里的实现是和AOP那一节一样的,只是对于事务来说来说,他做的是对该类的所有方法都进行一个代理。苏所以,无需在加任何的方法注解,只需要在invoke内部处理就好
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { TransactionStatus ts = transactionStatus.get(); if (ts == null) { try (Connection connection = dataSource.getConnection()) { final boolean autoCommit = connection.getAutoCommit(); if (autoCommit) { connection.setAutoCommit(false); } try { transactionStatus.set(new TransactionStatus(connection)); Object r = method.invoke(proxy, args); connection.commit(); return r; } catch (InvocationTargetException e) { TransactionException te = new TransactionException(e.getCause()); try { connection.rollback(); } catch (SQLException sqle) { te.addSuppressed(sqle); } throw te; } finally { transactionStatus.remove(); if (autoCommit) { connection.setAutoCommit(true); } } } } else { return method.invoke(proxy, args); } }
|
如此,我们就实现了声明式的事务。
但是,如果我们在一个声明式的事务里调用了另一个声明式事务,这两个事务怎么合并到同一个事务里的呢?
首先,我们要知晓当前的事务连接
public class TransactionalUtils { @Nullable public static Connection getCurrentConnection() { TransactionStatus ts = DataSourceTransactionManager.transactionStatus.get(); return ts == null ? null : ts.connection; } }
|
然后更新一下JdbcTemplate的代码,拿去事务的时候是从这个线程的状态判断的
Connection current = TransactionalUtils.getCurrentConnection(); if (current != null) { try { return action.doInConnection(current); } catch (SQLException e) { throw new DataAccessException(e); } } try (Connection newConn = dataSource.getConnection()) { return action.doInConnection(newConn); } catch (SQLException e) { throw new DataAccessException(e); }
|
准备好AnnotationProxyBeanPostProcessor来使得AOP机制生效
public class TransactionalBeanPostProcessor extends AnnotationProxyBeanPostProcessor<Transactional> { }
|
然后在配置类中配置所有的内容基本上就可以了
@Configuration public class JdbcConfiguration {
@Bean(destroyMethod = "close") DataSource dataSource( // properties: @Value("${summer.datasource.url}") String url, @Value("${summer.datasource.username}") String username, @Value("${summer.datasource.password}") String password, @Value("${summer.datasource.driver-class-name:}") String driver, @Value("${summer.datasource.maximum-pool-size:20}") int maximumPoolSize, @Value("${summer.datasource.minimum-pool-size:1}") int minimumPoolSize, @Value("${summer.datasource.connection-timeout:30000}") int connTimeout ) { ... return new HikariDataSource(config); }
@Bean JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource) { return new JdbcTemplate(dataSource); }
@Bean TransactionalBeanPostProcessor transactionalBeanPostProcessor() { return new TransactionalBeanPostProcessor(); }
@Bean PlatformTransactionManager platformTransactionManager(@Autowired DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
|