我正在开发一款运行良好的网络应用。但最近从管理层的要求,我们需要为多个团队提供我们的网络应用程序。所以我们决定为每个团队创建一个单独的数据库模式副本。
我们的团队使用TEAM_A架构&其他团队使用TEAM_B架构。两个数据库模式完全相同,只有差异就是数据。
所以我在我的应用程序中使用了AbstractRoutingDataSource,如下所示:
package com.company.app.utils.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return TeamContextHolder.getTeamType();
}
}
TeamContextHolder为:
package com.company.app.utils.datasource;
public class TeamContextHolder{
public static final String TEAM_A_DATA_SOURCE = "aTeamDataSource";
public static final String TEAM_B_DATA_SOURCE = "bTeamDataSource";
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static void setTeamType(String teamType) {
contextHolder.set(teamType);
}
public static String getTeamType() {
return contextHolder.get();
}
public static void clearTeamType() {
contextHolder.remove();
}
}
Spring配置文件具有以下数据源的bean定义:
<bean id="aTeamDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" >
<property name="url" value="jdbc:mysql://localhost.com:3306/TEAM_A?autoReconnect=true&verifyServerCertificate=false&useSSL=true" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="username" value="welcome" />
<property name="password" value="welcome" />
</bean>
<bean id="bTeamDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" >
<property name="url" value="jdbc:mysql://localhost.com:3306/TEAM_B?autoReconnect=true&verifyServerCertificate=false&useSSL=true" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="username" value="welcome" />
<property name="password" value="welcome" />
</bean>
<bean primary="true" id="dynamicDataSource" class="com.company.app.utils.datasource.DynamicDataSource" >
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="aTeamDataSource" key="aTeamDataSource"></entry>
<entry value-ref="bTeamDataSource" key="bTeamDataSource"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="aTeamDataSource" >
</property>
</bean>
我的家庭主管:
package com.company.app.web;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.Map;
@Controller
@SessionAttributes("team")
public class Home {
/**
* Logger
*/
private static final Logger logger = Logger.getLogger(Home.class);
@RequestMapping(value="/home")
public ModelAndView mainHomePage() {
logger.info("Landing on main home page");
Map<String, Object> model = new HashMap<>();
return new ModelAndView("/home", "model", model);
}
@RequestMapping(value="/setteam")
public ModelAndView rememberTeam(@RequestParam String product) {
logger.info("Setting product info: " + product);
Map<String, Object> model = new HashMap<>();
model.put("team", team);
return new ModelAndView("/home", "model", model);
}
}
从家庭控制器,我通过JSP文件将团队会话变量传递给其他控制器。
我的home.jsp有以下形式:
<form action="./setproduct" method="POST" class="p-a-4">
<fieldset class="page-signup-form-group form-group form-group-lg">
<select class="page-signup-form-control form-control" id="grid-input-lg-2" name="product">
<option value="Not selected">Not selected</option>
<option value="TEAM_A">TEAM_A</option>
<option value="TEAM_B">TEAM_B</option>
</select>
</fieldset>
<button type="submit" class="btn btn-block btn-lg btn-primary m-t-3">Submit</button>
</form>
然后最后我将数据源设置为在我的DaoImpl类中使用:
if (filters.getTeam().equalsIgnoreCase("TEAM_B")){
TeamContextHolder.setTeamType(TeamContextHolder.TEAM_B_DATA_SOURCE);
}
按照我的想法,它按预期工作。但是随着TEAM_B数据源的选择一直在生产中爆炸。
所以我的问题:
答案 0 :(得分:2)
我通过将contextHolder从ThreadLocal更改为HttpSession来解决了这个问题,这样我就可以连接到团队的数据库架构进行整个浏览器会话。此外,我现在不必将会话变量传递给其他控制器,也不需要像我之前那样在我的DaoImpl类中设置数据源。所以现在我更新的TeamContextHolder看起来像这样:
package com.company.app.utils.datasource;
import org.apache.log4j.Logger;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpSession;
public class TeamContextHolder {
private static final Logger logger = Logger.getLogger(TeamContextHolder.class);
public static HttpSession getCurrentSession() {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getRequest().getSession(true);
}
public static void setTeamType(String teamType) {
TeamContextHolder.getCurrentSession().setAttribute("team", teamType);
}
public static String getTeamType() {
logger.info("Session attribute product: " + TeamContextHolder.getCurrentSession().getAttribute("team"));
return (String) TeamContextHolder.getCurrentSession().getAttribute("team");
}
}
会话变量从家庭控制器设置为:
package com.company.app.web;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@Controller
@SessionAttributes("team")
public class HomeController {
private static final Logger logger = Logger.getLogger(HomeController.class);
@RequestMapping(value="/home")
public ModelAndView home() {
logger.info("Landing on main home page");
Map<String, Object> model = new HashMap<>();
return new ModelAndView("/home", "model", model);
}
@RequestMapping(value="/setteam")
public ModelAndView setteam(@RequestParam String team,
HttpServletRequest request) {
// Setting session attribute:
request.getSession().setAttribute("team", team);
logger.info("Setting team: " + team);
Map<String, Object> model = new HashMap<>();
model.put("team", team);
return new ModelAndView("/home", "model", model);
}
}
答案 1 :(得分:0)
您可以有多个实体管理器,它们将扫描不同的存储库集,并且您可以根据Cookie来切换这些实体管理器。