Dynamically configuring datasources - java

The situation is this: a table that is used in a query (name it SUMMARY) was formerly in the same server and database that I'm doing all the queries of the application (name it server1 and DB1). But recently the SUMMARY table was deleted from this database, which makes it necessary to consult other server / database combinations.
The data from the server name and the database to be used for access to the SUMMARY table are parameterized in a table for this purpose. These data depend on the database which is connected, this way: for example, if I'm in the database DB1 of server1 then parameters will be server21 and DB21, whereas if someone refers to the parameters from the DB5 from server1 parameters will be server16 and DB16.
On that side I have no problem because I have listed the SQL query of the two parameters, ready to give the name of the server and database to consult in each case. This query is necessary in order to give server name and database name with which dynamically generate the datasource to connect to.
The problem, and the topic of this entry is, whether anyone has ever had to dynamically configure the datasource to be used in hibernate.properties, since this is usually a single, fixed value and in this case should be allowed changes in order to view the SUMMARY table (using parameters retrieved by my SQL query) only in this specific case, while all other database operations must be performed by using the original connection properties.
That is: What I need is to dynamically generate the datasource based on the parameters coming from the query, so the approaches which handle this by knowing beforehand how many and what are the possible connections should be discarded because they are not viable to solve my problem.
The application specifications are:
Database engine: SQL Server 2005
Prog. Language: Java 5.0
Frameworks: Spring 2.0.4, Hibernate 3.0.5
App. Server: WAS 6.1
Thanks in advance to anyone who has that knowledge and willing to share.

You can use a ConnectionProvider in hibernate to decide how to get the connection to be used by a session. We use something like this in our application:
public Connection getConnection() throws SQLException {
DataSource ds = (DataSource) BeanFactory.getBean("dataSource" + UserInfo.getDSName());
return ds.getConnection();
}
UserInfo is a class that store stuff in a ThreadLocal. This code will chose a datasource from Spring depending on the name that was passed in the ThreadLocal. What we do is to set the name of the datasource we want to use before opening the session (the actual logic is a little bit more complicated than that, as it depends on user preferences and other stuff).
You can do something like that to choose what Database to connect to.
You can check the javadocs for the ConnectionProvider interface. Make your own implementation, set the hibernate.connection.provider_class property in your hibernate configuration file to point to your class, and you're done.

I guess, it depends on the number of database/server combinations and the frequency of using them, but if there are a lot of database/servers and low use frequency, using plain JDBC without Hibernate and data sources might be an option. I don't think Hibernate is meant for situations like that.

or extend org.apache.commons.dbcp.BasicDataSource
public class MyDataSource extends BasicDataSource {
private void init() {
username = ...
password = ...
url = ...
}
#Override
protected synchronized DataSource createDataSource() throws SQLException {
init();
return super.createDataSource();
}
}

Related

Use a second JpaConnection to a different database in keycloak 19

I'm trying to setup a keycloak instance which connects to a separate, second database for it's user management. I see a lot of jdbc examples online, but none with JPA.
I tried to setup my keycloak to use a separate JpaConnectionProvider to be able to use JPA in my CustomUserStorageProvider. For this, I created a subclass MySecondJpaConnectionProviderFactory of DefaultJpaConnectionProviderFactory which should create my own JpaConnectionProvider for usage in the CustomUserStorageProvider. But when obtaining the JpaConnectionProvider, strange things happen in the factory, even though the connection to the second database can be successfully obtained.
CustomUserStorageProvider using a JpaConnectionProvider created by MySecondJpaConnectionProviderFactory extends DefaultJpaConnectionProviderFactory
First, when calling migration(...) the factory is not able to locate the LiquibaseJpaUpdaterProviderFactory (on line 342), returning null for the lookup, causing a NullPointer on line 344.
void migration(MigrationStrategy strategy, boolean initializeEmpty, String schema, File databaseUpdateFile, Connection connection, KeycloakSession session) {
JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class, LiquibaseJpaUpdaterProviderFactory.PROVIDER_ID);
JpaUpdaterProvider.Status status = updater.validate(connection, schema);
if (status == JpaUpdaterProvider.Status.VALID) {
logger.debug("Database is up-to-date");
(....)
Since I don't need the migration, I copied the class methods into my own class instead of extending it and removed the migration part, but then keycloak is not able to find necessary classes when creating the entity manager factory on line 286.
Caused by: java.lang.ClassNotFoundException: org.jboss.jandex.DotName
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
Code in DefaultJpaConnectionProviderFactory:
(....)
properties.put(AvailableSettings.CLASSLOADERS, classLoaders);
emf = JpaUtils.createEntityManagerFactory(session, unitName, properties, jtaEnabled);
addSpecificNamedQueries(session, connection);
logger.trace("EntityManagerFactory created");
(....)
All in all, looking at these suspicious errors I'm wondering if I run into a dead end here: Is usage of a second JPA connection supposed to be possible? Am I missing something?
Or should I rather stick to doing it all with jdbc?
The setup:
I'm running keycloak 19 in a docker container where I pre-build according to this official guide. All my SPI's are recognized by keycloak, the jdbc connection to the second db is working.

Spring boot dynamic database URL based on REST parameters

I am using Spring boot to develop an application, we have one database for each customer, and the main problem is to avoid creating one instance for each, connected to a single database. With that in mind, I got stuck in the following situation:
I need my application to make connections to several different databases, witch Aliases I don't know when the application starts. Those aliases will be provided via REST requests, so it's dynamic.
All other credentials are equal on the databases, the Driver, URL, username and password, I really just need to change the alias to provide the right URL for that request.
I've done it creating datasources at the momment of the request, like this:
private static void selectAll(String alias){
String sql = "select * from user";
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.url("jdbc:postgresql://localhost:5432/"+alias);
dataSourceBuilder.username("username");
dataSourceBuilder.password("password");
DataSource dataSource = dataSourceBuilder.build();
try {
Connection con = dataSource.getConnection();
con.setAutoCommit(true);
con.prepareStatement(sql).executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
}
But by doing things that way, i can't use criteria and hibernate, and I have to map my objects retreived from the database by hand and write my own simple SQLs for each object/table.
The main question is, can i use Entities and Repositories (Spring data) with dynamic databases, or if I go for this I'll have to choose between those and having one instance for each customer, connected to a single database.
I've searched on forums, and the solutions are eigther based on static connections, configured inside Application.properties, or they assume that the the URL is known.
Thank you for your help, any other better aproaches are wellcome, any toughts at all.

How can I use Hibernate/JPA to tell the DB who the user is before inserts/updates/deletes?

Summary (details below):
I'd like to make a stored proc call before any entities are saved/updated/deleted using a Spring/JPA stack.
Boring details:
We have an Oracle/JPA(Hibernate)/Spring MVC (with Spring Data repos) application that is set up to use triggers to record history of some tables into a set of history tables (one history table per table we want audited). Each of these entities has a modifiedByUser being set via a class that extends EmptyInterceptor on update or insert. When the trigger archives any insert or update, it can easily see who made the change using this column (we're interested in which application user, not database user). The problem is that for deletes, we won't get the last modified information from the SQL that is executed because it's just a plain delete from x where y.
To solve this, we'd like to execute a stored procedure to tell the database which app user is logged in before executing any operation. The audit trigger would then look at this value when a delete happens and use it to record who executed the delete.
Is there any way to intercept the begin transaction or some other way to execute SQL or a stored procedure to tell the db what user is executing the inserts/updates/deletes that are about to happen in the transaction before the rest of the operations happen?
I'm light on details about how the database side will work but can get more if necessary. The gist is that the stored proc will create a context that will hold session variables and the trigger will query that context on delete to get the user ID.
From the database end, there is some discussion on this here:
https://docs.oracle.com/cd/B19306_01/network.102/b14266/apdvprxy.htm#i1010372
Many applications use session pooling to set up a number of sessions
to be reused by multiple application users. Users authenticate
themselves to a middle-tier application, which uses a single identity
to log in to the database and maintains all the user connections. In
this model, application users are users who are authenticated to the
middle tier of an application, but who are not known to the
database.....in these situations, the application typically connects
as a single database user and all actions are taken as that user.
Because all user sessions are created as the same user, this security
model makes it very difficult to achieve data separation for each
user. These applications can use the CLIENT_IDENTIFIER attribute to
preserve the real application user identity through to the database.
From the Spring/JPA side of things see section 8.2 at the below:
http://docs.spring.io/spring-data/jdbc/docs/current/reference/html/orcl.connection.html
There are times when you want to prepare the database connection in
certain ways that aren't easily supported using standard connection
properties. One example would be to set certain session properties in
the SYS_CONTEXT like MODULE or CLIENT_IDENTIFIER. This chapter
explains how to use a ConnectionPreparer to accomplish this. The
example will set the CLIENT_IDENTIFIER.
The example given in the Spring docs uses XML config. If you are using Java config then it looks like:
#Component
#Aspect
public class ClientIdentifierConnectionPreparer implements ConnectionPreparer
{
#AfterReturning(pointcut = "execution(* *.getConnection(..))", returning = "connection")
public Connection prepare(Connection connection) throws SQLException
{
String webAppUser = //from Spring Security Context or wherever;
CallableStatement cs = connection.prepareCall(
"{ call DBMS_SESSION.SET_IDENTIFIER(?) }");
cs.setString(1, webAppUser);
cs.execute();
cs.close();
return connection;
}
}
Enable AspectJ via a Configuration class:
#Configuration
#EnableAspectJAutoProxy
public class SomeConfigurationClass
{
}
Note that while this is hidden away in a section specific to Spring's Oracle extensions it seems to me that there is nothing in section 8.2 (unlike 8.1) that is Oracle specific (other than the Statement executed) and the general approach should be feasible with any Database simply by specifying the relevant procedure call or SQL:
Postgres for example as the following so I don't see why anyone using Postgres couldn't use this approach with the below:
https://www.postgresql.org/docs/8.4/static/sql-set-role.html
Unless your stored procedure does more than what you described, the cleaner solution is to use Envers (Entity Versioning). Hibernate can automatically store the versions of an entity in a separate table and keep track of all the CRUD operations for you, and you don't have to worry about failed transactions since this will all happen within the same session.
As for keeping track who made the change, add a new colulmn (updatedBy) and just get the login ID of the user from Security Principal (e.g. Spring Security User)
Also check out #CreationTimestamp and #UpdateTimestamp.
I think what you are looking for is a TransactionalEvent:
#Service
public class TransactionalListenerService{
#Autowired
SessionFactory sessionFactory;
#TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleEntityCreationEvent(CreationEvent<Entity> creationEvent) {
// use sessionFactory to run a stored procedure
}
}
Registering a regular event listener is done via the #EventListener
annotation. If you need to bind it to the transaction use
#TransactionalEventListener. When you do so, the listener will be
bound to the commit phase of the transaction by default.
Then in your transactional services you register the event where necessary:
#Service
public class MyTransactionalService{
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
#Transactional
public void insertEntityMethod(Entity entity){
// insert
// Publish event after insert operation
applicationEventPublisher.publishEvent(new CreationEvent(this, entity));
// more processing
}
}
This can work also outside the boundaries of a trasaction if you have the requirement:
If no transaction is running, the listener is not invoked at all since
we can’t honor the required semantics. It is however possible to
override that behaviour by setting the fallbackExecution attribute of
the annotation to true.

HikariCP - Setting connected database dynamically on connection check-in and check-out from connection pool

I need to switch the database when a connection is checked-in and checked-out from pool. For instance, session X needs to receive a connection to database A and session Y needs to receive connection to database B.
I amd able to do it with C3P0 using a connection customizer. It calls the methods onCheckIn and onCheckOut of AbstractConnectionCustomizer, so I can do something like:
public class MyConnectionCustomizer extends AbstractConnectionCustomizer {
#Override
public void onCheckOut(Connection c, String parentDataSourceIdentityToken) throws Exception {
if (something) {
c.setCatalog("some database name");
}
}
#Override
public void onCheckIn(Connection c, String parentDataSourceIdentityToken) throws Exception {
c.setCatalog("some other database name");
}
}
I am trying to switch to HikariCP but it calls the customize method of the IConnectionCustomizer only once, when the connection gets created. So, how can I manage to implement such functionality?
One use case is multi-tenancy where there is single Database with multiple schemas. Based on the tenant logged into the application we need to switch the schema dynamically
I'm trying to understand the use-case for this? Why wouldn't you use separate pools, if session X really needs a connection to database A, and session Y needs a connection to database B?
HikariCP will likely never support such functionality (I can say that as one of the authors). A connection pool should provide a DataSource that is completely transparent to the application, such that if the pool was removed and the native DataSource was used instead the application would function identically (albeit less efficiently).
The very fact that you can do something like this in C3P0, but not in HikariCP, Vibur, or Apache DBCP should be a red-flag. Using such functionality locks you into a specific pool implementation, which is never a good thing.
Sorry, I can't provide a happy answer. If I were you I would consider writing an application level helper class to get/return connections that provides the semantics you are looking for.

How do I do nested transactions in hibernate using only one connection?

Context of problem I want to solve: I have a java spring http interceptor AuditHttpCommunicationInterceptor that audits communication with an external system. The HttpClieant that does the communication is used in a java service class that does some business logic called DoBusinessLogicSevice.
The DoBusinessLogicSevice opens a new transaction and using couple of collaborators does loads of stuff.
Problem to solove: Regardless of the outcome of any of the operations in DoBusinessLogicSevice (unexpected Exceptions, etc) I want audits to be stored in the database by AuditHttpCommunicationInterceptor.
Solution I used: The AuditHttpCommunicationInterceptor will open a new transaction this way:
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
new TransactionTemplate(platformTransactionManager, transactionDefinition).execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// do stuff
}
});
Everything works fine. When a part of DoBusinessLogicSevice throws unexpected exception its transaction is rolled back, but the AuditHttpCommunicationInterceptor manages to store the audit in the database.
Problem that arises from this solution: AuditHttpCommunicationInterceptor uses a new db connection. So for every DoBusinessLogicSevice call I need 2 db connections.
Basicly, I want to know the solution to the problem: how to make TransactionTemplate "suspend" the current transaction and reuse the connection for a new one in this case.
Any ideas? :)
P.S.
One idea might be to take a different design approach: drop the interceptor and create an AuditingHttpClient that is used in DoBusinessLogicSevice directly (not invoked by spring) but I cannot do that because I cannot access all http fields in there.
Spring supports nested transactions (propagation="NESTED"), but this really depends on the database platform, and I don't believe every database platform is capable of handling nested transactions.
I really don't see what's a big deal with taking connection from a pool, doing a quick audit transaction and returning connection back.
Update: While Spring supports nested transactions, it looks like Hibernate doesn't. If that's the case, I say: go with another connection for audit.

Categories

Resources