我有一个Spring Boot项目,其中包含多个不同年份的数据库,这些数据库具有相同的表,因此唯一的区别是年份(…,DB2016,DB2017)。在应用程序的控制器中,我需要返回属于“不同”年份的数据。此外,在未来几年中还将创建其他数据库(例如,在2018年将有一个名为“ DB2018”的数据库)。因此,我的问题是如何在数据库之间切换连接而又不每隔一年创建一个新的数据源和一个新的存储库。在我发布的另一个问题中SpringBoot-不同数据库的相同存储库和相同实体答案是为每个现有数据库创建不同的数据源和不同的存储库,但是在这种情况下,我想根据当前年份从现有数据库返回数据。进一步来说:
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
它是如何工作的,基本上是Map<Object,DataSource>在创建您的@Configuration类时注册了一个内部类AbstractRoutingDataSource @Bean,在这种情况下,查找关键字将是年份。
Map<Object,DataSource>
@Bean
然后,您需要创建一个实现AbstractRoutingDataSource的类并实现该determineCurrentLookupKey()方法。每当进行数据库调用时,都会在当前上下文中调用此方法以查找DataSource应返回的查询。在您的情况下,听起来好像您只是想@PathVariable在URL中使用年,然后在实现中将其从URL中determineCurrentLookupKey()获取@PathVariable(例如,在您的控制器中,您具有@GetMapping("/{year}/foo/bar/baz"))。
determineCurrentLookupKey()
DataSource
@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");
我为一种产品编写了测试工具时使用了这种方法,该产品中有许多实例在多个不同的服务器上运行,并且我希望从@Controllers获得一个统一的编程模型,但仍然希望它能够为服务器/部署组合中的正确数据库提供正确的数据库。网址。像魅力一样工作。
@Controller
如果您使用的是Hibernate,则缺点是所有连接都将通过一个SessionFactory,这意味着您无法充分利用Hibernate的第二级缓存,但我猜这取决于您的需求。