Несколько динамических источников данных для контекста сервлета
Вопрос
Я разрабатываю веб-приложение Java-сервлета, которое управляет информацией из нескольких баз данных (все структурно одинаковы), каждая из которых соответствует отдельному «бизнесу».Пользователь выбирает «текущий бизнес», который хранится в сеансе, и приложение может отображать или изменять этот «текущий бизнес».
Я хотел бы использовать ресурсы tomcat динамически, чтобы иметь доступ к этим предприятиям с помощью jndi.Таким образом, я могу использовать теги jstl sql или контекстный поиск в сервлетах.Я не могу определить каждый ресурс в файле web.xml, поскольку они хранятся в таблице SQL.Конечным результатом будет возможность написать простой jsp, содержащий такие строки:
<%@ taglib uri="http://java.sun.com/jstl/sql" prefix="sql" %>
<sql:query var = "users" dataSource="sources/${sessionScope.currentBusiness}">
select id, firstName, lastName FROM user
</sql:query>
или сервлеты, которые могут иметь такие строки
String request.getSession().getAttribute("currentBusiness");
Context initial = new InitialContext();
Context context = (Context) initial.lookup("java:comp/env");
DataSource source = (DataSource) context.lookup("sources/" + currentBusiness);
где я могу получить правильный источник данных для «текущего бизнеса».
Я экспериментировал с написанием собственных ObjectFactory, производных от javax.naming.spi.ObjectFactory, но безуспешно.Есть какие-нибудь подсказки, как это легко сделать?
Решение
В конце концов я остановился на следующем решении, состоящем из SessionListener и сервлета, которые работают следующим образом.SessionListener имеет следующую форму:
public class SessionListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent event) {
HttpSession session = event.getSession();
// get list of possible data sources available to this session
List<DataSource> sources = new ArrayList<DataSource>();
... code to get the available sources
// get the current data source
DataSource source = null;
... code to get the current source
source = sources.get(0); // for example
// setup the session attributes
session.setAttribute("availableSources", sources);
session.setAttribute("currentSource", source);
}
}
Каждый раз, когда пользователь входит в систему и создается сеанс, в сеанс помещается список доступных источников данных и текущий.Это делается на уровне сеанса, поскольку источники данных зависят от входа пользователя в систему.Теперь к ним можно получить доступ из приложения.Чтобы изменить текущий источник данных, я создал сервлет с этой упрощенной версией:
public abstract class BoxletServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession(true);
String s = request.getParameter("source");
// based on 's' choose from the available DataSource
List<DataSource> sources = (List<DataSource>) session.getParameter("availableSources");
Source source = chooseFrom(sources, s);
session.setParameter("currentSource", source);
// forward to a page saying that the DataSource changed
}
}
Благодаря этой реализации теперь можно создавать следующие jsps:
<%@ taglib uri="http://java.sun.com/jstl/sql" prefix="sql" %>
<sql:query var = "users" dataSource="${sessionScope.currentSource}">
select id, firstName, lastName FROM user
</sql:query>
Надеюсь, это поможет кому-то еще.
Другие советы
Создайте источники данных в ServletContextListener и поместите их в Контекст сервлета.
Этот подход, безусловно, будет «работать», но идея отдельной, идентичной базы данных для каждого бизнеса мне кажется неправильной.Конечно, возможность очертить бизнес где-то в схеме кажется возможной.Для их такого разделения требуется новая база данных для каждого предприятия, тогда как для схемы потребуется только новый идентификатор предприятия.
Я бы также сказал, что любая возможность анализа данных между бизнесами будет потеряна, если вы не перенесете данные ETL из отдельных баз данных в многомерный куб для специальных отчетов и запросов.
А теги JSTL <sql> следует использовать только в самых простых веб-приложениях.Вы оставляете себя открытым для возможности атак с использованием SQL-инъекций, если отказываетесь от проверки на среднем уровне.
ОБНОВЛЯТЬ:
Вы должны объявить ресурсы в своем файле web.xml AFAIK, поэтому всякий раз, когда у вас появляется новая база данных, вам придется остановить приложение, настроить новый источник JNDI и перезапустить Tomcat.Я надеюсь, что вы кластеризованы, потому что на всех предыдущих клиентах приложение будет не работать каждый раз, когда вы добавляете новый бизнес/базу данных.