Мы любим работать с JDBC
Никто не сказал. Когда-либо.
Если говорить более серьезно, то JDBC на самом деле очень классный API, если подумать. Вероятно, это также одна из причин, почему Java стала популярной платформой, которой она является сегодня . До JDK 1.1 и до ODBC (и это было очень давно) было трудно представить какую-либо платформу, которая бы вообще стандартизировала доступ к базе данных. Черт, сам SQL почти не был стандартизирован в то время, и вместе с ним появилась Java с JDBC, простым API, содержащим всего несколько элементов, о которых вы должны знать в повседневной работе:
Connection
: объект, который моделирует все ваши взаимодействия с БДPreparedStatement
: объект, который позволяет вам выполнить операторResultSet
: объект, который позволяет вам получать данные из базы данных
Это оно!
Обратно в реальность
Это была теория. На практике корпоративное программное обеспечение, работающее поверх JDBC, быстро развилось в направлении этого:
Взлом JDBC. Информация об авторских правах на эту страницу
JDBC является одним из последних курортов для разработчиков Java, где они могут почувствовать себя настоящими хакерами, во многих отношениях взламывая этот очень насыщенный, очень многословный и очень загадочный API. Практически каждый, кто работает в JDBC, будет реализовывать обертки вокруг API, чтобы предотвратить по крайней мере:
- Распространенные синтаксические ошибки
- Несоответствие индекса переменной связывания
- Динамическое построение SQL
- Крайние случаи вокруг использования LOBs
- Обработка и закрытие ресурсов
- Управление массивом и UDT
- Хранимая процедура абстракции
… и многое другое.
So while everyone is doing the above infrastructure work, they’re not working on their business logic. And pretty much everyone does these things, when working with JDBC. Hibernate and JPA do not have most these problems, but they’re not SQL APIs any longer, either.
Here are a couple of examples that we have been solving inside of jOOQ, so you don’t have to:
How to fetch generated keys in somedatabases
case DERBY: case H2: case MARIADB: case MYSQL: { try { listener.executeStart(ctx); result = ctx.statement().executeUpdate(); ctx.rows(result); listener.executeEnd(ctx); } // Yes. Not all warnings may have been consumed yet finally { consumeWarnings(ctx, listener); } // Yep. Should be as simple as this. But it isn't. rs = ctx.statement().getGeneratedKeys(); try { List<Object> list = new ArrayList<Object>(); // Some JDBC drivers seem to illegally return null // from getGeneratedKeys() sometimes if (rs != null) { while (rs.next()) { list.add(rs.getObject(1)); } } // Because most JDBC drivers cannot fetch all // columns, only identity columns selectReturning(ctx.configuration(), list.toArray()); return result; } finally { JDBCUtils.safeClose(rs); } }
How to handle BigInteger and BigDecimal
else if (type == BigInteger.class) { // The SQLite JDBC driver doesn't support BigDecimals if (ctx.configuration().dialect() == SQLDialect.SQLITE) { return Convert.convert(rs.getString(index), (Class) BigInteger.class); } else { BigDecimal result = rs.getBigDecimal(index); return (T) (result == null ? null : result.toBigInteger()); } } else if (type == BigDecimal.class) { // The SQLite JDBC driver doesn't support BigDecimals if (ctx.configuration().dialect() == SQLDialect.SQLITE) { return Convert.convert(rs.getString(index), (Class) BigDecimal.class); } else { return (T) rs.getBigDecimal(index); } }
How to fetch all exceptions from SQL Server
switch (configuration.dialect().family()) { case SQLSERVER: consumeLoop: for (;;) try { if (!stmt.getMoreResults() && stmt.getUpdateCount() == -1) break consumeLoop; } catch (SQLException e) { previous.setNextException(e); previous = e; } }
Convinced?
This is nasty code. And we have more examples of nasty code here, or in our source code.
All of these examples show that when working with JDBC, you’ll write code that you don’t want to / shouldn’t have to write in your application. This is why…