AbstractRoutingDataSource

AbstractRoutingDataSource

我现在在数据库中有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,则可以尝试以下模式(自下而上的描述):

  • 一个会话范围的Bean,它将保留用户的实际数据库连接,应在第一个请求(确保会话中存在用户ID)时创建该连接,并缓存以备后用。销毁方法(在会话关闭时由Spring自动调用)应该关闭连接。
  • 一个AbstractRoutingDataSource,它将被注入到以上持有者的代理中,并且会向持有者询问实际的数据源

  • 与其他答案一样,如果同一用户可能同时进行多个会话,则可以在会话持有者中注入一个单例,以保持实际的数据库连接以及 Activity 会话的数量。这样,无论他有多少个并发会话,每个用户都将获得一个连接。

    08-16 18:33