现在网站的外部链接怎么做,福州 建站 软件,网站免费正能量直接进入,网站 建设文章目录 前言第10章 SpringBoot整合JDBC10.1 SpringBoot整合JDBC的项目搭建10.1.1 初始化数据库10.1.2 整合项目10.1.2.1 导入JDBC和MySQL驱动依赖10.1.2.2 配置数据源 10.1.3 编写业务代码10.1.3.1 编写与t_user表对应的实体类User10.1.3.2 编写Dao层代码10.1.3.3 编写Servic… 文章目录 前言第10章 SpringBoot整合JDBC10.1 SpringBoot整合JDBC的项目搭建10.1.1 初始化数据库10.1.2 整合项目10.1.2.1 导入JDBC和MySQL驱动依赖10.1.2.2 配置数据源 10.1.3 编写业务代码10.1.3.1 编写与t_user表对应的实体类User10.1.3.2 编写Dao层代码10.1.3.3 编写Service层代码 10.1.4 编写主启动类10.1.5 测试结果 10.2 整合JDBC后的自动装配10.2.1 配置数据源10.2.1.1 DataSourceInitializerInvoker1createSchema2initialize 10.2.1.2 DataSourceInitializerPostProcessor 10.2.2 创建JdbcTemplate10.2.3 配置事务管理器 前言
在实际SpringBoot项目中通常都离不开与数据库的交互更多的选择是使用持久层框架MyBatis或SpringData等而不是原生的spring-jdbc。
但学习SpringBoot整合JDBC场景下的组件装配以及注解声明式事务的生效原理、控制流程、事务传播行为等依旧是必要的对后续学习SpringBoot整合持久层框架具有很大帮助。
第10章 SpringBoot整合JDBC
10.1 SpringBoot整合JDBC的项目搭建
10.1.1 初始化数据库
选择MySQL作为本项目的数据源创建一个新的数据库springboot_demo和一个新表t_user
CREATE DATABASE springboot_demo CHARACTER SET utf8mb4;CREATE TABLE t_user(id INT(11) NOT NULL AUTO_INCREMENT,NAME VARCHAR(20) NOT NULL,tel VARCHAR(20) NULL,PRIMARY KEY (id)
);10.1.2 整合项目
10.1.2.1 导入JDBC和MySQL驱动依赖
dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jdbc/artifactId/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion5.1.47/version/dependency
/dependencies10.1.2.2 配置数据源
在resources目录下新建application.properties文件并配置数据源
spring.datasource.driver-class-namecom.mysql.jdbc.Driver
spring.datasource.urljdbc:mysql://localhost:3306/springboot_demo?characterEncodingutf8
spring.datasource.usernameroot
spring.datasource.password12345610.1.3 编写业务代码
10.1.3.1 编写与t_user表对应的实体类User
public class User {private Integer id;private String name;private String tel;// getter setter toString ...
}10.1.3.2 编写Dao层代码
Repository
public class UserDao {Autowiredprivate JdbcTemplate jdbcTemplate;public void save(User user) {jdbcTemplate.update(insert into t_user (name, tel) values (?, ?),user.getName(), user.getTel());}public ListUser findAll() {return jdbcTemplate.query(select * from t_user,BeanPropertyRowMapper.newInstance(User.class));}}10.1.3.3 编写Service层代码
Service
public class UserService {Autowiredprivate UserDao userDao;Transactional(rollbackFor Exception.class)public void test() {User user new User();user.setName(齐天大圣);user.setTel(12306);userDao.save(user);ListUser userList userDao.findAll();userList.forEach(System.out::println);}}10.1.4 编写主启动类
主启动类注意两点第一是要获取IOC容器并提取出UserService类调用其test方法第二是要标注EnableTransactionManagement注解开启注解式声明式事务。
SpringBootApplication
EnableTransactionManagement
public class JDBCApp {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(JDBCApp.class, args);UserService userService context.getBean(UserService.class);userService.test();}}10.1.5 测试结果
运行主启动类控制台可以正确打印出一条用户信息说明SpringBoot整合JDBC场景顺利完成。
User{id1, name齐天大圣, tel12306}10.2 整合JDBC后的自动装配
由 10.1 节可知对于原生的JDBC整合后主启动类中并没有声明与之相关的注解因此有关JDBC的组件装配都是以自动配置类的方式实现的。
借助IDEA通过spring-boot-autoconfigure依赖的spring.factories文件可以找到有关JDBC的自动配置类
源码1spring-boot-autoconfigure-2.3.11.RELEASE.jar!/META-INF/spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\可以发现SpringBoot默认支持的自动配置包含数据源、JdbcTemplate、事务管理器、JNDI、XA协议等。
10.2.1 配置数据源
在SpringBoot的官方文档中介绍了SpringBoot支持的数据源连接池 1.1.4. Supported Connection Pools Spring Boot uses the following algorithm for choosing a specific implementation: We prefer HikariCP for its performance and concurrency. If HikariCP is available, we always choose it. Otherwise, if the Tomcat pooling DataSource is available, we use it. Otherwise, if Commons DBCP2 is available, we use it. If none of HikariCP, Tomcat, and DBCP2 are available and if Oracle UCP is available, we use it. 可见SpringBoot支持的数据源连接池包括HikariCP、Tomcat、DBCP2、Oracle UCP等默认使用HikariCP。
因此自动配置类DataSourceAutoConfiguration的静态内部类PooledDataSourceConfiguration会生效。因为它通过Import注解导入了DataSourceConfiguration.Hikari类。
源码2DataSourceAutoConfiguration.javaConfiguration(proxyBeanMethods false)
ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
ConditionalOnMissingBean(type io.r2dbc.spi.ConnectionFactory)
EnableConfigurationProperties(DataSourceProperties.class)
Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {Configuration(proxyBeanMethods false)Conditional(PooledDataSourceCondition.class)ConditionalOnMissingBean({ DataSource.class, XADataSource.class })Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,DataSourceJmxConfiguration.class })protected static class PooledDataSourceConfiguration {}
}源码3DataSourceConfiguration.javaConfiguration(proxyBeanMethods false)
ConditionalOnClass(HikariDataSource.class)
ConditionalOnMissingBean(DataSource.class)
ConditionalOnProperty(name spring.datasource.type, havingValue com.zaxxer.hikari.HikariDataSource,matchIfMissing true)
static class Hikari {BeanConfigurationProperties(prefix spring.datasource.hikari)HikariDataSource dataSource(DataSourceProperties properties) {HikariDataSource dataSource createDataSource(properties, HikariDataSource.class);if (StringUtils.hasText(properties.getName())) {dataSource.setPoolName(properties.getName());}return dataSource;}
}由 源码2、3 可知SpringBoot默认会创建一个HikariDataSource。
此外DataSourceAutoConfiguration还使用Import注解导入了一个DataSourceInitializationConfiguration配置类。
源码4DataSourceInitializationConfiguration.javaConfiguration(proxyBeanMethods false)
Import({DataSourceInitializerInvoker.class, DataSourceInitializationConfiguration.Registrar.class})
class DataSourceInitializationConfiguration {static class Registrar implements ImportBeanDefinitionRegistrar {private static final String BEAN_NAME dataSourceInitializerPostProcessor;Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (!registry.containsBeanDefinition(BEAN_NAME)) {// 注册DataSourceInitializerPostProcessorGenericBeanDefinition beanDefinition new GenericBeanDefinition();beanDefinition.setBeanClass(DataSourceInitializerPostProcessor.class);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);beanDefinition.setSynthetic(true);registry.registerBeanDefinition(BEAN_NAME, beanDefinition);}}}}由 源码4 可知这个配置类又使用Import注解导入了一个DataSourceInitializerInvoker和一个Registrar注册器。注册器又向BeanDefinition中注册了一个DataSourceInitializerPostProcessor。
10.2.1.1 DataSourceInitializerInvoker
由类名理解这是一个数据源初始化的执行器。
源码5DataSourceInitializerInvoker.javaclass DataSourceInitializerInvoker implements ApplicationListenerDataSourceSchemaCreatedEvent, InitializingBean {Overridepublic void afterPropertiesSet() {DataSourceInitializer initializer getDataSourceInitializer();if (initializer ! null) {boolean schemaCreated this.dataSourceInitializer.createSchema();if (schemaCreated) {initialize(initializer);}}}}由 源码5 可知DataSourceInitializerInvoker实现了ApplicationListener接口因此是一个监听器监听的事件是DataSourceSchemaCreatedEvent此外它还实现了InitializingBean接口会在对象创建后回调afterPropertiesSet方法执行初始化逻辑。
在afterPropertiesSet方法中首先会调用getDataSourceInitializer方法获取DataSourceInitializer实例随后执行DataSourceInitializer的createSchema方法如果执行成功则继续执行initialize方法。
1createSchema
该方法名直译为“创建约束”即执行DDL语句。也就在项目开发中可以先不创建数据库的表结构而是在应用程序启动时自动读取自定义的SQL文件执行DDL语句进行创建。
源码6DataSourceInitializer.javaboolean createSchema() {// 加载Schema资源ListResource scripts getScripts(spring.datasource.schema, this.properties.getSchema(), schema);// 解析Schema资源if (!scripts.isEmpty()) {if (!isEnabled()) {logger.debug(Initialization disabled (not running DDL scripts));return false;}String username this.properties.getSchemaUsername();String password this.properties.getSchemaPassword();runScripts(scripts, username, password);}return !scripts.isEmpty();
}private ListResource getScripts(String propertyName, ListString resources, String fallback) {if (resources ! null) {// 如果全局配置文件中配置了spring.datasource.schema属性// 则直接根据该spring.datasource.schema属性加载资源文件return getResources(propertyName, resources, true);}// 默认返回字符串allString platform this.properties.getPlatform();ListString fallbackResources new ArrayList();// 拼接文件名schema-all.sql和schema.sqlfallbackResources.add(classpath*: fallback - platform .sql);fallbackResources.add(classpath*: fallback .sql);// 加载文件schema-all.sql和schema.sqlreturn getResources(propertyName, fallbackResources, false);
}由 源码6 可知createSchema方法先读取全局配置文件中的spring.datasource.schema属性根据该属性配置的路径加载资源文件再对资源文件进行解析。在调用getScripts方法读取资源文件时分为两种情况
如果全局配置文件中配置了spring.datasource.schema属性则直接根据该属性的值加载资源文件如果全局配置文件没有配置该属性则加载名为schema-all.sql和schema.sql的资源文件。
2initialize
源码7DataSourceInitializerInvoker.javaprivate void initialize(DataSourceInitializer initializer) {try {// 广播DataSourceSchemaCreatedEvent事件this.applicationContext.publishEvent(new DataSourceSchemaCreatedEvent(initializer.getDataSource()));if (!this.initialized) {// 解析资源文件this.dataSourceInitializer.initSchema();this.initialized true;}} // catch ......
}Override
public void onApplicationEvent(DataSourceSchemaCreatedEvent event) {DataSourceInitializer initializer getDataSourceInitializer();if (!this.initialized initializer ! null) {initializer.initSchema();this.initialized true;}
}源码8DataSourceInitializer.javavoid initSchema() {// 加载资源文件ListResource scripts getScripts(spring.datasource.data, this.properties.getData(), data);// 解析资源文件if (!scripts.isEmpty()) {if (!isEnabled()) {logger.debug(Initialization disabled (not running data scripts));return;}String username this.properties.getDataUsername();String password this.properties.getDataPassword();runScripts(scripts, username, password);}
}由 源码7 可知initialize方法会广播一个DataSourceSchemaCreatedEvent事件回调onApplicationEvent方法最终执行DataSourceInitializer类的initSchema方法。
由 源码8 可知initSchema方法和createSchema方法大同小异不同的是initSchema方法寻找资源文件的依据是全局配置文件中的spring.datasource.data属性如果没有配置该属性则加载名为data-all.sql和data.sql的资源文件。
总结一下有了DataSourceInitializerInvoker的设计使得项目开发中可以自定义DDL语句和DML语句并保存在SQL文件中放置在resources目录下在项目启动时自动初始化数据库表结构和数据。
10.2.1.2 DataSourceInitializerPostProcessor
由类名理解这是一个专门为DataSourceInitializer定制的后置处理器。
源码9DataSourceInitializerPostProcessor.javaOverride
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof DataSource) {this.beanFactory.getBean(DataSourceInitializerInvoker.class);}return bean;
}由 源码9 可知当DataSourceInitializerPostProcessor检测到当前正在创建的bean对象的类型是DataSource主动调用BeanFactory的getBean方法创建一个DataSourceInitializerInvoker对象。这样做的目的是使预定定义好的SQL脚本立即执行以确保DataSource与数据库表结构、数据的同步初始化。
10.2.2 创建JdbcTemplate
源码10JdbcTemplateAutoConfiguration.javaConfiguration(proxyBeanMethods false)
ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
ConditionalOnSingleCandidate(DataSource.class)
AutoConfigureAfter(DataSourceAutoConfiguration.class)
EnableConfigurationProperties(JdbcProperties.class)
Import({ JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {}由 源码10 可知自动配置类JdbcTemplateAutoConfiguration会使用Import注解注册一个JdbcTemplateConfiguration配置类和一个NamedParameterJdbcTemplateConfiguration配置类。
源码11JdbcTemplateConfiguration.javaConfiguration(proxyBeanMethods false)
ConditionalOnMissingBean(JdbcOperations.class)
class JdbcTemplateConfiguration {BeanPrimaryJdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {JdbcTemplate jdbcTemplate new JdbcTemplate(dataSource);JdbcProperties.Template template properties.getTemplate();jdbcTemplate.setFetchSize(template.getFetchSize());jdbcTemplate.setMaxRows(template.getMaxRows());if (template.getQueryTimeout() ! null) {jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds());}return jdbcTemplate;}}由 源码11 可知JdbcTemplateConfiguration配置类会注册一个JdbcTemplate对象用于与数据库的简单交互。
源码12NamedParameterJdbcTemplateConfiguration.javaConfiguration(proxyBeanMethods false)
ConditionalOnSingleCandidate(JdbcTemplate.class)
ConditionalOnMissingBean(NamedParameterJdbcOperations.class)
class NamedParameterJdbcTemplateConfiguration {BeanPrimaryNamedParameterJdbcTemplate namedParameterJdbcTemplate(JdbcTemplate jdbcTemplate) {return new NamedParameterJdbcTemplate(jdbcTemplate);}}由 源码12 可知NamedParameterJdbcTemplateConfiguration配置类会注册一个NamedParameterJdbcTemplate对象用于支持参数命名化的JdbcTemplate增强。
10.2.3 配置事务管理器
源码13DataSourceTransactionManagerAutoConfiguration.javaConfiguration(proxyBeanMethods false)
ConditionalOnClass({JdbcTemplate.class, PlatformTransactionManager.class})
AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {Configuration(proxyBeanMethods false)ConditionalOnSingleCandidate(DataSource.class)static class DataSourceTransactionManagerConfiguration {BeanConditionalOnMissingBean(PlatformTransactionManager.class)DataSourceTransactionManager transactionManager(DataSource dataSource,ObjectProviderTransactionManagerCustomizers transactionManagerCustomizers) {DataSourceTransactionManager transactionManager new DataSourceTransactionManager(dataSource);transactionManagerCustomizers.ifAvailable((customizers) - customizers.customize(transactionManager));return transactionManager;}}}由 源码13 可知自动配置类DataSourceTransactionManagerAutoConfiguration会注册一个DataSourceTransactionManager用于支持基于数据源的事务控制。
······
本节完更多内容请查阅分类专栏SpringBoot源码解读与原理分析