我现在在数据库中有2个表:
我在用户中存储登录名,密码,角色
在user_database中,我存储数据库驱动程序,URL,密码和用户。
图数据库
我希望用户登录到我的页面,然后将他所做的下一次连接发送到用户数据库。为什么我需要什么?我计划流行的电子商务地图,并创建android应用程序,以便用户登录并查看商店数据,可以添加和查看产品订单。
现在该练习了,我对弹簧技术的了解很少,当我做错事情时,请向我解释一些事情。
网络上所有
AbstractRoutingDataSource
的示例都在持久性文件中声明了数据源,或者创建了数据源bean并开始使用AbstractRoutingDataSource
。在我的项目中,我现在没有用户连接,我需要从数据库中获取此信息。我尝试使用存储库和此示例
https://stackoverflow.com/a/17575648/3037869
但是我在控制器中的
@Autowired
上获取了空值,我认为存储库的连接为空。如何为此存储库设置连接并设置路由?缺陷是,当我添加用户时,我需要重新启动服务器以刷新连接。接下来尝试我现在使用的是
User
类在用户登录后实现UserDetails
我可以从getPrincipal()
获得用户连接并添加到地图。private void setDataSources() {
HashMap<Object, Object> targetDataSources = new HashMap<>();
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:AZ;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
dataSourceBuilder.username("sa");
dataSourceBuilder.password("");
targetDataSources.put("auth", dataSourceBuilder.build());
setDefaultTargetDataSource(dataSourceBuilder.build());
if( SecurityContextHolder.getContext().getAuthentication()!=null) {
User user=(User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
System.out.println(user.getUserDatabase().getDriver());
dataSourceBuilder.driverClassName(user.getUserDatabase().getDriver());
dataSourceBuilder.url(user.getUserDatabase().getUrl());
dataSourceBuilder.username("3450_Menadzer");
dataSourceBuilder.password(user.getUserDatabase().getPassword());
targetDataSources.put("user", dataSourceBuilder.build());
}
setTargetDataSources(targetDataSources);
afterPropertiesSet(); //map is refresh when i add this
}
我在构造函数上运行此方法并确定CurrentLookupKey
protected Object determineCurrentLookupKey() {
if( SecurityContextHolder.getContext().getAuthentication()!=null) {
setDataSources();
return "user";
}
return "auth";
}
这是可行的,但是当我刷新3-4次对用户数据库的请求时,
User 3450_Menadzer already has more than 'max_user_connections' active connections
手动设置连接映射,而不刷新每个运行的
determineCurrentLookupKey
方法,我都没有这个问题。我认为我的方法不是关闭连接。我该如何清洁?这可能是更好的路由连接方法吗?编辑
@SergeBallesta我从您的示例中更改了一些代码
这是我的地图课
@Component
@Scope(value = "singleton")
public class DataSourceMap {
private Map<Object,Object> dataSourceMap;
public DataSourceMap()
{
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:AZ;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
dataSourceBuilder.username("sa");
dataSourceBuilder.password("");
dataSourceMap=new HashMap<Object,Object>();
dataSourceMap.put("auth",dataSourceBuilder.build());
}
public void addDataSource(String session,DataSource dataSource)
{
this.dataSourceMap.put(session,dataSource);
}
public Map<Object,Object> getDataSourceMap()
{
return dataSourceMap;
}
public void removeSource(String session)
{
dataSourceMap.remove(session);
}
}
对于
AbstractRoutingDataSource
,我做了一些更改,我添加了afterPropertiesSet()
,因为数据源未刷新。我做了一些刷新,但没有收到错误,我认为这是可行的。以后我需要测试更多数据库@Component
public class CustomRoutingDataSource extends AbstractRoutingDataSource{
@Autowired
DataSourceMap dataSources;
@Override
protected Object determineCurrentLookupKey() {
setDataSources(dataSources);
afterPropertiesSet();
System.out.println("test");
if( SecurityContextHolder.getContext().getAuthentication()!=null) {
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
return request.getSession().getId();
}
return "auth";
}
@Autowired
public void setDataSources(DataSourceMap dataSources) {
System.out.println("data adding");
setTargetDataSources(dataSources.getDataSourceMap());
}
}
最佳答案
首先,每个用户数据库是一个非常不常见的设计。如果所有这些数据库都以相同的结构结尾,请不要在实际应用程序中这样做,而只需在表和查询中添加user_id。
接下来,我在我的another answer中找到了一个动态AbstractRoutingDataSource的另一个(不是完整的)示例。
我的代码(请注意,未经测试)与您的问题之间的一大区别是,我使用SessionListener关闭数据库,以避免打开的数据库的数量不确定地增加。
如果您打算以此学习Spring,则可以尝试以下模式(自下而上的描述):
AbstractRoutingDataSource
,它将被注入到以上持有者的代理中,并且会向持有者询问实际的数据源与其他答案一样,如果同一用户可能同时进行多个会话,则可以在会话持有者中注入一个单例,以保持实际的数据库连接以及 Activity 会话的数量。这样,无论他有多少个并发会话,每个用户都将获得一个连接。