2017-10-25 16 views
3

Ich habe ein Spring Boot-Projekt mit mehreren Datenbanken aus verschiedenen Jahren und diese Datenbanken haben die gleichen Tabellen, so dass der einzige Unterschied ist das Jahr (..., DB2016, DB2017). Im Controller der Anwendung muss ich Daten zurückgeben, die zu "unterschiedlichen" Jahren gehören. Außerdem werden in den nächsten Jahren weitere Datenbanken erstellt (zB 2018 wird es eine Datenbank namens "DB2018" geben). Mein Problem ist also, wie man die Verbindung zwischen den Datenbanken wechselt, ohne jedes Jahr eine neue Datenquelle und ein neues Repository zu erstellen. In einer anderen Frage von mir (Spring Boot - Same repository and same entity for different databases) war die Antwort zu verschiedenen Datenquellen und verschiedenen Repositories für jede vorhandene Datenbank zu erstellen, aber in diesem Fall möchte ich Daten aus vorhandenen Datenbanken auf der Grundlage des aktuellen Jahres zurückgeben. Insbesondere:Spring Boot - Ändern der Verbindung dynamisch

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 
So

wenn das aktuelle Jahr 2017 ist möchte ich etwas wie folgt aus:

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; 

Hoffnung everithing ist klar

Antwort

1

Die Sache, die hier wohl am besten geeignet, um Ihre Bedürfnisse ist Spring AbstractRoutingDataSource. Sie müssen mehrere DataSources definieren, aber Sie benötigen nur ein einzelnes Repository. Mehrere Datenquellen sind hier kein Problem, da es immer eine Möglichkeit gibt, die DataSource-Beans zur Laufzeit programmatisch zu erstellen und sie im Anwendungskontext zu registrieren.

Wie es funktioniert, ist man im Grunde ein Map<Object, DataSource> in Ihrer @Configuration Klasse registrieren, wenn Ihre AbstractRoutingDataSource@Bean und in diesem Fall wird der Suchschlüssel wäre das Jahr zu schaffen.

Dann müssen Sie eine Klasse erstellen, die AbstractRoutingDataSource implementiert und die Methode determineCurrentLookupKey() implementiert. Jedes Mal, wenn ein Datenbankaufruf durchgeführt wird, wird diese Methode im aktuellen Kontext zum Nachschlagen aufgerufen, der DataSource zurückgegeben werden soll. In Ihrem Fall klingt es so, als wollten Sie einfach das Jahr als @PathVariable in der URL haben und dann als Implementierung von determineCurrentLookupKey() die @PathVariable aus der URL holen (z. B. in Ihrem Controller haben Sie Mappings wie @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"); 

benutzen ich diesen Ansatz, wenn ein Test-Tool für ein Produkt zu schreiben, wo es auf mehreren verschiedenen Servern laufen viele Fälle waren und ich wollte wollte ein einheitliches Programmiermodell von meinem @Controller s aber immer noch die richtigen Datenbank zu schlagen für die Server/Deployment-Kombination in der URL. Lief wie am Schnürchen.

Der Nachteil, wenn Sie Hibernate verwenden, ist, dass alle Verbindungen eine einzige SessionFactory durchlaufen, was bedeutet, dass Sie die Vorteile von Hibernate's Caching auf der zweiten Ebene nicht nutzen können, aber ich denke, das hängt von Ihren Bedürfnissen ab.

+0

Vielen Dank! In diesen Tagen fand ich einen Workaround, aber meine Lösung folgt nicht dem Prinzip des Autowiderlaufs, also werde ich mit Ihrer Lösung versuchen – Mark

Verwandte Themen