Java AbstractRoutingDataSource运行时更改映射
我现在在数据库中有2个表:Java AbstractRoutingDataSource运行时更改映射,java,spring,spring-security,Java,Spring,Spring Security,我现在在数据库中有2个表: 使用者 用户数据库 在用户I中存储登录名、密码、角色 在user_数据库中,我存储数据库驱动程序、url、密码和用户。 图表数据库 我希望用户登录到我的页面,下一个连接他所做的将被发送到用户数据库。为什么我需要什么?我正在规划流行的电子商务地图,并创建android应用程序,用户可以登录并查看商店数据,添加和查看产品订单。 现在是时候去实践了,我在spring技术方面的知识不多,请解释一下我做错了什么。 web上所有关于AbstractRoutingDataSo
在user_数据库中,我存储数据库驱动程序、url、密码和用户。 图表数据库 我希望用户登录到我的页面,下一个连接他所做的将被发送到用户数据库。为什么我需要什么?我正在规划流行的电子商务地图,并创建android应用程序,用户可以登录并查看商店数据,添加和查看产品订单。
现在是时候去实践了,我在spring技术方面的知识不多,请解释一下我做错了什么。 web上所有关于
AbstractRoutingDataSource
的示例都在持久性文件中声明了datasource,或者创建DataSourcebean并开始使用AbstractRoutingDataSource
。
在我的项目中,我现在不需要用户连接,我需要从数据库中获取它。我正在尝试使用存储库和这个示例获取
但我在控制器中的@Autowired
上得到null,我认为存储库的连接为null。如何为此存储库设置连接并设置路由?此方法的缺陷是,当我添加用户时,我需要重新启动服务器以刷新连接。
下一步尝试我现在使用的是class
User
implementUserDetails
在用户登录后,我可以从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
}
这是可行的,但当我刷新3-4次用户数据库请求时,我得到了
User 3450_Menadzer already has more than 'max_user_connections' active connections
手动设置连接映射,不刷新每个方法determineCurrentLookupKey
run我没有这个问题。我认为我的方法不是封闭连接。我怎样才能清洗这个?这是否可能是路由连接的更好方法
编辑
@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);
}
}
首先,每用户数据库是一种非常罕见的设计。如果所有这些数据库都以相同的结构结束,请不要在实际应用程序中这样做,而只需在表和查询中添加用户id即可 接下来,我在我的文章中找到了另一个动态AbstractRoutingDataSource(不完整)示例 我的代码(注意从未测试过)和您的问题之间的一个巨大区别是,我使用SessionListener关闭数据库,以避免打开的数据库数量无限增长 如果您想学习Spring,可以尝试以下模式(自下而上的描述):
- 一个会话范围的bean,它将为用户保存实际的数据库连接,该连接应该在第一次请求时创建(以确保会话中存在用户id)并缓存以供后续使用。销毁方法(会话关闭时由Spring自动调用)应该关闭连接
- 一个
,将向上述持有者注入一个代理,并向持有者请求实际的数据源AbstractRoutingDataSource
与另一个答案一样,如果同一用户可能同时拥有多个会话,则可以在会话持有者中注入一个单例,该会话持有者将保留实际的数据库连接以及活动会话的数量。这样,每个用户都可以获得一个连接,无论他可以有多少个并发会话。我看到了你的老例子,我认为这是可能的,但你需要我解释一下
sessionDestroyed
方法在类中实现了什么HttpSessionListener
?您在哪里执行AbstractRoutingDataSource这是组件?添加连接数据库是在servlet上下文或类中?@seti:我稍微修改了我的旧示例。其思想是map userId->dataSource是一个注入AbstractRoutingDataSource实现中的单例bean,注入HttpSessionListener中,它将关闭数据源并将其从映射中删除。您可以找到一个成功登录事件的侦听器示例,它可以将数据库存储在mapi中编辑我的问题您的逻辑帮助了我。你能解释一下我的旧代码和新代码有什么不同吗(这是可行的)。在旧代码中,我创建数据源映射集并刷新,在新代码中,我从@Autowired
类集合中获取数据源并刷新。你能分享你的代码吗?我有同样的任务后,用户登录,连接到一个基本的数据库和获得数据源,然后CRUD塔特数据源!但我对Spring框架非常陌生,我尝试了一个月,但没有什么好结果。我希望你的代码能帮助我。
@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);
}
}
@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());
}
}