Когда я впервые начал программировать Oracle PL / SQL для своего работодателя, я понял, что оказался в средневековье. Синтаксис, напоминающий мне об Ada и Pascal , сверхсильной типизации, компиляторах, которые находят ошибки компиляции примерно через 100 000 строк и т. Д. И т. Д. Я был молод (хорошо, я все еще), полон видений и идей, так что это было как трудно, как реальность может получить. Тем более, что у меня есть друзья, работающие в лабораториях Scala в Лозанне, Швейцария … развивающиеся для «будущего», а не «прошлого». Думаю, сегодня я присоединился бы к этой группе в фейсбуке .
Но у меня нет. На самом деле, я начал полностью менять свое мнение об этих архаичных кусочках логики глубоко в базе данных. Они очень живы! В нашей компании мы используем хранимые процедуры как средство взаимодействия различных систем с нашей огромной базой данных (несколько миллиардов записей в некоторых таблицах). Фактически, база данных стала промежуточным ПО между нашими собственными программными компонентами. На одном сервере (или из скрипта и т. Д.) Мы вызываем хранимую процедуру с параметром сообщения UDT. Процедура обновляет некоторые таблицы и помещает UDT в Oracle AQ . AQ используется другим сервером. Хотя это не стандартная архитектура J2EE, она работает очень хорошо и эффективно.
Хранимая процедура эволюции
Проверьте эту веселую сердитую статью 2004 года . Люди яростно ненавидели хранимые процедуры с большим энтузиазмом, и они делают это и сегодня. Но хранимые процедуры не исчезли. Наоборот, сегодня они еще важнее, поскольку базы данных, как правило, содержат все больше и больше данных, которые невозможно обработать на бизнес-уровне, если речь идет о многих операциях. Проверьте «более новые» базы данных, такие как MySQL , Postgres , H2 , HSQLDB и т. Д. Все они поставляются со своими собственными языками хранимых процедур. Так что речь идет не только об «архаичных», «злых» монолитах, предоставляемых Oracle, IBM (DB2) или Microsoft.
Итак, куда это идет?
В IT-бизнесе мы живем в быстро меняющемся мире. Никто из нас не знает, куда мы пойдем в ближайшие 10 лет. Я здесь не для того, чтобы обсуждать эту чрезмерно религиозную тему. Может быть, я не прав насчет бизнеса хранимых процедур. Но я люблю их за их производительность и близость к данным, которыми они манипулируют. И я думаю, что разделяю эту любовь со многими из вас там. То же самое относится и к SQL. Я здесь не для того, чтобы обсуждать несоответствие объектно-реляционного импеданса и чья это вина и т. Д. Это скучное и переоцененное обсуждение. В конце концов, мы тоже не знаем, куда движутся эти вещи.
jOOQ
Вместо этого я сосредотачиваюсь на чем-то другом. Недавно я опубликовал статью о dzone и на стороне сервера о jOOQ . У меня было много интересных, конструктивных и мотивирующих отзывов и первый коммиттер! Тем временем мы создали надежную поддержку хранимых процедур (и UDT), которые хорошо интегрируются в DSL jOOQ . Потому что одна из самых утомительных задач при использовании хранимых процедур — вызывать и отображать их на языке более высокого уровня, таком как Java и JDBC.
Генерация ПРОЦЕДУР как методов, ПАКЕТОВ как пакетов, UDT как классов
Как и многие другие инструменты персистентности, jOOQ поставляется с генерацией кода. Он также будет генерировать классы для ваших хранимых процедур и для ваших UDT. Проверьте этот пример PL / SQL пакета:
CREATE OR REPLACE PACKAGE library AS
-- A procedure checking the existence of an author and
-- providing the result as an OUT parameter
PROCEDURE p_author_exists (author_name VARCHAR2, result OUT NUMBER);
-- A function checking the existence of an author and
-- providing the result as a RETURN value
FUNCTION f_author_exists (author_name VARCHAR2) RETURN NUMBER;
END library;
И его сгенерированное (упрощенное) представление Java:
public final class Library {
// Invoke the stored procedure on a connection. The return value
// represents the OUT parameter.
public static BigDecimal pAuthorExists(
Connection connection,
String authorName)
throws SQLException { ... }
// Invoke the stored function on a connection
public static BigDecimal fAuthorExists(
Connection connection,
String authorName)
throws SQLException { ... }
// Use the stored function as a Field in jOOQ's query DSL
public static Field<BigDecimal> fAuthorExists(String authorName) { ... }
// Use the stored function as a Field taking another field as parameter
public static Field<BigDecimal> fAuthorExists(Field<String> authorName) { ... }
}
Помимо вызова хранимых функций и процедур непосредственно из вашего Java-приложения, вы также можете встроить их в запрос jOOQ, например:
// Let's say PERSONS is a table holding FIRST_NAME and LAST_NAME fields
// Then the following code will use those fields as generated Java objects
Factory create = new Factory(connection);
Field<BigDecimal> isAuthor = Library.fAuthorExists(LAST_NAME).as("is_author");
// Find persons born in 1981 and check whether they are authors
Result<?> result = create
.select(FIRST_NAME, LAST_NAME, isAuthor)
.from(PERSONS)
.where(YEAR_OF_BIRTH.equal(1981)).fetch();
for (Record record : result) {
System.out.println(
record.getValue(LAST_NAME) + " is an author : " +
record.getValue(isAuthor));
}
Вы можете встраивать хранимую функцию везде, где можете, в SQL, то есть в предложения SELECT, в предложения ORDER BY, предложения GROUP BY и т. Д. Например, вы можете фильтровать авторов непосредственно в базе данных, а не в коде Java:
Result<?> result = create
.select(FIRST_NAME, LAST_NAME)
.from(PERSONS)
.where(YEAR_OF_BIRTH.equal(1981))
.and(Library.fAuthorExists(LAST_NAME).equal(1))
.fetch();
Теперь, если у вас есть UDT в вашей хранимой процедуре (что гораздо более распространено, чем наличие UDT в таблицах), тогда вы можете легко сделать jOOQ для их сопоставления с миром Java. Допустим, у вас есть этот UDT:
CREATE TYPE u_address_type AS OBJECT (
street u_street_type, -- A nested UDT!
zip VARCHAR2(50),
city VARCHAR2(50),
country VARCHAR2(50)
)
И этот UDT используется в следующей автономной (т.е. не в пакете) хранимой процедуре:
CREATE OR REPLACE PROCEDURE p_check_address
(address IN OUT u_address_type);
Затем эти части кода Java (упрощенно для примера) генерируются для вас:
public class UAddressTypeRecord extends UDTRecordImpl<UAddressTypeRecord> {
// The nested UDT is referenced
public UStreetTypeRecord getStreet();
public String getZip();
public String getCity();
public String getCountry();
}
И вы сможете общаться с вашей базой данных, используя код, подобный следующему:
// Create the nested UDT structure
UAddressTypeRecord address = new UAddressTypeRecord();
UStreetTypeRecord street = new UStreetTypeRecord();
street.setStreet("Bahnhofstrasse");
street.setNo("1");
address.setStreet(street);
address.setZip("8001");
address.setCity("Zurich");
address.setCountry("Switzerland");
// Pass the UDT as a parameter to the database. Note how the OUT parameter
// is mapped as a return value to the method. If you have several OUT parameters
// A value object containing all qualified parameters is generated and returned
UAddressTypeRecord resultingAddress = Procedures.pCheckAddress(connection, address);
Вывод
В первые дни jOOQ я думал, что создаю альтернативу Hibernate или JPA, которые являются отличными инструментами, но они являются OR-картографами (см. Введение о религиозных верованиях). Так как я влюблен в SQL, я не хочу никакого отображения в мир OO. (Я также влюблен в ОО, поэтому он называется J- OO -Q).
Но в то же время я понял, что jOOQ — это нечто совершенно другое. Он может идти бок о бок с Hibernate, JPA или другими инструментами, обеспечивая гораздо более легкий доступ к вашей любимой (или ненавистной) функциональности базы данных, как вы привыкли в Java. С генерацией кода доступ к конструкциям конкретного поставщика становится настолько простым, насколько это возможно. Вы можете вызывать ваши хранимые процедуры, как если бы они были обычными методами Java, не зная подробностей о расширенных функциях JDBC. Вы можете сделать вашу базу данных неотъемлемой частью вашего приложения. Вы можете сделать хранимые процедуры веселыми! Черт возьми, ты можешь даже передумать, как я! ?
Проверьте на jOOQ для будущих версий , с большим количеством функций и баз данных в будущем. И, возможно, вы добавите странный патч для интеграции вашей любимой функции базы данных в jOOQ!