问题
Alice and Bob want Dave to develop an application. They both want to work with identical data types, but they both want to keep their data separate and secure. So Dave goes off to build a DEV schema.
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;
Dave uses Flyway to initialize schemas for Alice and Bob, so they both have the foo table and bar function. Dave uses jOOQ to generate a java api and map the DEV schema at runtime to the user schemas. Dave, previously having no relation to either of his clients, suddenly finds himself the nephew of Bob.
But Alice and Bob both come back to Dave later and ask him to write some automation for them. So Dave decides to create a machine user Rob who has access to Alice and Bob's schemas. He can reuse all the same jOOQ generated code and everything that uses foo directly works flawlessly, until the automation tries to execute the bar function - Error: relation "foo" does not exist. The runtime schema mapping has no effect on the schema-less reference to foo inside the function bar.
The trivial answer is to set the search_path to whichever user Rob should masquerade as. This could be set manually before every set of jOOQ calls, but that feels like the sort of error-prone, tedious code jOOQ generally insulates us from writing. Dave could create two users Rolice and Robob, each individually configured to access their respective client. But this obviously scales poorly, especially when using connection pooling. Dave opts for a custom ConnectionProvider that sets the search_path, but that's potentially an awful lot of overhead, as the search_path query has to return a result before returning the connection.
So Dave hunts around, asks the obvious question on StackOverflow about setting the search_path, and finds out that the feature was removed, for excellent reasons, from jOOQ during the 3.0 release. But Dave's the kind of guy who really likes the elegance of jOOQ and feels like there must be a better solution than his hack.
I'm not Dave, but I certainly see where he's coming from. What is the best way in jOOQ to have one automated user interact with several identical user schemas, and in particular, with functions containing schema-less references?
In case it matters, I'm on PostgreSQL 9.3 and jOOQ 3.5. Dave is on whatever you want him to be, since I made him up a half hour ago.
The relevant bit of the ConnectionProvider solution:
@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;
}
回答1:
It appears that for the time being at least, setting search_path is something out of scope for jOOQ. We could, in theory, use Java migrations in Flyway and force all references within a function to have explicit schemas, but that sounds pretty painful. Which leaves us with either setting the search path manually, potentially adding it to our transaction management, or writing a smarter ConnectionProvider.
We have a pretty strict one-query-per-transaction model on our application, so there isn't really any transaction management in place to add the "set search_path = ..." query to. The ConnectionProvider solution appears to be our best option.
We already have a custom ConnectionProvider implementation for connection pooling, so it wasn't too big of a hassle to add in the logic (given above at the end of the question) to set the search_path. We could make it more performant by decorating the Connection with something that remembered what its current search_path is set to, and prefixing a "set search_path = ...;" to the front of any statement before it goes out the door if need be. We are already seeing a performance impact to setting the search_path every request, so it's just a matter of time before this becomes a necessity. Well, that or going back to the pain of our old proprietary database access layer.
At the very least, I won't accept this as the answer until I write and open source the connection pooling/search_path setting ConnectionProvider, in the hope that someone else will come up with a better solution before I get around to it.
来源:https://stackoverflow.com/questions/27911948/dynamic-runtime-postgresql-schema-selection-in-jooq