Статьи

2011. Отличный год для хранимых процедур!

Когда я впервые начал программировать 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!