Reputation: 24527
We currently have an application which uses multiple databases with the same schema. At the moment we're using a custom solution for switching between them based on the user's session. This works via
public final class DataSourceProxy extends BasicDataSource {
...
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.getDetails() instanceof Map) {
Map<String, String> details = (Map<String, String>) auth.getDetails();
String targetUrl = details.get("database");
Connection c = super.getConnection();
Statement s = c.createStatement();
s.execute("USE " + targetUrl + ";");
s.close();
return c;
} else {
return super.getConnection();
}
}
Now we want to build a solution using AbstractRoutingDataSource
. The problem is:
@Component
public class CustomRoutingDataSource extends AbstractRoutingDataSource {
@Autowired
Environment env;
@Autowired
DbDetailsRepositoy repo;
public CustomRoutingDataSource() {
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
for(DBDetails dbd : repo.findAll() {
// create DataSource and put it into the map
}
setTargetDataSources(new HashMap<Object, Object>());
}
@Override
protected Object determineCurrentLookupKey() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.getDetails() instanceof Map) {
Map<String, String> details = (Map<String, String>) auth.getDetails();
return details.get("database");
}
return null;
}
}
Inside the constructor (or even via @PostConstruct
) we have to fill the targetDataSources Map
. But(!) for this we need the connection details which are stored in another database, which has its own DataSource and Entity Manager.
It seems like Spring can't determine the order of Bean construction, or maybe I'm just missing something. It always gives a NullPointerException
when accessing the repository (which btw is a JpaRepository
).
We're using Spring 3.2.3, Spring Data, Hibernate 4.2. Complete Annotation and Java-Code configuration of Spring and Spring Security.
Please help us!
Upvotes: 1
Views: 1748
Reputation: 83081
Spring of course has to call the constructor before it can populate the properties. But that's not a Spring thing, that's basic Java 101 and one of the plenty downsides of using field injection.
To avoid this, simply add your dependencies to the constructor:
@Component
class CustomRoutingDataSource extends AbstractRoutingDataSource {
@Autowired
public CustomRoutingDataSource(DbDetailsRepository repo, Environment environment) {
…
}
…
}
Upvotes: 1