2015-01-12 1 views
1

Алиса и Боб хотят, чтобы Дэйв разработал приложение. Оба хотят работать с идентичными типами данных, но оба хотят сохранить свои данные отдельно и безопасными. Поэтому Дейв уходит, чтобы построить схему DEV.Dynamic runtime Выбор схемы PostgreSQL в jOOQ

CREATE TABLE foo (id INT PRIMARY KEY); 
CREATE FUNCTION bar(_id INT) RETURNS INT AS $$ 
    INSERT INTO foo (id) VALUES (_id) RETURNING id; 
$$ LANGUAGE SQL; 

Дэйв использует пролетный путь для инициализации схемы для Алисы и Боба, поэтому они оба имеют таблицу Foo и функцию бара. Дэйв использует jOOQ для генерации java-api и сопоставляет схему DEV во время выполнения с пользовательскими схемами. Дейв, ранее не имеющий отношения ни к одному из своих клиентов, внезапно оказывается племянником Боба.

Но Алиса и Боб возвращаются к Дейву позже и попросят его написать для них автоматизацию. Поэтому Дейв решает создать машинного пользователя Роба, который имеет доступ к схемам Алисы и Боба. Он может повторно использовать все тот же код, сгенерированный jOOQ, и все, что использует foo, работает безупречно, пока автоматизация не попытается выполнить функцию бара - Ошибка: отношение «foo» не существует. Отображение схемы времени выполнения не влияет на ссылку, не содержащую схемы, на foo внутри функциональной панели.

Тривиальный ответ заключается в том, чтобы установить путь search_path к тому, какой пользователь Rob должен маскироваться как. Это можно установить вручную перед каждым набором вызовов jOOQ, но это похоже на тип подверженного ошибкам, утомительный код jOOQ, как правило, изолирует нас от записи. Дэйв мог создать двух пользователей Rolice и Robob, каждый из которых индивидуально настроен для доступа к своему соответствующему клиенту. Но это, очевидно, плохо масштабируется, особенно при использовании пула соединений. Дэйв выбирает настраиваемый ConnectionProvider, который задает путь search_path, но это потенциально ужасно много накладных расходов, так как запрос search_path должен возвращать результат перед возвратом соединения.

Так что Дэйв охотится вокруг, задает очевидный вопрос о StackOverflow о настройке пути search_path и узнает, что функция была удалена по отличным причинам из jOOQ во время выпуска 3.0. Но Дэйв - тот парень, который действительно любит элегантность jOOQ и чувствует, что должно быть лучшее решение, чем его взломать.

Я не Дэйв, но я, конечно, вижу, откуда он. Каков наилучший способ в jOOQ, чтобы один автоматизированный пользователь взаимодействовал с несколькими идентичными пользовательскими схемами и, в частности, с функциями, содержащими ссылки без схемы?

В случае, если это имеет значение, я на PostgreSQL 9.3 и jOOQ 3.5. Дэйв находится на том, кем бы вы его ни хотели, так как я сделал его полчаса назад.

Соответствующий бит решения ConnectionProvider:

@Override 
public Connection acquire() throws DataAccessException { 
    Connection c = dataSource.getConnection(); 
    try(Statement s = c.createStatement()){ 
     s.execute("SET search_path = '"+schema+"'"); 
    }catch(SQLException e){ 
     throw new DataAccessException("Could not initialize connection", e); 
    } 
    return c; 
} 
+0

Я не получаю эту часть: «*, поскольку запрос search_path должен возвращать результат перед возвратом соединения *» - не можете ли вы просто запустить 'set schema = ...' (или 'set search_path = ...'), например, как «родной» оператор в «ConnectionProvider»? W hy нужно ли запускать * запрос *? –

+0

@a_horse_with_no_name, JDBC не имеет асинхронного выполнения, поэтому каждый раз, когда jOOQ запрашивает соединение, он должен ждать, пока сервер ответит OK, чтобы установить путь search_path. Он не возвращает ResultSet, но возвращает результат. Может быть, я не понимаю, что вы подразумеваете под «родной»? – Fuwjax

+0

Что вы используете для управления транзакциями? Я считаю, что правильное место для инициализации пути поиска - это начало транзакции ... Ваше текущее решение слишком часто устанавливает путь поиска, я думаю –

ответ

1

Оказывается, что в настоящее время, по крайней мере, установка search_path что-то выходит за рамки jOOQ. Теоретически мы могли бы использовать Java-миграции в Flyway и заставить все ссылки внутри функции иметь явные схемы, но это звучит довольно болезненно. Это оставляет нам возможность вручную установить путь поиска, потенциально добавить его в наше управление транзакциями или написать более интеллектуальный ConnectionProvider.

У нас есть довольно строгая модель с одним запросом на транзакцию в нашем приложении, поэтому на самом деле нет никакого управления транзакциями, чтобы добавить запрос «set search_path = ...». Решение ConnectionProvider представляется нашим лучшим вариантом.

У нас уже есть пользовательская реализация ConnectionProvider для пула соединений, поэтому для добавления в логику (указанная выше в конце вопроса) не было слишком много сложностей, чтобы задать путь search_path. Мы могли бы сделать его более эффективным, украсив соединение чем-то, что запомнило, к чему его текущий путь search_path, и префиксом «set search_path = ...«перед началом любого заявления, прежде чем он выйдет за дверь, если это необходимо. Мы уже видим влияние производительности на настройку search_path на каждый запрос, так что это просто вопрос времени, прежде чем это станет необходимостью. назад к боли нашего старого проприетарного уровня доступа к базе данных.

По крайней мере, я не буду принимать это как ответ, пока не напишу и не открою исходный код пула соединений/search_path для параметра ConnectionProvider, в надежде, что кто-то еще будет предлагать лучшее решение, прежде чем я приступлю к нему.