Multi-Keyspace with Cassandra's Java Driver on fly - java

Currently, We use Cassandra's Java driver to configure session based on the attributes defined in properties file which looks like this:
Cluster cluster = Cluster.builder()
.addContactPoints(contactPoints)
.withPort(environment.getRequiredProperty("cassandra.port", Integer.class))
.withCredentials(username, password)
.build();
Session session = cluster.connect(environment.getRequiredProperty("cassandra.keyspace"));
What we want to achieve now is to use multi-keyspace on fly, if there is a way to do it, being able to detect the cassandra connection for a given request without changing much on the existing logic except the configuration. Any suggestion or help on this would be a great way to start

The best way to achieve this is to specify table as keyspace.table in all queries, and don't rely on the keyspace set in the session. You can of course have multiple session objects - one per keyspace, but it's not recommended, as every session is quite heavyweight, and open at least 2 connections to every node in the cluster.
Recent versions of the Java driver support setting the keyspace via setKeyspace function of the SimpleStatement class, but it requires support on the Cassandra side as well (don't remember which version although, at least DSE 6.0 supports this).
(Update): The setting keyspace on query level isn't merged into OSS Java driver, and available only in DSE Java driver.

Related

Dealing with DB instance IAM authentication token expiry

As per here after you generate an authentication token, it's valid for 15 minutes before it expires.
Consequently, if you don't hold onto the connection it will expire after 15 mins.
Upon application startup, we have code a bit like this:
Jdbi.create(dbProperties().getDbUrl(), jdbiProperties());
part of the jdbiProperties() method calls:
generator.getAuthToken(GetIamAuthTokenRequest.builder()
.hostname(url).port(PORT).userName(username)
.build());
We have a repo method that uses the jdbi (wrappper of a JDBC DataSource) like so:
jdbi.withHandle(handle ->
handle.createQuery("select name from contacts")
.mapTo(String.class)
.list());
The problem here is that since we generated the token upon startup it stop working after 15 min.
Is the some standard pattern for dealing with this? Seems like we could refresh the token upon every call (seems inefficient). Or we could request a new token upon receipt of expiry (through an exception).
Any suggestions appreciated.
TIA
Please see https://github.com/team-carepay/carepool-aws-jdbc
This library supports different ways of providing IAM authentication for RDS:
Wrapper for Tomcat Datasource
Wrapper for Hikari Datasource
Plugin for MariaDB driver
Plugin for MySQL driver
Depending on your project, you can pick any of the above options. For Jdbi, you can use Jdbc.create with takes a DataSource, e.g. Hikari or MySQL.
Please note that some versions of MariaDB driver support native AWS IAM (assuming that you have the AWS Java SDK in your classpath). Since that support was removed, you can now use https://github.com/awslabs/aws-mysql-jdbc which also supports IAM authentication. The MariaDB and AWS-MySQL drivers need a few libraries on the classpath (jackson, commons-collections, etc), so make sure you include these. The carepool-aws-jdbc plugin does not require any 3rd party libraries.

Challenge with getting correct mappings for aliases using Java+Hibernate+DB2

I am writing a Java application using Hibernate and IBM DB2 9.7 LUW for database. I am using the SQLQuery API to read a custom SQL query string and execute it against the DB. The query contains aliases, i.e. SELECT WORK.EMPLOYEE AS WORKEMPLOYEE, just as an example.
When retrieving the result set from DB with list() command, the resulting map does not contain the alias as key. So writing map.containsKey("WORKEMPLOYEE") returns false. I also tried using query.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE) but it did not change the situation.
I don't know Hibernate, but I suspect this is a symptom of an issue we've seen regarding DB2 LUW and aliases.
It relates to the distinction between a column's "name" and its "label". IBM document the behaviour at this page, which suggests that the behaviour will be different with different versions of the database driver.
Specifically, this paragraph:
Whether a column in the SELECT list of a query has an AS clause. For JDBC drivers before the IBM Data Server Driver for JDBC and SQLJ Version 4.0, if a column in the SELECT list of a query contains an AS clause,ResultSetMetaData.getColumnNamereturns the argument of the AS clause. Under the IBM Data Server Driver for JDBC and SQLJ Version 4.0,ResultSetMetaData.getColumnNamereturns the table column name.
suggests that you are seeing the behaviour that you (and I) regard as faulty because you are using a version 4+ driver.
There are three possible solutions, none of which is entirely satisfactory.
Use a version 3.x driver.
You can switch to calling getColumnLabel on the ResultSetMetaData. That would give you the correct result with DB2 LUW using version 4+ drivers. This applies to our version of the problem, but might not be relevant or possible via Hibernate.
There is a property which you can set on the DataSource or Connection object: useJDBC4ColumnNameAndLabelSemantics would need to be set to DB2BaseDataSource.NO. However, I don't know whether you can set that via Hibernate. If you can, it should make aliases behave the old (and propert) way.
In our environment we haven't decided yet on how best to deal with this. We're working around it using option 1 for the moment. I'm inclined to use option 2, but we have to suport multiple DB platforms, and I'm not sure how it will work with the others. For option 3, we're sing Spring, so it may not be practical to set that property.

Connecting two datasources to the same database

I am working on an application where we have decided to go for a multi-tenant architecture using the solution provided by Spring, so we route the data to each datasource depending on the value of a parameter. Let's say this parameter is a number from 1 to 10, depending on our clients id.
However, this requires altering the application-context each time we add a new datasource, so to start we have thought on the following solution:
Start with 10 datasources (or more) pointing to different IPs and the same schema, but in the end all routed to the same physical database. No matter the datasource we use, the data will be sent to the same schema in this first scenario.
The data would be in the same schema, so the same table would be shared among datasources, but each row would only be visible to each datasource (using a fixed where clause in every CRUD operation)
When we have performance problems, we will create another database, migrate some clients to the new schema, and reroute the IP of one of the datasources to the new database, so this new database gets part of the load of the old one
Are there any drawbacks with this approach? I am concerned about:
ACID properties lost
Problems with hibernate sessionFactory and second level cache
Table locking issues
We are using Spring 3.1, Hibernate 4.1 and MySQL 5.5
i think your spring-link is a little outdated, hibernate 4 can handle multi-tenancy pretty well on it's own. i would suggest to use the multiple schemas approach because setting up and initializing a new schema is programmatically relativly easy to do (for example on registration-time), if you have so much load though (and your database-vendor does not provide a solution to make this transparent to your application) you need the multiple database approach, you should try to incorporate the tenant-id in the database-url or something in that case http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch16.html

How to set default schema in a Spring/mybatis application with Oracle DB?

Coming from a mysql background, I am able to set the default schema name that I want to use for all my sql queries in the connection url. I now have an Oracle DB that I need to access. I am aware that I cannot specify the schema I want to use in the URL since the user is the schema name being used.
I realize that I can use a line of SQL code:
ALTER SESSION SET CURRENT_SCHEMA=default_schema
The project is using mybatis 2.3.5 as my SQL framework, but I am completely new to mybatis. Is there a simple way to configure mybatis to accomplish this? My application is a Spring 3 application, so I am using the Spring DataSourceTransactionManager to manage my transactions. I would presume that the manager must be made aware of this requirement to ensure that the command is sent whenever creating a new connection.
I've tried searching online, but most of the examples I find all have the schema names included within the sql queries in the SqlMaps, which I find to be bad practice.
In an ideal world, the schema name would be part of the URL such that I can make changes to the schema name for different environments (ex: dev, test, prod, etc) without touching the code (ie: only configured at the JNDI/application server level). I would be happy if I could use a Spring configuration value to set this as well as I could still use a JNDI lookup or a system environment property to retrieve the value.
Can anyone point me in the right direction?
Thanks,
Eric
As far as I know, there is no option in Oracle to change your URL in order to connect to a specific user schema.
1) mybatis: You may set your current schema to a deserved one before you start your operations. You can write your specification in a property file and set your method's arguments from that file. You do not need to change your code to change your schema in that case.
<update id="mySetSchemaMethod" parameterClass="String">
ALTER SESSION SET CURRENT_SCHEMA = ${schemaName}
</update>
2) trigger: If you are using this connection only for this particular java application, you can set a client event trigger so set your CURRENT_SCHEMA. This time, you need to change the trigger in order to manage test/prod changes.
CREATE OR REPLACE TRIGGER Set_Schema_On_Logon
AFTER LOGON
ON MY_SCHEMA
BEGIN
ALTER SESSION SET CURRENT_SCHEMA = MY_TEST_SCHEMA;
END;

Pattern for connecting to different databases using JDBC

I'm writing an application which has to be configurable to connect to Oracle, SQL Server and MySQL depending on client whim.
Up till now I'd been planning on using the JDBC-ODBC bridge and just connecting to the databases using different connection strings.
I'm told this is not very efficient.
Is there a pattern or best practice for connecting to multiple database systems? Or for selecting which driver to use?
Should I have it configurable? but include all three drivers or build three separate clients?
I'm not doing anything complex just pumping (inserting) data into the database from an event stream.
I would suggest that you make it configurable and include the three drivers. You can use a pattern like this: Create a super class (lets call it DAO) that provides the functionality of connecting to the database. This could be abstract.
Create a concrete sub class for each type of database that you wish to connect to. So you may end up with MySQLDAO, MSSQLDAO, and OracleDAO. each one will load the respective driver and use its respective connection string.
Create another class (lets call it DAOFactory) with a method getDAO(DB) that will create an instance of the DAO depending on the value of DB.
So for instance(in Pseudocode):
if(DB.equals("MySQL")){
DAO = new MySQLDAO();
}
return DAO;
So any code that needs to connect to the database will call the DAOFactory and ask for a DAO instance. You may store the DB value in an external file (like a properties file) so that you do not have to modify code to change the type of database.
this way your code does not need to know which type of database it is connecting to, and if you decide to support a fourth type of database later you will have to add one more class and modify the DAOFactory, not the rest of your code.
If you need anything complex, Hibernate is a good choice.
otherwise, what I would do is store your connection details in a properties file (or some other form of configuration) - namely: driver classname, JDBC url, username and password.
Then, all you need to do is load up the connection details from your properties file and include the correct JAR file on your classpath and you're done.
You could use a library such as Commons-DBCP if you wanted it to be a little easier to configure but other than that it's all you need to do (provided your SQL statements work on every database, of course).
If you're careful (and you test), you can do this with straight JDBC and just vary the driver class and connection information. You definitely want to stay away from the JDBC-ODBC bridge as it's generally slow and unreliable. The bridge is more likely to behave differently across dbs than JDBC is.
I think the DAO path is overkill if your requirements are as simple as listed.
If you're doing a lot of inserts, you might want to investigate prepared statements and batched updates as they are far more efficient. This might end up being less portable - hard to say without testing.
Take a look at Datasource. This is the preferred mechanism for obtaining a database connection.
IMO this provides an adminstrator the greatest flexibility for choosing database, connection pooling, and transaction strategies.
If you're using tomcat, then see here for how to register a Datasource with tomcat's JNDI.
If you're using Spring, then you can obtain a Datasource using jee:jndi-lookup.
If you're using Spring, but don't want to use JNDI, take a look at DriverManagerDataSource for a discussion of how to obtain a pooled Datasource (DBCP or C3P0).

Categories