There are some limitations of using JDBC and database dependency is one of those limitations.
Is there any pattern or way to achieve database independence in JDBC (w
While (very) late to the party and not bringing anything new really, I'd like to endorse a bit and expand on Neville's and Vlad's answers (as a plausible option).
Frameworks like jOOQ and Hibernate are, probably, the better way to go, especially IF what you're trying to obtain is something completely generic (i.e. handle most imaginable DB operations) as opposed to a handful of specific tasks involving SQL. However, since it seems they're not an option for you, externalizing SQL in one way or another could actually take you quite far (from an SQL dialect point of view, not necessarily from the JDBC binding issues so nicely outlined in Lukas' answer before).
I've previously worked on a custom Java-based ETL engine (successfully) running on both Oracle and DB2. As you can imagine, we needed the best queries we could get in terms of speed and our DBAs were optimizing them using every trick in the book and then some! So, not only had we 2 SQL dialects to accommodate, but the queries we had to execute were also highly customized (hints etc.)... The solution we settled upon was to generate the SQLs at runtime, using a template engine (Velocity, at the time) and custom templates, tailored for the target DB (probably similar to the way Hibernate or jOOQ build they final SQL). Admittedly, we had our own "context" with specific and well defined needs…
Firstly, database independence is hard. Really, really hard; to achieve it without using an ORM or other tool, you will have to trade some other aspect of your solution design. Simplicity and maintainability will suffer, as will the effort to implement the solution.
So, I'd make really, really sure that this is a high priority requirement - a hypothetical "what if we want to switch from Oracle to SQL Server" question is - in my opinion - not sufficient justification to incur the additional costs...
If you must deliver this feature, and ORM is by far the easiest way to do it. The ORM frameworks are specifically designed with database independence in mind (and their complexity is at least in part due to that requirement). They do so by abstracting away the database implementation into a higher level; instead of thinking in SQL statements, you're encouraged to think about domain objects.
Having said all that...
I have delivered a solution (not in Java, but the principle stands) which allowed database independence. We stored our SQL statements as resources, and loaded them via resource files. The default was ANSI SQL, which should work on any modern SQL-compatible database.
We had resource files for each database flavour we supported (in our case MySQL and Oracle), and used overrides to load in database-specific SQL statement if they existed.
This works like internationalization in most languages - look for locale-specific strings first, fall back to default if you can't find one.
Java's resource bundle would make this pretty easy. Not hardcoding your SQL in your application has other benefits - fixing a database problem without changing the application code is much easier, you can deploy fixes as "resource" updates rather than shipping a new binary etc.
It seems to me that what you're actually trying to build there by creating a library that allows you to generically access a database is quite similar to what many of the ORMs already do.
Have you tried jOOQ? it's quite different to the other ORMs out there and does what you're looking for. One could refer to it as a "tool" but then I could also refer to what you're trying to build as a "tool".
jOOQ strives to be a java native language for making portable DB calls so sounds like it's exactly what you're looking for.
https://www.jooq.org/
I think I'm qualified to answer, being the author of jOOQ, which was already suggested in another answer. As I've shown, it's totally possible to achieve what you're trying to do, but there is a long long road ahead for you, if you want to roll your own.
JDBC is an excellent network protocol abstraction, so it's a great starting point. There are quite a few caveats though as you move on to solving more complex problems inside of an API like the one you're trying to build. For instance:
NULL
values. Sometimes it works, sometimes it doesn't.java.sql.Date
and java.time.LocalDate
? Good luck!TIMESTAMP WITH TIME ZONE
data type there are?INTERVAL
types? Really?OUT
parametersBOOLEAN
typeautoCommit
is set to true?DatabaseMetaData
to reverse engineer your schema? Forget it!ResultSetMetaData
to discover qualified column names? Well...As you've seen, even if JDBC does its job really well for most people (and there's always a hacky workaround for each of the above that works for an individual database. But you want to write an API that works on all databases, so you have to fix / work around all of the above. Trust me. That'll keep you busy for a while!
But thus far, we've only discussed how hard it is to bind to JDBC. We haven't discussed how hard it is to standardise SQL. So let's discuss that for a moment:
LIMIT n OFFSET m
is nice, eh? Or is it LIMIT m, n
? Or TOP n START AT m
? Or OFFSET m ROWS FETCH NEXT n ROWS ONLY
? What if you want to support older databases? Will you roll your own ROW_NUMBER()
filtering? Here, I've documented it for you.SELECT
without FROM
. In other databases, you need something like a DUAL
table. There you go, all documented.DUAL
table, until their parser breaks and you still need it (hello MySQL)SELECT
without FROM
, but they do require FROM
for WHERE
/ HAVING
/ GROUP BY
(SELECT 1 UNION SELECT 2) UNION ALL SELECT 3
. Will it work on all databases? (I mean the parenthesised nesting)EXCEPT ALL
supported? Is EXCEPT
even supported?FULL OUTER JOIN
supported?AS
permitted on derived tables?ORDER BY
clause contain expressions referencing aliases from the SELECT
clause? Or only expressions referencing columns from the FROM
clause?ORDER BY
clause contain expressions at all?ORDER BY
clause?SUBSTRING()
or SUBSTR()
or INSTR()
or what?VALUES()
constructor, as in SELECT * FROM (VALUES (1), (2)) t(a)
? Few databases have native supporttable(column)
in one go) if it's not supported? Here's a funky idea.(a, b) > (x, y)
is the same as this: a > x OR a = x AND b > y
. The former isn't supported everywherePostgreSQL's UPDATE .. RETURNING
can be emulated using a PL/SQL block in Oracle 12c:
declare
t0 dbms_sql.number_table;
t1 dbms_sql.date_table;
c0 sys_refcursor;
c1 sys_refcursor;
begin
update "TEST"."T_2155"
set "TEST"."T_2155"."D1" = date '2003-03-03'
returning
"TEST"."T_2155"."ID",
"TEST"."T_2155"."D1"
bulk collect into t0, t1;
? := sql%rowcount; // Don't forget to fetch the row count
open c0 for select * from table(t0);
open c1 for select * from table(t1);
? := c0; // These need to be bound as OracleTypes.CURSOR OUT params
? := c1; // These need to be bound as OracleTypes.CURSOR OUT params
end;
As you can see, it can totally be done. I've done it, it's called jOOQ. It's probably been the biggest challenge of my professional life and it has been fun. jOOQ 3.10 will feature a parser, which can translate from a SQL string (in any dialect) to another SQL string (in a specific dialect), which is the next level of vendor agnosticity.
But it was a long way to go to get here. Before I did jOOQ (started in 2009), I've worked with Oracle SQL and in-house JDBC-based frameworks (like the one you're planning to write) intensively. I wrote jOOQ because I've seen many in-house frameworks being written and none of them did the job well. The developers always tackled SELECT
.. FROM
.. WHERE
- which is the easy part. Some managed to get JOIN
in the game, and perhaps GROUP BY
and that's it. They then abandoned the task, because they had more important stuff to do than maintain boring and buggy infrastructure software.
Now, I don't know what your motivation is to do this yourself, but my advice here is:
You can try building your own jOOQ (or Hibernate). It's a fun challenge. But if you have deadlines, I really suggest you review the above options.
As I explained in my book, there are two ways to address database portability:
However, since you explicitly stated that:
Is there any pattern or way to achieve database independence in JDBC (without using any other ORM framework or tool).
You need to solve this problem using JDBC API, right?
The only way to do it, without taking neither the ORM or jOOQ approach which practically means implementing your own data access framework, is to use a custom DAO for each supported database.
So, you can define the DAO interfaces that are used by the Service layer, like ProductDAO
and implement each and one for every supported database:
OracleProductDAOImpl
MySQLProductDAOImpl
PostgreSQLProductDAOImpl
SQLServerProductDAOImpl
Or, you can use a single ProductDAOImpl
, but then you need to use stored procedures and make sure each stored procedure is implemented in the database:
So, although it's possible, you are basically where we were in the early 2000's when we didn't have either Hibernate or jOOQ. From my experience, it's way more work than if you used a mature data access framework.