Spring數據訪問1 - 數據源配置及數據庫連接池的概念


無論你要選擇哪種數據訪問方式,首先你都需要配置好數據源引用。

Spring中配置數據源的幾種方式

  • 通過在JDBC驅動程序定義的數據源;
  • 通過JNDI查找的數據源;
  • 連接池的數據源;

對於即將發布到生產環境中的應用程序, 建議使用從連接池獲取連接的數據源。 可能的話, 傾向於通過應用服務器的JNDI來獲取數據源。 

使用JNDI數據源

Spring應用程序經常部署在Java EE應用服務器中,例如Tomcat、JBoss。這些服務器器允許你通過配置獲取數據源,這樣做的好處是數據源可以在應用之外進行管理。另外,在應用服務器中數據源通常都是以連接池的方式組織,從而具備更好的性能,並且還支持系統管理員對其進行熱切換

對於Tomcat需要在tomcat/conf/context.xml中配置好連接信息,其中name指的是JNDI的名稱

<Resource auth="Container" 
driverClassName="oracle.jdbc.driver.OracleDriver" 
name="jdbc/dev" 
password="dev" 
type="javax.sql.DataSource" 
url="jdbc:oracle:thin:@127.0.0.1:1521/orcl" 
username="dev"/>

對於Sping應用來說需要手動配置數據源Bean

@Bean
public JndiObjectFactoryBean dataSource() {
    JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
    jndiObjectFactoryBean.setJndiName("jdbc/dev");
    jndiObjectFactoryBean.setResourceRef(true);
    jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
    return jndiObjectFactoryBean;
}

Spring Boot中幫我們自動配置好了,只需要在application.properties中聲明指定jndiName就可以了。

spring.datasource.jndi-name=java:comp/env/jdbc/dev
// 或者是
spring.datasource.jndi-name=jdbc/dev

因為Spring boot自帶web容器,因此JNDI方式只適用於將war發布到獨立的web容器啟動的方式。

 

使用連接池的數據源

啥是連接池?

  數據庫連接池是web容器(比如Tomcat)提供的一個數據庫連接管理的容器,連接池負責分配、管理和釋放數據庫連接,它允許應用程序重復使用一個現有的數據庫連接,而不是再重新建立一個。

 為什么要用它?

   數據庫連接是一種關鍵的有限的昂貴的資源,這一點在多用戶的網頁應用程序中體現得尤為突出。  一個數據庫連接對象均對應一個物理數據庫連接,每次操作都打開一個物理連接,使用完都關閉連接,這樣造成系統的性能低下。 數據庫連接池的解決方案是在應用程序啟動時建立足夠的數據庫連接,並將這些連接組成一個連接池(簡單說:在一個“池”里放了好多半成品的數據庫連接對象),由應用程序動態地對池中的連接進行申請、使用和釋放。對於多於連接池中連接數的並發請求,應該在請求隊列中排隊等待。並且應用程序可以根據池中連接的使用率,動態增加或減少池中的連接數。 連接池技術盡可能多地重用了消耗內存地資源,大大節省了內存,提高了服務器地服務效率,能夠支持更多的客戶服務。通過使用連接池,將大大提高程序運行效率,同時,我們可以通過其自身的管理機制來監視數據庫連接的數量、使用情況等。 

 

在Spring Boot中怎么用?

配置連接池參數

  • 最小連接數:是連接池一直保持的數據庫連接,所以如果應用程序對數據庫連接的使用量不大,將會有大量的數據庫連接資源被浪費.
  • 最大連接數:是連接池能申請的最大連接數,如果數據庫連接請求超過次數,后面的數據庫連接請求將被加入到等待隊列中,這會影響以后的數據庫操作
  • 最大空閑時間: 超出這個時間,該連接將被銷毀
  • 獲取連接超時時間: 超出時間程序將會返回連接超時異常
  • 超時重試連接次數: 超時后重新連接的次數

 

Spring Boot2.0默認使用HikariCP連接池管理數據源

 

HikariCP的特點是快,字節碼級別優化(很多⽅法通過 JavaAssist ⽣成),⼤量⼩改進

常用HkariCP配置

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.hikari.maximumPoolSize=10
spring.datasource.hikari.minimumIdle=10 spring.datasource.hikari.idleTimeout=600000 spring.datasource.hikari.connectionTimeout=30000 spring.datasource.hikari.maxLifetime=1800000

更多配置去官網https://github.com/brettwooldridge/HikariCP

這樣就搞定了,Spring Boot幫我們把數據源創建、事務管理、JdbcTemplate創建都搞定了。

 

Alibaba Druid 連接池(https://github.com/alibaba/druid)

Druid連接池是阿⾥巴巴開源的數據庫連接池項⽬。 Druid連接池為監控而生,內置強⼤的監控功能,監控特性不影響性能。功能強⼤,能防SQL注⼊,內置Logging能診斷Hack應⽤⾏為。
 

實用的功能
詳細的監控(真的是全⾯)
ExceptionSorter,針對主流數據庫的返回碼都有⽀持
SQL 防注⼊
內置加密配置
眾多擴展點,⽅便進⾏定制

Spring Boot中通過druid-spring-boot-starter來引入druid, spring.datasource.druid.* 來配置相關參數,如下

 

Durid通過Filter來擴展和定制連接池操作的細節

Filter 配置
• spring.datasource.druid.filters=stat,config,wall,log4j (全部使⽤默認值) 
密碼加密
• spring.datasource.password=<加密密碼>
• spring.datasource.druid.filter.config.enabled=true
• spring.datasource.druid.connection-properties=config.decrypt=true;config.decrypt.key=<public-key>
SQL 防注⼊
• spring.datasource.druid.filter.wall.enabled=true
• spring.datasource.druid.filter.wall.db-type=h2
• spring.datasource.druid.filter.wall.config.delete-allow=false  // 是否允許執行delete
• spring.datasource.druid.filter.wall.config.drop-table-allow=false // 是否允許執行drop table

實現Filter

可以繼承 FilterEventAdapter 以便⽅便地實現 Filter

修改 META-INF/druid-filter.properties 增加 Filter 配置

 

在數據庫連接建立前后打印日志的例子:

 

 

 

多數據源配置

在Sring boot中配置多數據源需要寫多套配置,例如創建兩個數據源

foo.datasource.url=jdbc:h2:mem:foo
foo.datasource.username=sa
foo.datasource.password=

bar.datasource.url=jdbc:h2:mem:bar
bar.datasource.username=sa
bar.datasource.password=

分別用Java Config 為不同的配置創建數據源Bean。

首先讓Spring boot取消自動配置

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class,
        JdbcTemplateAutoConfiguration.class})

然后手動配置DataSource和TransactionManager,@Primary注解指明當有兩個類實現同一接口的時候該采用哪個實現類進行@Autowried,另外可以在實現類上用@Qualifier("className")指定名稱,后面@Autowried的時候也用@Qualifier指定注入哪個類。

@Configuration
@Slf4j
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties("foo.datasource")
    public DataSourceProperties fooDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    public DataSource fooDataSource() {
        DataSourceProperties dataSourceProperties = fooDataSourceProperties();
        log.info("foo datasource: {}", dataSourceProperties.getUrl());
        return dataSourceProperties.initializeDataSourceBuilder().build();
    }

    @Bean
    JdbcTemplate fooJdbcTemplate(@Qualifier("fooDataSource")DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean
    @Resource
    public PlatformTransactionManager fooTxManager(DataSource fooDataSource) {
        return new DataSourceTransactionManager(fooDataSource);
    }



    @Bean
    @ConfigurationProperties("bar.datasource")
    public DataSourceProperties barDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    public DataSource barDataSource() {
        DataSourceProperties dataSourceProperties = barDataSourceProperties();
        log.info("bar datasource: {}", dataSourceProperties.getUrl());
        return dataSourceProperties.initializeDataSourceBuilder().build();
    }

    @Primary
    @Bean
    JdbcTemplate barJdbcTemplate(@Qualifier("barDataSource")DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean
    @Resource
    public PlatformTransactionManager barTxManager(DataSource barDataSource) {
        return new DataSourceTransactionManager(barDataSource);
    }
}

 

到此我們可以使用DataSource了

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class,
        JdbcTemplateAutoConfiguration.class})
@Slf4j
public class MultiDataSourceApplication implements CommandLineRunner {

    @Autowired
    private DataSource dataSource;

    @Autowired
    @Qualifier("barDataSource")
    private DataSource barDataSource;

    @Autowired
    @Qualifier("fooJdbcTemplate")
    private JdbcTemplate fooJdbcTemplate;

    @Autowired
    @Qualifier("barJdbcTemplate")
    private JdbcTemplate barJdbcTemplate;

    public static void main(String[] args) {
        SpringApplication.run(MultiDataSourceApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        showConnection();
    }

    private void showConnection() throws SQLException {
        log.info("fooDataSource數據源: " + dataSource.toString());
        log.info("fooJdbcTemplate: " + fooJdbcTemplate.toString());

        log.info("barDataSource數據源: " + barDataSource.toString());
        log.info("barJdbcTemplate: " + barJdbcTemplate.toString());


    }
}

 

 

 

對於即將發布到生產環境中的應用程序, 建議使用從連接池獲取連接的數據源。 如果可能的話, 我傾向於通過應用服務器的JNDI來獲取數據源。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM