一、简介

  对于数据访问层,无论是SQL还是NOSQL, Spring Boot默认采用整合Spring Data的方式进行统一处理,添加大量自动配置,屏蔽了很多设置。

  引入各种xxxTemplate, xxxRepository来简化我们对数据访问层的操作。对我们来说只需要进行简单的设置即可。我们将在数据访问章节测试使用SQL相关、NOSQL在缓存、消息、检索等章节测试。

  相关的 starter:

  

 

 

 

二、整合JDBC

  1、添加依赖信息

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
            <scope>runtime</scope>
        </dependency>

 

  2、配置数据库信息

spring:
  datasource:
    username: root
    password: '123456'
    driver-class-name: com.mysql.cj.jdbc.Driver
#    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.1.6:3306/jdbc

 

    如果没有更新驱动的,使用 com.mysql.jdbc.Driver。

    如果在 Maven 中更新了 mysql 驱动,使用 com.mysql.cj.jdbc.Driver 驱动。

  3、测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBoot06DataJdbcApplicationTests {

    @Autowired
    private DataSource dataSource;

    @Test
    public void contextLoads() throws SQLException {
        System.out.println(dataSource.getClass());
        Connection connection = dataSource.getConnection();
        System.out.println("connection = " + connection);
    }

}

 

  4、效果

  (1)默认是用org.apache.tomcat.jdbc.pool.DataSource作为数据源;

  (2)数据源的相关配置都在DataSourceProperties里面;

 

三、自动配置原理

  jdbc 自动配置相关类所在包:

org.springframework.boot.autoconfigure.jdbc

  

  1、数据源的自动配置

    参考 DataSourceConfiguration,根据配置创建数据源,默认使用 Tomcat 连接池;

    可以使用 spring.datasource.type 指定自定义的数据源类型;

  2、SpringBoot 默认支持的数据源

org.apache.tomcat.jdbc.pool.DataSource
com.zaxxer.hikari.HikariDataSource
org.apache.commons.dbcp.BasicDataSource
org.apache.commons.dbcp2.BasicDataSource

  

  3、自定义数据源类型

/**
 * Generic DataSource configuration.
 */
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {

    @Bean
    public DataSource dataSource(DataSourceProperties properties) {
        //使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性
        return properties.initializeDataSourceBuilder().build();
    }

}

public DataSourceBuilder initializeDataSourceBuilder() {
    return DataSourceBuilder.create(getClassLoader()).type(getType())
            .driverClassName(determineDriverClassName()).url(determineUrl())
            .username(determineUsername()).password(determinePassword());
}

public DataSource build() {
    Class<? extends DataSource> type = getType();
    DataSource result = BeanUtils.instantiate(type);
    maybeGetDriverClassName();
    bind(result);
    return result;
}

private void bind(DataSource result) {
    MutablePropertyValues properties = new MutablePropertyValues(this.properties);
    new RelaxedDataBinder(result).withAlias("url", "jdbcUrl")
            .withAlias("username", "user").bind(properties);
}

 

  

  4、DataSourceInitializer 

    在 DataSourceAutoConfiguration 类中会导入一个 DataSourceInitializer 组件:

    

 

 

     DataSourceInitializer 组件:

/**
 * Bean to handle {@link DataSource} initialization by running {@literal schema-*.sql} on
 * {@link PostConstruct} and {@literal data-*.sql} SQL scripts on a
 * {@link DataSourceInitializedEvent}.
 * 通过在 {@link PostConstruct} 上运行 {@literal schema-.sql} 和在 {@link DataSourceInitializedEvent} 上运行 {@literal data-.sql} SQL 脚本来处理 {@link DataSource} 初始化的 Bean。
*/

class DataSourceInitializer implements ApplicationListener<DataSourceInitializedEvent> {}

 

    作用:

    (1)runSchemaScripts():运行建表语句

    @PostConstruct
    public void init() {
        if (!this.properties.isInitialize()) {
            logger.debug("Initialization disabled (not running DDL scripts)");
            return;
        }
        if (this.applicationContext.getBeanNamesForType(DataSource.class, false,
                false).length > 0) {
            this.dataSource = this.applicationContext.getBean(DataSource.class);
        }
        if (this.dataSource == null) {
            logger.debug("No DataSource found so not initializing");
            return;
        }
        runSchemaScripts();
    }

    private void runSchemaScripts() {
        List<Resource> scripts = getScripts("spring.datasource.schema",
                this.properties.getSchema(), "schema");
        if (!scripts.isEmpty()) {
            String username = this.properties.getSchemaUsername();
            String password = this.properties.getSchemaPassword();
            runScripts(scripts, username, password);
            try {
                this.applicationContext
                        .publishEvent(new DataSourceInitializedEvent(this.dataSource));
                // The listener might not be registered yet, so don't rely on it.
                if (!this.initialized) {
                    runDataScripts();
                    this.initialized = true;
                }
            }
            catch (IllegalStateException ex) {
                logger.warn("Could not send event to complete DataSource initialization ("
                        + ex.getMessage() + ")");
            }
        }
    }
    private List<Resource> getScripts(String propertyName, List<String> resources,
            String fallback) {
        if (resources != null) {
            return getResources(propertyName, resources, true);
        }
        String platform = this.properties.getPlatform();
        List<String> fallbackResources = new ArrayList<String>();
        fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql");
        fallbackResources.add("classpath*:" + fallback + ".sql");
        return getResources(propertyName, fallbackResources, false);
    }

 

    (2)runDataScripts():运行插入数据的 SQL 语句

    @Override
    public void onApplicationEvent(DataSourceInitializedEvent event) {
        if (!this.properties.isInitialize()) {
            logger.debug("Initialization disabled (not running data scripts)");
            return;
        }
        // NOTE the event can happen more than once and
        // the event datasource is not used here
        if (!this.initialized) {
            runDataScripts();
            this.initialized = true;
        }
    }

    private void runDataScripts() {
        List<Resource> scripts = getScripts("spring.datasource.data",
                this.properties.getData(), "data");
        String username = this.properties.getDataUsername();
        String password = this.properties.getDataPassword();
        runScripts(scripts, username, password);
    }
    private List<Resource> getScripts(String propertyName, List<String> resources,
            String fallback) {
        if (resources != null) {
            return getResources(propertyName, resources, true);
        }
        String platform = this.properties.getPlatform();
        List<String> fallbackResources = new ArrayList<String>();
        fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql");
        fallbackResources.add("classpath*:" + fallback + ".sql");
        return getResources(propertyName, fallbackResources, false);
    }

 

    如果指定了 resources 资源路径,就加载,如果没有就加载 类路径下面的文件。

    默认只需要将文件命名为:

schema-*.sql 、data-*.sql

默认规则(类路径下面):schema.sql,schema-all.sql

 

也可以在配置文件中指定位置

spring:

  schema:

    - classpath:department.sql

  data:

    - classpath: abc.sql

    指定位置

 

spring:
  datasource:
    username: root
    password: '123456'
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.1.6:3306/jdbc
    schema:
      - classpath:department.sql
    data:
      - classpath:abc.sql

 

 

  5、操作数据库:自动配置了 JdbcTemplate 操作数据库

    JdbcTemplateAutoConfiguration 类自动注入了 JdbcTemplate:

@Configuration
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class JdbcTemplateAutoConfiguration {

    private final DataSource dataSource;

    public JdbcTemplateAutoConfiguration(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Bean
    @Primary
    @ConditionalOnMissingBean(JdbcOperations.class)
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(this.dataSource);
    }

    @Bean
    @Primary
    @ConditionalOnMissingBean(NamedParameterJdbcOperations.class)
    public NamedParameterJdbcTemplate namedParameterJdbcTemplate() {
        return new NamedParameterJdbcTemplate(this.dataSource);
    }

}

 

posted on 2021-12-18 15:47  格物致知_Tony  阅读(47)  评论(0编辑  收藏  举报