App下載

在 SpringBoot 應(yīng)用程序中使用多個(gè)數(shù)據(jù)源

加里敦大學(xué)學(xué)生 2021-09-02 10:39:33 瀏覽數(shù) (2574)
反饋

本篇文章,我將為大家介紹如何通過(guò)使用 Oracle 數(shù)據(jù)庫(kù)的簡(jiǎn)化應(yīng)用程序在 SpringBoot 應(yīng)用程序中使用多個(gè)數(shù)據(jù)源。下面是詳情內(nèi)容,希望能夠?qū)Ω魑蛔x者有所幫助。

項(xiàng)目創(chuàng)建

我們可以使用Spring Initializer創(chuàng)建具有以下依賴項(xiàng)的示例應(yīng)用程序:

依賴截圖。

選擇后,請(qǐng)按“生成”按鈕并將項(xiàng)目另存為 zip 文件。

我使用 Eclipse 作為 Java IDE,但你可以使用任何你喜歡的方式來(lái)開發(fā)示例應(yīng)用程序。打開 zip 文件,并將該文件夾作為 Eclipse 中的 Maven 項(xiàng)目導(dǎo)入。項(xiàng)目結(jié)構(gòu)應(yīng)如下所示:

項(xiàng)目結(jié)構(gòu)截圖。

屬性文件

首先,讓我們?cè)?application.properties 文件中定義兩個(gè)不同的數(shù)據(jù)源設(shè)置,如下所示:

server.port=9090
spring.output.ansi.enabled=ALWAYS

application-description=@project.description@
application-version=@project.version@

spring.customerdatasource.url=[url1]
spring.customerdatasource.username=[username1]
spring.customerdatasource.password=[password1]
spring.customerdatasource.driverClassName=oracle.jdbc.OracleDriver

spring.billingdatasource.url=[url2]
spring.billingdatasource.username=[username2]
spring.billingdatasource.password=[password1]
spring.billingdatasource.driverClassName=oracle.jdbc.OracleDriver

database.sql.databasename=SELECT dbid, name FROM v$database

正如我之前提到的,我使用過(guò) Oracle 數(shù)據(jù)庫(kù),但你也可以使用其他數(shù)據(jù)庫(kù),只要相應(yīng)地更改 ?driverClassName?。

數(shù)據(jù)源配置

為了創(chuàng)建 ?DataSource bean?,我創(chuàng)建了一個(gè) ?DatasourceConfig ?類并將其注釋為配置,并將兩個(gè)數(shù)據(jù)源添加為兩個(gè)不同的 ?bean?:

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

@Configuration
public class DatasourceConfig {

	Environment env;
	
	public DatasourceConfig(Environment env) {
		this.env = env;
	}

	@Bean(name = "customerDataSource")
    public DataSource customerDataSource() {
    	DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("spring.customerdatasource.driverClassName"));
        dataSource.setUrl(env.getProperty("spring.customerdatasource.url"));
        dataSource.setUsername(env.getProperty("spring.customerdatasource.username"));
        dataSource.setPassword(env.getProperty("spring.customerdatasource.password"));

        return dataSource;
    }

    @Bean(name = "billingDataSource")
    public DataSource billingDataSource() {
    	DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("spring.billingdatasource.driverClassName"));
        dataSource.setUrl(env.getProperty("spring.billingdatasource.url"));
        dataSource.setUsername(env.getProperty("spring.billingdatasource.username"));
        dataSource.setPassword(env.getProperty("spring.billingdatasource.password"));

        return dataSource;
    }
    
}

數(shù)據(jù)訪問(wèn)

我使用了 ?JdbcTemplate?作為數(shù)據(jù)訪問(wèn)。要?jiǎng)?chuàng)建 ?DAO ?接口,請(qǐng)?jiān)??com.tech.multipledatasources.dao? 包下添加 ?IDatabaseDAO ?接口,如下所示:

import com.tech.multipledatasources.domain.DatabaseInfo;

public interface IDatabaseDAO {
    
    public DatabaseInfo getDatabaseInfo();
}

我對(duì)這個(gè)接口做了兩種不同的實(shí)現(xiàn),因?yàn)榧词顾且粋€(gè)簡(jiǎn)單的應(yīng)用程序,現(xiàn)實(shí)生活中的場(chǎng)景也會(huì)有所不同。

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.tech.multipledatasources.domain.DatabaseInfo;

@Repository
public class BillingDAO implements IDatabaseDAO{
    
    private JdbcTemplate jdbcTemplate;
    
    @Value("${database.sql.databasename}")
    private String sql;

    public BillingDAO(@Qualifier("billingDataSource")DataSource datasource) {
        jdbcTemplate = new JdbcTemplate(datasource);
    }

    @Override
    public DatabaseInfo getDatabaseInfo() {
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<DatabaseInfo>(DatabaseInfo.class));
    }
}
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.tech.multipledatasources.domain.DatabaseInfo;

@Repository
public class CustomerDAO implements IDatabaseDAO {

    private JdbcTemplate jdbcTemplate;
    
    @Value("${database.sql.databasename}")
    private String sql;

    public CustomerDAO(@Qualifier("customerDataSource") DataSource datasource) {
        jdbcTemplate = new JdbcTemplate(datasource);
    }

    @Override
    public DatabaseInfo getDatabaseInfo() {
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<DatabaseInfo>(DatabaseInfo.class));
    }
}

對(duì)于 ?database.sql.databasename? 變量,請(qǐng)?jiān)??application.properties? 文件中添加?“SELECT dbid, name FROM v$database”?值。請(qǐng)注意,此 ?SQL ?也適用于 Oracle 數(shù)據(jù)庫(kù)。如果您打算使用不同的數(shù)據(jù)庫(kù),則需要更改此語(yǔ)句。

我使用構(gòu)造函數(shù)級(jí)別的依賴注入來(lái)注入數(shù)據(jù)源,并使用 Qualifier 注釋來(lái)指定 bean 名稱。

我還使用 ?DatabaseInfo ?模型類將 SQL 結(jié)果映射到一個(gè)對(duì)象中。

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class DatabaseInfo {

    private long dbid;
    private String name;
    
}

為了保持代碼干凈整潔,我使用了?Lombok?插件。

服務(wù)

我添加了兩個(gè)服務(wù)類作為服務(wù)層,如下所示,注釋為服務(wù),并注入相關(guān)的DAO類:

import com.tech.multipledatasources.dao.BillingDAO;
import com.tech.multipledatasources.domain.DatabaseInfo;
import org.springframework.stereotype.Service;

@Service
public class BillingService {
    
    private BillingDAO billingDAO;

    public BillingService(BillingDAO billingDAO){
        this.billingDAO = billingDAO;
    }

    public DatabaseInfo getDatabaseInfo(){
        return billingDAO.getDatabaseInfo();
    }
}
import com.tech.multipledatasources.dao.CustomerDAO;
import com.tech.multipledatasources.domain.DatabaseInfo;
import org.springframework.stereotype.Service;

@Service
public class CustomerService {
    
    private CustomerDAO customerDAO;

    public CustomerService(CustomerDAO customerDAO){
        this.customerDAO = customerDAO;
    }

    public DatabaseInfo getDatabaseInfo(){
        return customerDAO.getDatabaseInfo();
    }
}

異常處理

我在下面創(chuàng)建了 ?CustomException ?類,并在 ?ControllerAdvice ?中使用了中央異常處理:

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

import java.util.Date;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class CustomException extends Exception {
    
    private static final long serialVersionUID = 1L;
	private int errStatusCode;
    private String errMsg;
    private Date errDate;
    private String reqDesc; 
}
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

import java.util.Date;

public class ControllerExceptionHandler {
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<CustomException> genericExceptionHandler(Exception ex, WebRequest request) {
        CustomException customException = new CustomException(
            HttpStatus.EXPECTATION_FAILED.value(),
            ex.getMessage(),
            new Date(),
            request.getDescription(false)
            );
        
        return new ResponseEntity<CustomException>(customException, HttpStatus.EXPECTATION_FAILED);
      }
}

控制器

我創(chuàng)建了兩個(gè)控制器。一種用于計(jì)費(fèi),一種用于客戶請(qǐng)求;將兩個(gè)服務(wù)類注入控制器。

import com.tech.multipledatasources.domain.DatabaseInfo;
import com.tech.multipledatasources.service.BillingService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/billing")
public class BillingController {
    
    private BillingService billingService;

    public BillingController(BillingService billingService){
        this.billingService = billingService;
    }

    @GetMapping("/dsinfo")
    public ResponseEntity<DatabaseInfo> getDatasourceInfo(){
        return ResponseEntity.ok(billingService.getDatabaseInfo());
    }
}
import com.tech.multipledatasources.domain.DatabaseInfo;
import com.tech.multipledatasources.service.CustomerService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/customer")
public class CustomerController {
    
    private CustomerService customerService;

    public CustomerController(CustomerService customerService){
        this.customerService = customerService;
    }
    
    @GetMapping("/dsinfo")
    public ResponseEntity<DatabaseInfo> getDatasourceInfo(){
        return ResponseEntity.ok(customerService.getDatabaseInfo());
    }
}

運(yùn)行應(yīng)用程序

運(yùn)行?應(yīng)用程序?和?請(qǐng)求?后,它將返回結(jié)果,該結(jié)果將指示我們剛剛在連接中創(chuàng)建的數(shù)據(jù)庫(kù)。


關(guān)于Spring boot應(yīng)用程序中如何使用多個(gè)數(shù)據(jù)源的內(nèi)容就介紹到此結(jié)束了,感謝各位讀者的閱讀。

0 人點(diǎn)贊