AbstractRoutingDataSource在运行时更改映射

时间:2015-05-16 08:41:58

标签: java spring spring-security

我现在在数据库中有2个表:

  1. 用户
  2. user_database
  3. 在用户中我存储登录名,密码,角色
    在user_database中,我存储数据库驱动程序,URL,密码和用户。 图数据库 enter image description here

    我希望用户登录我的页面,下一次连接他所做的将被发送到用户数据库。为什么我需要什么?我刨图流行电子商务并创建用户登录和查看商店数据的Android应用程序,可以添加和查看产品订单。
    现在是时候去练习了,我对弹簧技术的知识很小,请在我做错的时候向我解释一下。 web AbstractRoutingDataSource上的所有示例都在持久性文件中创建声明数据源或创建数据源bean并开始使用AbstractRoutingDataSource。 在我的项目中,我现在没有用户连接,我需要从数据库中获取此信息。我试着使用存储库和这个例子 https://stackoverflow.com/a/17575648/3037869 但我在控制器的@Autowired上得到null,我认为存储库的连接是空的。如何为此存储库设置连接并设置Route?缺陷此方法是当我添加用户时我需要重启服务器来刷新连接。
    接下来尝试我现在​​使用的是UserUserDetails用户登录后我可以从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
    
    }
    

    我在constuctor和determineCurrentLookupKey

    上运行此方法
    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() beacuse数据源不刷新。我做了一些刷新,我没有得到错误,我认为这是有效的。我需要在将来为更多数据库测试这个

    @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());
        }
    
    }
    

1 个答案:

答案 0 :(得分:2)

首先,每个用户数据库是一种非常罕见的设计。如果所有这些数据库都以相同的结构结束,请不要在真实世界的应用程序中执行此操作,而只需在表和查询中添加user_id。

接下来,我在我的another answer中找到了动态AbstractRoutingDataSource的另一个(不完整)示例。

我的代码(谨防从未测试过)与您的问题之间的一个重大区别是我使用SessionListener来关闭数据库,以避免开放数据库的数量无法正常增长。

如果您要学习Spring,可以尝试以下模式(自下而上的描述):

  • 会话范围的bean,它将保存用户的实际数据库连接,应该在第一次请求时创建连接(以确保会话中存在用户ID)并缓存以供后续使用。一个destroy方法(当会话关闭时由Spring自动调用)应该关闭连接。
  • 一个AbstractRoutingDataSource,将向代理注入上方持有者,并将实际数据源提供给持有者

与另一个答案一样,如果同一个用户可能同时拥有多个会话,那么您可以在会话持有者中注入一个单例,以保持实际数据库连接以及活动会话数。这样,无论他有多少并发会话,每个用户都可以获得一个连接。