C3P0连接池无法切换数据库:多企业共享Web应用中登出后销毁连接的配置咨询
解决C3P0连接池切换数据库的问题
首先要明确一个关键:C3P0的ComboPooledDataSource实例初始化后,其JDBC URL是不可动态修改的。你之前直接修改已初始化数据源的jdbcUrl是无效的,因为连接池已经根据初始URL创建了连接池中的连接,后续修改不会让连接池重新创建连接,所以才会出现切换code后仍使用旧数据库连接的问题。
要实现用户登出后切换数据库的功能,正确的思路是为每个独立数据库维护专属的连接池实例,并在用户登出时销毁当前使用的连接池。具体方案如下:
1. 用线程安全的Map管理连接池实例
我们可以用ConcurrentHashMap来存储不同数据库对应的连接池,key为数据库名称(也就是你获取的get_db值),这样同一个数据库重复登录时可以复用连接池,避免重复创建;不同数据库则使用各自独立的连接池。
// 全局维护连接池映射,使用ConcurrentHashMap保证线程安全 private static final Map<String, ComboPooledDataSource> DB_POOL_MAP = new ConcurrentHashMap<>();
2. 用户登录时创建/复用连接池
当用户输入code验证通过并获取到目标数据库名称get_db后,先处理当前可能存在的旧连接池(如果用户是切换账号登录),再创建或复用新的连接池:
// 从Session中获取当前用户正在使用的数据库(如果有) String currentDb = (String) request.getSession().getAttribute("currentDb"); // 若存在旧连接池,先销毁并移除 if (currentDb != null && DB_POOL_MAP.containsKey(currentDb)) { ComboPooledDataSource oldPool = DB_POOL_MAP.get(currentDb); oldPool.close(); // 销毁该连接池的所有连接 DB_POOL_MAP.remove(currentDb); } // 检查目标数据库的连接池是否已存在,不存在则创建 if (!DB_POOL_MAP.containsKey(get_db)) { ComboPooledDataSource newPool = new ComboPooledDataSource(); // 配置新连接池 newPool.setDriverClass("com.mysql.cj.jdbc.Driver"); newPool.setJdbcUrl("jdbc:mysql://host.com/" + get_db + "?autoReconnect=true&useUnicode=yes"); newPool.setUser("username"); newPool.setPassword("password"); newPool.setMinPoolSize(2); newPool.setMaxPoolSize(3000); newPool.setAcquireIncrement(1); newPool.setMaxIdleTime(1800); newPool.setMaxStatements(0); newPool.setIdleConnectionTestPeriod(5); newPool.setBreakAfterAcquireFailure(false); newPool.setUnreturnedConnectionTimeout(5); DB_POOL_MAP.put(get_db, newPool); } // 将当前数据库名称存入Session,方便登出时处理 request.getSession().setAttribute("currentDb", get_db);
3. 用户登出时销毁当前连接池
在登出逻辑中,从Session获取当前使用的数据库名称,找到对应的连接池并销毁:
String currentDb = (String) request.getSession().getAttribute("currentDb"); if (currentDb != null && DB_POOL_MAP.containsKey(currentDb)) { ComboPooledDataSource pool = DB_POOL_MAP.get(currentDb); pool.close(); // 彻底销毁连接池的所有连接 DB_POOL_MAP.remove(currentDb); } // 销毁Session request.getSession().invalidate();
额外安全提示:修复SQL注入风险
你当前获取数据库名称的代码存在严重的SQL注入漏洞:
String sql="select db from checker where code='"+entry_code+"'";
攻击者可以通过构造恶意的entry_code值来执行任意SQL语句,比如' OR 1=1 --。请立即修改为参数化查询:
String sql="select db from checker where code=?"; pst=con.prepareStatement(sql); pst.setString(1, entry_code); // 绑定参数 rs=pst.executeQuery();
这样就能彻底解决连接池无法切换数据库的问题,同时提升应用的安全性。
内容的提问来源于stack exchange,提问作者roddy cool
相关产品推荐
相关产品推荐

