Аннотации в Java. Пишем свою ORM с блэк-джеком и вьюхами

Аннотации в Java – сильный инструмент, который способен существенно облегчить жизнь простых смертных разработчиков, а также сделать ее настоящим адом. Существует несколько полезных библиотек, работающих с помощью аннотаций, которые я использую практически каждый день. Но о них чуть позже. Давайте для начала разберемся что такое эти аннотации.

Что было до аннотаций?

Вы наверняка знакомы с таким фреймворком как JUnit. Начиная с 4 версии, в нем появилась поддержка аннотаций, с помощью которых вы можете помечать отдельные методы и целые классы. Например:

Но мало кто знает, что JUnit до 4 версии не использовал преимущества аннотаций, т.к. этот механизм еще не появился в Java. Вместо них фреймворк использовал популярный в то время и единственный вариант решения – naming pattern. В его основе лежали определенные правила для именования классов и методов. Например, JUnit 3 требовал, чтобы имя тестового метода строго начиналось со слова test. Этот паттерн содержал несколько недостатков, самый очевидный из которых – синтаксические ошибки. Например, разработчик мог допустить ошибку в названии метода, при этом компилятор никак не отреагирует на это, т.к. с его точки зрения код корректен, а фреймворк JUnit 3 не определит его как тестовый:

Благодаря появлению аннотаций в Java от использования этого паттерна отказались.

Аннотации. Базовый синтаксис

Аннотации (метаданные) – механизм включения в код информации, которая может быть легко использована в программе во время компиляции или выполнения. Вот так выглядит описание аннотации:

Наверняка вы обратите внимание, что сигнатура похожа на описание интерфейса, за исключением знака @. Так же для задания аннотации необходимо добавить дополнительные мета-аннотации @Target и @Retention. Первая указывает к чему можно применять данную аннотацию. Это может быть конструктор, поле, локальная переменная, метод, пакет, параметр и прочие элементы класса. Аннотация @Retention определяет уровень доступности аннотации. Всего таких уровней 3:

  1. SOURCE – доступно в исходном коде (игнорируется компилятором)
  2. CLASS – доступно в файлах класса (игнорируется JVM)
  3. RUNTIME – доступно во время выполнения (не игнорируется вообще)

Приведенная выше аннотация называется маркерной, т.к. ее тело пустое. Другой тип аннотации содержит нечто похожее на гибрид полей и методов. Покажем это на примере нашей тестовой аннотации:

Как вы можете видеть, в теле аннотации у нас появились две сущности, похожие на методы, с именами как у полей. Теперь при использовании аннотации мы может задавать эти параметры, при этом, если параметр name не задан, он будет автоматически определен значением по умолчанию, которое мы указали.

Таким образом, мы можем задавать параметры для каждого использования аннотации индивидуально. В методе test2() значение name будет присвоено значению по умолчанию.

Компилятор будет следить за тем, чтобы все параметры аннотации были вами определены. Они должны либо иметь значения по умолчанию, либо определяться во время использования аннотации. Причем есть ограничения. Вы не можете использовать null как значение по умолчанию для непримитивных типов.

Аннотации не поддерживают наследование, но вы можете использовать одни аннотации как параметры в других аннотациях. Это мы рассмотрим дальше. Да, пожалуй, прямо сейчас!

Написания собственной ORM

Создадим с помощью аннотаций свою собственную ORM (Object-Relational Mapping), которая будет создавать таблицы на основании Java-классов. Также нам понадобится написать обработчик аннотаций – инструмент для чтения аннотаций. Без него они просто мусор в коде.

Начнем с написания аннотаций

@DBTable представляет собой аннотацию со значением @Target(ElementType.TYPE), это значит что ее можно использовать для класса. В поле name мы будем задавать название для таблицы.

@Constraints представляет собой набор стандартных метаданных для таблицы таких, как первичный ключ, возможность записывать null.

@ColumnString и @ColumnInteger являются конкретными типами SQL. Для примера нам хватит двух, но можно расширить этот функционал и добавить новые типы. Они имеют внутри себя также поле name, которое будет содержать название колонки. Помимо этого также они содержат вложенную аннотацию @Constraints. Для большинства полей заполнять ее не нужно, т.к. в ней уже установлены значения по умолчанию, удовлетворяющие большинству полей.

Следующим шагом создадим класс – entity.

Вы могли обратить внимание, что мы используем аннотацию @ColumnString(30), передавая значение 30 без указания поля. Такая возможность доступна для Java в случае, если вы указываете только один параметр, который называется value. Так же для некоторых полей мы не указали название колонок. Мы сделали это умышленно. На следующем шаге, когда мы будем писать обработчик аннотаций, мы будем присваивать для таких колонок название из поля класса.

Итак, приступим к написанию обработчика. Мы будем использовать механизм Reflection, хотя это и не единственный способ обрабатывать аннотации, но другие варианты выходят за рамки этой статьи.

В данном обработчике мы получаем наш класс, находим в нем аннотации, с помощью них создаем SQL-запрос на создание таблицы и выполняем его на сервере БД. Я использовал PostgreSQL, вы можете использовать другую реляционную базу данных. Если вы забыли как выполнять запросы и подключать JDBC, то вам сюда.

В результате выполнения кода мы получим запрос, который легко выполняется на сервере.

Если вам “зашла” данная тема, то пишите комментарии, делитесь с друзьями в соц.сетях и тогда я напишу статью, в которой мы с вами создадим свой собственный JUnit!

Заключение

Как и обещал, вот пара ссылок на библиотеки, которые используют аннотации и очень полезны в повседневной работе.

5 comments On Аннотации в Java. Пишем свою ORM с блэк-джеком и вьюхами

  • Интересная статья, но ты не думал сделать код более ООП-шный? т.е. вынести TableCreator в отдельный класс и передавать в него либо класс лоадер, либо класс. Создать отдельно Класс Application и показать что это не сложно можно воткнуть в ваш Application. и избавиться от static методов. оставить их только в Application(Возможно будет более нагляднее).

    • Николай Грибанов

      Дмитрий, спасибо за комментарий! Ты совершенно прав! Я не стал выносить в отдельный класс для большей наглядности в статье, чтобы можно было “с листа” прочитать весь код

  • Константин

    Спасибо, Хорошая статья, все понятно, с содержательным и рабочим примером

  • Я немного в шоке с контента по аннотациям в интернете…
    Можете обьяснить Ваш пример? Ну создали Вы свою аннотацию и использовали ее в main методе…а что толку от Вашего примера? Вы же в main явно указываете класс который Вы парсите (annotations.Employee), и разбираете с помощью рефлекшн.
    А как мне пользоваться моей аннотацией в дальнейшем? Как ею будут пользоваться другие разрабы проекта? Им постоянно лезть в main класс и добавлять имена классов, где будет аннотация использоваться?

    А если я хочу свою аннотацию подключать как либу? Как мне знать на каких классах и пакетах ее будут использовать в бедующем?

    Просто крик души – десятая статья в интернете по созданию аннотаций с абсолютно бесполезной структурой парсинга конкретного класса в конкретно месте… А main метода в Spring например, или в тестировании (TestNG) нет совсем – где тогда описывать логику своей аннотации? Ну что за бред?

Оставить комментарий:

Ваш email не будет опубликован.

Site Footer