Spring MVC:多个数据源:根据UI

时间:2018-03-08 04:55:44

标签: spring spring-mvc spring-boot spring-data spring-jdbc

我正在开发一款运行良好的网络应用。但最近从管理层的要求,我们需要为多个团队提供我们的网络应用程序。所以我们决定为每个团队创建一个单独的数据库模式副本。

我们的团队使用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&amp;verifyServerCertificate=false&amp;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&amp;verifyServerCertificate=false&amp;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数据源的选择一直在生产中爆炸。

所以我的问题:

  1. 是否有可能在单个tomcat网络服务器上(我们部署了我们的网站) app)很少有用户可以选择TEAM_A并使用TEAM_A_DATA_SOURCE 少数用户选择TEAM_B并使用TEAM_B_DATA_SOURCE? 最初我认为这是可行的但是在这个kaboom生产之后 我不确定春天是否有可能。
  2. 如果不可能的话 我应该如何处理最终用户可以选择他们的团队的情况 并在同一个网络应用程序上同时处理两个不同的数据库架构。

2 个答案:

答案 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来切换这些实体管理器。