首页 文章

Spring Boot - 动态更改连接

提问于
浏览
3

我有一个Spring Boot项目,有多个不同年份的数据库,这些数据库有相同的表,所以唯一的区别是年份(...,DB2016,DB2017) . 在应用程序的控制器中,我需要返回属于"different"年的数据 . 此外,在未来几年将创建其他数据库(例如,在2018年,将有一个名为"DB2018"的数据库) . 所以我的问题是如何在每年新建一个新数据源和新存储库的情况下切换数据库之间的连接 . 在我发布的另一个问题(Spring Boot - Same repository and same entity for different databases)中,答案是为每个现有数据库创建不同的数据源和不同的存储库,但在这种情况下,我想根据当前年份从现有数据库返回数据 . 进一步来说:

SomeEntity.java

@Entity(name = "SOMETABLE")
public class SomeEntity implements Serializable {
@Id
@Column(name="ID", nullable=false)
private Integer id;

@Column(name="NAME")
private String name;
}

SomeRepository.java

public interface SomeRepository extends PagingAndSortingRepository<SomeEntity, Integer> {
@Query(nativeQuery= true, value = "SELECT * FROM SOMETABLE WHERE NAME = ?1")
List<SomeEntity> findByName(String name);
}

SomeController.java

@RequestMapping(value="/foo/{name}", method=RequestMethod.GET)
public ResponseEntity<List<SomeEntity>> findByName(@PathVariable("name") String name) {
List<SomeEntity> list = autowiredRepo.findByName(name);
return new ResponseEntity<List<SomeEntity>>(list,HttpStatus.OK);

}

application.properties

spring.datasource.url=jdbc:postgresql://localhost:5432/DB
spring.datasource.username=xxx
spring.datasource.password=xxx

因此,如果当前年份是2017年,我想要这样的事情:

int currentyear = Calendar.getInstance().get(Calendar.YEAR);
int oldestDbYear = 2014;
List<SomeEntity> listToReturn = new LinkedList<SomeEntity>();
//the method getProperties is a custom method to get properties from a file
String url = getProperties("application.properties", "spring.datasource.url");
props.setProperty("user", getProperties("application.properties","spring.datasource.username"));
props.setProperty("password", getProperties("application.properties","spring.datasource.password"));
for (int i = currentYear, i>oldestDbYear, i--) {
//this is the connection that must be used by autowiredRepo Repository, but i don't know how to do this.
//So the repository uses different connection for every year.
Connection conn = getConnection(url+year,props);
List<SomeEntity> list_of_specific_year = autowiredRepo.findByName(name);
conn.close;
listToReturn.addAll(list_of_specific_year);
}
return listToReturn;

希望永远清楚

1 回答

  • 2

    可能最适合您需求的是Spring的 AbstractRoutingDataSource . 您确实需要定义多个DataSource,但您只需要一个存储库 . 多个数据源在这里不是问题,因为总有一种方法可以在运行时以编程方式创建DataSource bean并将它们与应用程序上下文一起注册 .

    它的工作原理是你在创建 AbstractRoutingDataSource @Bean 时基本上在@Configuration类中注册一个 Map<Object, DataSource> ,在这种情况下,查找键将是年份 .

    然后,您需要创建一个实现AbstractRoutingDataSource的类并实现 determineCurrentLookupKey() 方法 . 无论何时进行数据库调用,都会在当前上下文中调用此方法以查找应返回的 DataSource . 在你的情况下,听起来你只是希望将年份作为 @PathVariable 在URL中,然后随着 determineCurrentLookupKey() 的实现从URL中抓取 @PathVariable (例如在你的控制器中你有像 @GetMapping("/{year}/foo/bar/baz") 这样的映射) .

    HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder
                .getRequestAttributes()).getRequest();
    
        HashMap templateVariables =
     (HashMap)request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
    
        return templateVariables.get("year");
    

    我为一个产品编写测试工具时使用了这种方法,其中有许多实例在多个不同的服务器上运行,我想从我的 @Controller 中获得统一的编程模型,但仍希望它能够在服务器/部署组合中使用正确的数据库 . 网址 . 工作就像一个魅力 .

    如果你使用Hibernate的缺点是所有连接都将通过一个SessionFactory,这意味着你不能利用Hibernate的二级缓存,因为我理解它,但我想这取决于你的需求 .

相关问题