Spring + Hibernate: как динамически обрабатывать умноженные (неограниченные) источники данных и фабрики сеансов?

Наше приложение может иметь несколько баз данных. Базы данных могут быть разного типа (Oracle и MS SQL) для одного и того же экземпляра приложения. Структуры баз данных немного отличаются, но используется тот же набор Hibernate POJO. Список баз данных может быть определен только при фактическом запуске приложения (поэтому его нельзя сохранить в виде фиксированного XML).

Пользователь должен выбрать БД во время входа в систему. Выбранная БД хранится в HTTP-сессии.

Поэтому я хотел бы использовать автоматическую проводку Sping, чтобы Session Factory автоматически подключалась к моим DAO в веб-контроллерах в зависимости от текущего пользователя. Нравится:

@Repository
@Transactional
@Controller
public abstract class UserGroupDAO {

    @Autowired
    @Qualifier("sessionFactory")
    private SessionFactory sessionFactory;

    public List<Users> getListOfUsers() {
        Session session = sessionFactory.currentSession();
        return session.createCriteria(Users.class).list();
    }
}


@Controller
public class WebController {

    @Autowired
    @Qualifier("userGroupDAO")
    private UserGroupDAO userGroupDAO;  

    @RequestMapping("/greeting.html")
    public ModelAndView greeting(Model model) {
        userGroupDAO.getListOfUsers();
    }

Например: есть 2 БД - Oracle_DB и MS_SQL_DB и два пользователя A и B. Пользователь A вошел в Oracle_DB, пользователь B вошел в MS_SQL_DB. Когда пользователь A нажимает /greeting, он должен видеть список пользователей из Oracle_DB, когда пользователь B нажимает /greeting - из MS_SQL_DB.

Я просмотрел AbstractRoutingDatasource, но похоже, что он работает только для одной фабрики сеансов с несколькими источниками данных. Однако я ищу индивидуальную фабрику сеансов для каждого источника данных (и диспетчера транзакций тоже).

Заранее спасибо за помощь

Обзор проблемы

Базы данных генерируются динамически, поэтому нельзя жестко запрограммировать настройки базы данных в конфигурационный файл. Приложение Spring должно иметь возможность определять, какие базы данных доступны, а затем подключаться к ним.


person Logger    schedule 09.10.2014    source источник
comment
Можешь перефразировать свой пост в вопрос? Большинство людей пропускают вопросы и не отвечают, если не могут понять, о чем вы спрашиваете.   -  person JaneGoodall    schedule 09.10.2014
comment
То, что вы хотите, - это некоторая форма многопользовательской аренды. Решение, которое я использовал много лет назад для клиента, было задокументировано здесь. Код доступен на github, вас может заинтересовать проект multi-tenant.   -  person M. Deinum    schedule 09.10.2014


Ответы (1)


Решение было 1) Установите для UserGroupDAO, SessionFactory и т. д. область «сеанс».

@Repository
@Transactional
@Scope(value="session",proxyMode=ScopedProxyMode.TARGET_CLASS) 
public class UserGroupDAO extends GeneralDAO {

2) Инициализировать SessionFactory, Session и т. д. вручную в синглтоне (DataSourceProvider) и получить его из этого синглтона в ApplicationContextConfig

@Configuration
@ComponentScan(....)
@EnableTransactionManagement
public class ApplicationContextConfig {
    @Autowired
    @Bean("sessionFactory")
    @Scope(value="session")
    public SessionFactory getSessionFactory(DataSource dataSource) {
    return DataSourceProvider.getInstance().getDatasourceByContext().getSessionFactory();
    }
    @Autowired
    @Bean("dataSource")
    @Scope(value="session")
    public DataSource getDataSource() {
        return DataSourceProvider.getInstance().getDatasourceByContext().getDataSource();
    }

3) Обнаружить текущую БД внутри DataSourceProvider

public class DataSourceProvider {

private static DataSourceProvider instance = new DataSourceProvider();

public static DataSourceProvider getInstance() {
    return instance;
}

private HashMap<String, MyDataSource> dataSources;

private DataSourceProvider(){
}

public MyDataSource getDatasourceByContext() {
    String connectionName;
    if (RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        if (request==null) {
            connectionName = null;
        } else {
            //"currentDatabase" is set during logon
            connectionName = (String)request.getSession().getAttribute("currentDatabase");
        }
    } else {
        connectionName = null;
    }
    if (connectionName!=null) {
        MyDataSource dataSource = dataSources.get(connectionName);
        if (dataSource != null) {
            return dataSource;
        }
    }
    throw new IllegalStateException("No datasource for the context");
}
person Logger    schedule 13.10.2014