Spring 启动 - 动态更改连接
Spring Boot - Change connection dynamically
我有一个 Spring 引导项目,其中包含多个不同年份的数据库,这些数据库具有相同的表,因此唯一的区别是年份(...,DB2016、DB2017)。在应用程序的控制器中,我需要 return 属于 "different" 年的数据。此外,在未来几年将创建其他数据库(例如,在 2018 年将有一个名为 "DB2018" 的数据库)。所以我的问题是如何在不每年创建新数据源和新存储库的情况下切换数据库之间的连接。
在我发布的另一个问题 () 中,答案是为每个现有数据库创建不同的数据源和不同的存储库,但在这种情况下,我想 return 基于现有数据库的数据今年。更具体地说:
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;
希望一切都清楚
这里最适合您需要的可能是Spring的AbstractRoutingDataSource
。您确实需要定义多个数据源,但您只需要一个存储库。多个数据源在这里不是问题,因为总有一种方法可以在 运行 时间以编程方式创建 DataSource bean 并将它们注册到应用程序上下文。
它是如何工作的,基本上是在创建 AbstractRoutingDataSource
@Bean
时在 @Configuration class 中注册一个 Map<Object, DataSource>
,在这种情况下,查找键将是年。
然后您需要创建一个 class 实现 AbstractRoutingDataSource 并实现 determineCurrentLookupKey()
方法。任何时候进行数据库调用时,都会在当前上下文中调用此方法以查找应返回的 DataSource
。在您的情况下,听起来您只是想将年份作为 URL 中的 @PathVariable
,然后作为 determineCurrentLookupKey()
的实现从 [=] 中获取 @PathVariable
36=](例如,在您的控制器中,您有 @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
的统一编程模型,但仍然想要它会为 url 中的 server/deployment 组合命中正确的数据库。工作得很好。
如果您使用 Hibernate,缺点是所有连接都将通过单个 SessionFactory,这意味着您无法利用 Hibernate 的二级缓存,据我所知,但我想这取决于您的需要。
我有一个 Spring 引导项目,其中包含多个不同年份的数据库,这些数据库具有相同的表,因此唯一的区别是年份(...,DB2016、DB2017)。在应用程序的控制器中,我需要 return 属于 "different" 年的数据。此外,在未来几年将创建其他数据库(例如,在 2018 年将有一个名为 "DB2018" 的数据库)。所以我的问题是如何在不每年创建新数据源和新存储库的情况下切换数据库之间的连接。
在我发布的另一个问题 (
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;
希望一切都清楚
这里最适合您需要的可能是Spring的AbstractRoutingDataSource
。您确实需要定义多个数据源,但您只需要一个存储库。多个数据源在这里不是问题,因为总有一种方法可以在 运行 时间以编程方式创建 DataSource bean 并将它们注册到应用程序上下文。
它是如何工作的,基本上是在创建 AbstractRoutingDataSource
@Bean
时在 @Configuration class 中注册一个 Map<Object, DataSource>
,在这种情况下,查找键将是年。
然后您需要创建一个 class 实现 AbstractRoutingDataSource 并实现 determineCurrentLookupKey()
方法。任何时候进行数据库调用时,都会在当前上下文中调用此方法以查找应返回的 DataSource
。在您的情况下,听起来您只是想将年份作为 URL 中的 @PathVariable
,然后作为 determineCurrentLookupKey()
的实现从 [=] 中获取 @PathVariable
36=](例如,在您的控制器中,您有 @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
的统一编程模型,但仍然想要它会为 url 中的 server/deployment 组合命中正确的数据库。工作得很好。
如果您使用 Hibernate,缺点是所有连接都将通过单个 SessionFactory,这意味着您无法利用 Hibernate 的二级缓存,据我所知,但我想这取决于您的需要。