Русский
Русский
English
Статистика
Реклама

Lombok

Перевод Сравнение Java-записей, Lombok Data и Kotlin data-классов

16.06.2021 20:20:54 | Автор: admin

Несмотря на то что все три решения позволяют бороться с бойлерплейт кодом, общего между ними довольно мало. У записей более сильная семантика, из которой вытекают их важные преимущества. Что часто делает их лучшим выбором, хотя и не всегда.

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

class Range {private final int low;private final int high;public Range(int low, int high) {this.low = low;this.high = high;}public int getLow() {return low;}public int getHigh() {return high;}@Overridepublic boolean equals(Object o) {if (this == o)return true;if (o == null || getClass() != o.getClass())return false;Range range = (Range) o;return low == range.low &&high == range.high;}@Overridepublic int hashCode() {return Objects.hash(low, high);}@Overridepublic String toString() {return "[" + low + "; " + high + "]";}}

в одну строчку кода:

//          это компоненты записи (components)record Range (int low, int hight) { }

Конечно, аннотации @Data и @Value из Lombok обеспечивают аналогичную функциональность с давних пор, хоть и с чуть большим количеством строк:

@Dataclass Range {private final int low;private final int high;}

А если вы знакомы с Kotlin, то знаете, что то же самое можно получить, используя data-класс:

data class Range(val low: Int, val high: Int)

Получается, что это одно и то же? Нет. Уменьшение бойлерплейт кода не является целью записей, это следствие их семантики.

К сожалению, этот момент часто упускается. Об уменьшении бойлерплейт кода говорят много, так как это очевидно и легко демонстрируется, но семантика и вытекающие из нее преимущества остаются незамеченными. Официальная документация не помогает в ней тоже все описывается под углом бойлерплейта. И хотя JEP 395 лучше объясняет семантику, но из-за своего объема все довольно расплывчато, когда дело доходит до описания преимуществ записей. Поэтому я решил описать их в этой статье.

Семантика записей (records)

В JEP 395 говорится:

Записи (records) это классы, которые действуют как прозрачные носители неизменяемых данных.

Таким образом, создавая запись, вы говорите компилятору, своим коллегам, всему миру, что указанный тип хранит данные. А точнее, иммутабельные (поверхностно) данные с прозрачным доступом. Это основная семантика все остальное вытекает из нее.

Если такая семантика не применима к нужному вам типу, то не используйте записи. А если вы все равно будете их использовать (возможно, соблазнившись отсутствием бойлерплейта или потому что вы думаете, что записи эквивалентны @Data / @Value и data-классам), то только испортите свою архитектуру, и велики шансы, что это обернется против вас. Так что лучше так не делать.

(Извините за резкость, но я должен был это сказать.)

Прозрачность и ограничения

Давайте подробнее поговорим о прозрачности (transparency). По этому поводу у записей есть даже девиз (перефразированный из Project Amber):

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

Для реализации этого необходимы ряд ограничений:

  • для всех компонент должны быть аксессоры (методы доступа) с именем, совпадающим с именем компонента, и возвращающие такой же тип, как у компонента (иначе API не будет моделировать состояние)

  • должен быть конструктор с параметрами, которые соответствуют компонентам записи (так называемый канонический конструктор; иначе API не будет моделировать состояние)

  • не должно быть никаких дополнительных полей (иначе API не будет моделировать состояние)

  • не должно быть наследования классов (иначе API не будет моделировать состояние, так как некоторые данные могут находиться в другом месте за пределами записи)

И Lombok и data-классы Kotlin позволяют создавать дополнительные поля, а также приватные "компоненты" (в терминах записей Java, а Kotlin называет их параметрами первичного конструктора). Так почему же Java относится к этому так строго? Чтобы ответить на этот вопрос, нам понадобится вспомнить немного математики.

Математика

Множество (set) это набор некоторых элементов. Например, можно сказать, что C это множество всех цветов {синий, золотой, ...}, а N множество всех натуральных чисел {0, 1, ...}. Конечное множество {-2147483648, ..., 0, ..., 2147483647} это то, что в Java мы называем типом int. А если добавить к этому множеству null, то получим Integer. Аналогично бесконечное множество всех возможных строк (плюс null) мы называем String.

Итак, как вы поняли, тип это множество, значения которого допустимы для данного типа. Это также означает, что теория множеств "раздел математики, в котором изучаются общие свойства множеств" (как говорит Википедия), связана с теорией типов "академическим изучением систем типов" (аналогично), на которую опирается проектирование языков программирования.

class Pair {private final int first;private final int second;}

Можно назвать соответствующее множество Pair, и это будет работать. Давайте немного углубимся, так как теперь мы знаем о множествах. В частности, мы видим, что у нас получилось сочетание всех целых чисел со всеми целыми числами. В теории множеств это называется произведением и записывается как int int (каждый тип в произведении называется операндом).

Это здорово, потому что теория множеств может многое сказать о применении функций к произведениям. Одним из аспектов этого является то, как функции, работающие с одним операндом, могут комбинироваться с функциями, работающими с несколькими операндами, и какие свойства функций (инъективные, биективные и т. д.) остаются нетронутыми.

// given: bijective function from int to intIntUnaryOperator increment =i -> i == Integer.MAX_VALUE ? Integer.MIN_VALUE : ++i;// then: combining two `increment`s yields a bijective function//       (this requires no additional proof or consideration)UnaryOperator<Pair> incrementPair =pair -> new Pair(increment.applyAsInt(pair.first()),increment.applyAsInt(pair.second()));

Вы обратили внимание на аксессоры Pair::first и Pair::second? Их не было в классе выше, поэтому их пришлось добавить. Иначе нельзя было бы применить функции к отдельным компонентам / операндам, и использовать Pair в качестве пары целых чисел. Аналогично, но с другой стороны, мне нужен конструктор, принимающий в качестве аргументов оба целых числа, чтобы можно было воспроизвести pair.

В общем случае, чтобы применить теорию множеств к типу так, как я упоминал выше, ко всем его операндам должен быть доступ и должен существовать способ превратить кортеж операндов в экземпляр. Если верно и то и другое, то теория типов называет такой тип "тип-произведение" (а его экземпляры кортежами), и с ними можно делать несколько интересных вещей.

На самом деле записи лучше кортежей. В JEP 395 говорится:

Записи можно рассматривать как номинативные кортежи.

Где "номинативность" означает, что записи идентифицируются по их именам, а не по их структуре. Таким образом, два типа записей, которые моделируют int int, например, Pair(int first, int second) и Range(int low, int high) будут разными типами. А также обращение к компонентам записи идет не по индексу (range.get1()), а по имени (record.low()).

Следствия

Я хочу донести до вас следующую мысль: записи стремяться стать типом-произведением и, чтобы это работало, все их компоненты должны быть доступны. То есть не может быть скрытого состояния, и должен быть конструктор, принимающий все компоненты. Именно поэтому записи являются прозрачными носителями неизменяемых данных.

Итак, если подытожить:

  • Аксессоры (методы доступа) генерируются компилятором.

  • Мы не можем изменять их имена или возвращаемый тип.

  • Мы должны быть очень осторожны с их переопределением.

  • Компилятор генерирует канонический конструктор.

  • Наследование отсутствует.

Преимущества записей

Большинство преимуществ, которые мы получаем от алгебраической структуры, связаны с тем, что аксессоры вместе с каноническим конструктором позволяют разбирать и пересоздавать экземпляры записей структурированным образом без потери информации.

Деструктурирующие паттерны

if (range instanceof Range(int low, int high) && high < low)    return new Range(high, low);

Благодаря полной прозрачности записей мы можем быть уверены, что не пропустим скрытое состояние. Это означает, что разница между range и возвращаемым экземпляром это именно то, что вы видите: low и high меняются местами не более того.

Блок with

Range range = new Range(5, 10);// SYNTAX IS MADE UP!Range newRange = range with { low = 0; }// range: [5; 10]// newRange: [0; 10]

И, как и раньше, мы можем рассчитывать на то, что newRange будет точно таким же, как и range за исключением low: нет скрытого состояния, которое мы не перенесли. И синтаксически здесь все просто:

  • объявить переменные для компонент (например, low, high) и присвоить значения с помощью аксессоров

  • выполнить блок with

  • передать переменные в канонический конструктор

(Обратите внимание, что этот функционал далек от реальности и может быть не реализован или быть значительно изменен.)

Сериализация

Для представления объекта в виде потока байт, JSON / XML-документа или в виде любого другого внешнего представления и обратной конвертации, требуется механизм разбивки объекта на его значения, а затем сборки этих значений снова вместе. И вы сразу же можете увидеть, как это просто и хорошо работает с записями. Они не только раскрывают все свое состояние и предлагают канонический конструктор, но и делают это структурированным образом, что делает использование Reflection API очень простым.

Более подробно том, как записи изменили сериализацию, слушайте в подкасте Inside Java Podcast, episode 14 (также в Spotify). Если вы предпочитаете короткие тексты, то читайте твит.

Бойлерплейт код

Вернемся на секунду к бойлерплейту. Как говорилось ранее, чтобы запись была типом-произведением, должны выполняться следующие условия:

  • канонический конструктор

  • аксессоры (методы доступа)

  • отсутствие наследования

Я не сказал об этом явно, но было бы неплохо, если (0, 0) = (0, 0), то есть должна быть правильная реализация equals, которая сразу же требует реализации hashCode.

И все это генерируется компилятором (а также еще toString) не столько для того, чтобы избавить нас от написания этого кода, сколько потому, что это естественное следствие алгебраической структуры.

Недостатки записей

Семантика записей ограничивает возможности по работе с классами. Как уже говорилось, вы не можете добавлять скрытое состояние через добавление полей, не можете переименовывать аксессоры и изменять тип возвращаемого значения и, вероятно, не должны менять возвращаемое ими значение. Записи также не позволяют изменять значения компонент, так как соответствующие им поля объявлены final. И отсутствует наследование классов (хотя вы можете реализовать интерфейсы).

Так что же делать, если вам все это нужно? Тогда записи вам не подходят и вместо них следует использовать обычный класс. Даже если изменив только 10% функциональности, вы получите 90% бойлерплейта, от которого вы бы избавились с помощью записей.

Преимущества Lombok @Data/@Value

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

(При этом я не рекламирую Lombok. Он в значительной степени полагается на внутренние API компилятора, которые могут измениться в любой момент, а это означает, что проекты, использующие его, могут сломаться при любом незначительном обновлении Java. То, что он много делает для скрытия технического долга от своих пользователей, тоже не очень хорошо.)

Преимущества data-классов Kotlin

Вот что говорится в документации о data-классах:

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

Вы можете видеть, что здесь также присутствует семантика хранения данных, но она довольно слабая, и основное внимание уделяется получению функциональности, то есть генерации кода. Действительно, data-классы предлагают больше возможностей по работе с классами, чем записи (мутабельные "компоненты", скрытое состояние, ...), но в отличие от Lombok не все (не могут расширяться, нельзя создавать свой метод copy, ...). С другой стороны, data-классы не дают сильных гарантий как записи, поэтому Kotlin не может построить на их основе аналогичную функциональность. Это разные подходы с разной ценой и выгодой.

Некоторые указывали на @JvmRecord в Kotlin как на большую ошибку: "Видите, data-классы могут быть записями шах и мат ответ" (я перефразировал, но смысл был такой). Если у вас возникли такие же мысли, то я прошу вас остановиться и подумать на секунду. Что именно это дает вам?

Data-класс должен соблюдать все правила записи, а это значит, что он не может делать больше, чем запись. Но Kotlin все еще не понимает концепции прозрачных кортежей и не может сделать с @JvmRecord data-классом больше, чем с обычным data-классом. Таким образом, у вас есть свобода записей и гарантии data-классов данных худшее из обоих миров.

Для чего тогда нужен @JvmRecord? Просто для совместимости. Как говорится в proposal:

В Kotlin нет большого смысла использовать JVM-записи, за исключением двух случаев:

  • перенос существующей Java-записи на Kotlin с сохранением ее ABI;

  • генерация атрибута класса записи с информацией о компоненте записи для класса Kotlin для последующего чтения каким-либо фреймворком, использующим Java reflection для анализа записей.

Рефлексия

Записи не лучше и не хуже рассмотренных альтернатив или других вариантов с аналогичным подходом, таких как case-классы Scala. У них действительно сильная семантика с твердым математическим фундаментом, которая хотя и ограничивает возможности по проектированию классов, но приносит мощные возможности, которые, в противном, случае были бы невозможны или, по крайней мере, не столь надежны.

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


В преддверии старта курса "Java Developer. Professional" приглашаю всех желающих на бесплатный демоурок по теме: Система получения курсов валют ЦБ РФ.

Подробнее..

Из песочницы Билдеры или конструкторы? Рассуждаем вслух

16.08.2020 20:19:14 | Автор: admin
Всем привет! Хочу порассуждать над целесообразностью использования билдеров для не сложных объектов.

Для упрощения буду использовать аннотации lombok'a:

@Value
@Builder

Недолго погуглив, получаем, что builder Отделяет конструирование сложного объекта от его представления так, что в результате одного и того же процесса конструирования могут получаться разные представления. Только ли для сложных объектов?

Рассмотрим на простом примере:

@Valuepublic class Info {    @Nullable String uuid;    @Nullable String email;    @Nullable String phone;}

Довольно таки простой класс. На деле получаем иммутабельный объект, который инициализируется через конструктор.

Но, как мы видим, все поля nullable, и создание такие объектов будет выглядеть не очень красиво:

        final Info info1 = new Info(null, "email@email.com", "79998888888");        final Info info2 = new Info("3d107928-d225-11ea-87d0-0242ac130003", null, null);        final Info info3 = new Info("3d107928-d225-11ea-87d0-0242ac130003 ", "email@email.com", null);...

Безусловно, есть варианты:

  1. Объекты, где немного полей разных типов, можно завезти несколько конструкторов. Но это не решает проблему класса выше.
  2. Использовать setter'ы субьективно, нагромождает код.


А что с билдером?

@Value@Builderpublic class Info {    @Nullable String uuid;    @Nullable String email;    @Nullable String phone;}

Мы получаем весьма элегантное построение не сложного объекта:

        final Info info1 = Info.builder()                .uuid("3d107928-d225-11ea-87d0-0242ac130003")                .phone("79998888888")                .build();        final Info2 info2 = Info.builder()                .email("email@email.com")                .phone("79998888888")                .build();...}

Однако, для использование в проекте jackson'а, необходимо дополнить наш клас, чтобы он успешно десериализовывался:

@Value@Builder(builderClassName = "InfoBuilder")@JsonDeserialize(builder = Info.InfoBuilder.class)public class Info {    @Nullable String uuid;    @Nullable String email;    @Nullable String phone;    @JsonPOJOBuilder(withPrefix = "")    public static class InfoBuilder {    }}

Получаем свои плюсы и минусы за оба подхода:

builder:

+
1. Код становится лаконичнее.
3. null в параметрах контруктора не бросается в глаза.
2. Меньше шанс перепутать параметры одного типа.
-
1. Создаем лишний объект, который GC в целом благополучно уберет, но забывать об этом не стоит.
2. При необходимости использовать jacson нагромоздим класс.

конструктор:

+
1. Минимально нагромождает наш класс, никакой воды.
2. Нет создания лишних объектов.
-
1. Весьма часто в конструктор такого объекта будет прилетать null.
2. Есть вероятность ошибится, когда кто-то будет вносить изменения в код.

Итог


Опираясь на свой опыт склоняюсь к использованию билдеров. Плата за это не высока, а на выходе имеем код, который приятно читать.

И конечно же, пишите тесты, чтобы избежать 2-го отрицательного пункта использования контрукторов.

P.S. Это моя первая статья, буду благодарен конструктивной критике и комментариям.
Подробнее..
Категории: Java , Lombok , Рассуждения

Перевод Написание более читаемого кода с помощью Project Lombok

17.10.2020 18:18:02 | Автор: admin

В течение многих лет мы написали на Java много шаблонного кода, такого как методы getter, setter, equals, hashCode и т.д..В некоторых случаях это вызывает проблемы с точки зрения чистого и читаемого кода.

В таких ситуациях Project Lombok спасает нас.Кроме того, с помощью Lombok вы сможете уделять больше времени бизнес-логике.

Фото Фачи Марина на UnsplashФото Фачи Марина на Unsplash

В этом руководстве я расскажу вам о Project Lombok и приведу пример использования Spring Boot.

  • Здесь используются проекты Spring Boot и Spring Data для разработки REST API.

  • Mavenиспользуется для автоматизации процесса сборки.

  • Используется типичный подход к разработке на основе предметной области (domain-driven), разделяющий классы модели, репозитория, сервиса и контроллера.

Вы можете скачать исходный код проекта по этойссылке на GitHub

Что такое Project Lombok?

Я скопировал описание проекта Ломбок отсюда(Официальный сайт).Я не мог бы придумать лучшего предложения, чтобы объяснить это :).Project Lombok - это java-библиотека, которая автоматически подключается к вашему редактору и инструментам сборки, делая вашу java более пикантной.

Фото Канвара на SlideshareФото Канвара на Slideshare

Никогда больше не пишите еще один метод getter или equals, с одной аннотацией ваш класс получает полнофункциональный конструктор, автоматизируется создание переменных журналирования и многое другое.

Project Lombok использует аннотации, чтобы избежать написания шаблонного кода.В некоторых случаях всего пять строк кода могут заменить сотни строк.

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

Класс Book, созданный с использование Lombok

package com.medium.libraryinfosystem.model.lombok;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;import javax.persistence.Table;/** * @author ragcrix */@Data@AllArgsConstructor@NoArgsConstructor@Entity@Tablepublic class Book {    @Id    @GeneratedValue    private Long id;    private String writer;    private String name;    private String genre;    private String year;}

Описание используемых аннотаций согласноLombokJavaDoc:

  • @Data: Создает методы getter для всех полей, метод toString и реализации hashCode и equals, которые проверяют * все не transient поля.Также будет генерировать конструктор, а также методы setter для всех не finalполей.

  • @AllArgsConstructor: Создает конструктор со всеми аргументами.

  • @NoArgsConstructor: Создает конструктор без аргументов.

  • @Slf4j: Заставляет ломбок генерировать поле logger.

Ниже Вы можете увидеть класс модели Book, которая также создана без использования Lombok.

Класс Book, созданный с использование стандартного кода

package com.medium.libraryinfosystem.model.classic;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;import javax.persistence.Table;import java.util.Objects;/** * @author ragcrix */@Entity@Tablepublic class Book {    @Id    @GeneratedValue    private Long id;    private String writer;    private String name;    private String genre;    private String year;    public Book() {    }    public Book(Long id, String writer,                String name, String genre, String year) {        this.id = id;        this.writer = writer;        this.name = name;        this.genre = genre;        this.year = year;    }    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getWriter() {        return writer;    }    public void setWriter(String writer) {        this.writer = writer;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getGenre() {        return genre;    }    public void setGenre(String genre) {        this.genre = genre;    }    public String getYear() {        return year;    }    public void setYear(String year) {        this.year = year;    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        Book book = (Book) o;        return Objects.equals(id, book.id) &&                Objects.equals(writer, book.writer) &&                Objects.equals(name, book.name) &&                Objects.equals(genre, book.genre) &&                Objects.equals(year, book.year);    }    @Override    public int hashCode() {        return Objects.hash(id, writer, name, genre, year);    }    @Override    public String toString() {        return "Book{" +                "id=" + id +                ", writer='" + writer + '\'' +                ", name='" + name + '\'' +                ", genre='" + genre + '\'' +                ", year='" + year + '\'' +                '}';    }}

Project Lombok также обеспечивает удобство ведения журнала следующим образом.Например, вы можете использовать@Slf4jаннотацию для ведения журнала.

Аннотация @ Slf4j используется для ведения журнала

package com.medium.libraryinfosystem.service.impl;import com.medium.libraryinfosystem.model.lombok.Book;import com.medium.libraryinfosystem.repository.BookRepository;import com.medium.libraryinfosystem.service.BookService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;/** * @author ragcrix */@Slf4j@Servicepublic class BookServiceImpl implements BookService {    @Autowired    private BookRepository bookRepository;    @Override    public Book findByName(String name) {        log.info("inside findByName()");        return bookRepository.findByName(name);    }    @Override    public List<Book> findAll() {        log.info("inside findAll()");        return bookRepository.findAll();    }    @Override    public Book save(Book book) {        log.info("inside save()");        return bookRepository.save(book);    }    @Override    public void delete(Book book) {        log.info("inside delete()");        bookRepository.delete(book);    }}

Как установить Lombok

Мы можем легко интегрировать Lombok в нашу IDE (IntelliJ, Eclipse и т.д.).

В этом разделе я расскажу вам об установке Lombok для IntelliJ Idea.Вы можете перейтисюда,чтобы увидеть другие варианты установки.

  1. Во-первых, вы должны выбрать Lombok при инициализации проекта Spring Boot, как показано ниже.

Инициализация проектаИнициализация проекта

2. После этого, если вы не добавили плагин Lombok в свою IDE раньше, IntelliJ задаст вам вопрос.Вы должны выбрать плагин Lombok и нажать кнопку ОК, чтобы установить плагин в вашу среду IDE.

Установка Lombok Установка Lombok

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

Плагин Lombok установленПлагин Lombok установлен

Информационная система библиотеки

В этом руководстведля кодирования, сборки и тестирования REST APIиспользуютсяIntelliJ IDEA,Maven иPostman.Я работаю вWindows 10с помощью PowerShell.

Все программное обеспечение написано на Java, с использованием Spring 5.2 и Spring Boot 2.2.2, база данных H2 в памяти используется для сохранения данных.

Далее я предполагаю, что у вас установлены JDK 13.0, IntelliJ,Maven и Postman.

Информационная система библиотеки предоставляет информацию о книгах и позволяет добавлять новые книги в систему или удалять книги из системы.

Фото Иво Раиньи на UnsplashФото Иво Раиньи на Unsplash

Моя цель здесь - показать, как использовать Lombok простым способом без перетасовки бизнес-логики.

  • Операция POST используется для добавления книг в систему.

  • Таблицаbookв базе данных H2 в памяти содержит данные о названии каждой книги, авторе, жанре и году.

  • Все, что хранится, получается и возвращается службой, форматируется как JSON.

Модель Book

Эта модель имеет 5 полей: id, name, writer, genre и year.

package com.medium.libraryinfosystem.model.lombok;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;import javax.persistence.Table;/** * @author ragcrix */@Data@AllArgsConstructor@NoArgsConstructor@Entity@Tablepublic class Book {    @Id    @GeneratedValue    private Long id;    private String writer;    private String name;    private String genre;    private String year;}

Все использованные аннотации Lombok объяснены выше.

Репозиторий Book

Вы можете легко создать интерфейс, расширяющийCrudRepository,с помощью Spring Data Project.При этом нет необходимости реализовывать этот интерфейс :).

package com.medium.libraryinfosystem.repository;import com.medium.libraryinfosystem.model.lombok.Book;import org.springframework.data.repository.CrudRepository;import java.util.List;/** * @author ragcrix */public interface BookRepository extends CrudRepository<Book, Long> {    Book findByName(String name);    List<Book> findAll();    Book save(Book book);    void delete(Book book);}

В BookRepository есть четыре метода.Это простыеоперацииCRUD(создание, чтение, обновление, удаление).

Сервис Book

Сервис Bookимеет четыре метода.Просто эти методы выдают список книг по некоторым критериям, сохраняют или обновляют данные о книге и удаляют книгу.

package com.medium.libraryinfosystem.service;import com.medium.libraryinfosystem.model.lombok.Book;import java.util.List;/** * @author ragcrix */public interface BookService {    Book findByName(String name);    List<Book> findAll();    Book save(Book book);    void delete(Book book);}

Вы можете увидеть реализацию этого интерфейса ниже.

package com.medium.libraryinfosystem.service.impl;import com.medium.libraryinfosystem.model.lombok.Book;import com.medium.libraryinfosystem.repository.BookRepository;import com.medium.libraryinfosystem.service.BookService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;/** * @author ragcrix */@Servicepublic class BookServiceImpl implements BookService {    @Autowired    private BookRepository bookRepository;    @Override    public Book findByName(String name) {        return bookRepository.findByName(name);    }    @Override    public List<Book> findAll() {        return bookRepository.findAll();    }    @Override    public Book save(Book book) {        return bookRepository.save(book);    }    @Override    public void delete(Book book) {        bookRepository.delete(book);    }}

Book контроллер

BookController.java- это класс REST контроллера, который имеет два методас аннотацией @GetMapping,один с @PostMappingи один с@DeleteMapping.

package com.medium.libraryinfosystem.controller;import com.medium.libraryinfosystem.dto.BookDTO;import com.medium.libraryinfosystem.model.lombok.Book;import com.medium.libraryinfosystem.service.BookService;import com.medium.libraryinfosystem.util.ObjectMapperUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import java.util.List;/** * @ragcrix */@RestController@RequestMapping("/books")public class BookController {    @Autowired    private BookService bookService;    @GetMapping(value = "/")    public List<BookDTO> getAllBooks() {        return ObjectMapperUtils.mapAll(bookService.findAll(), BookDTO.class);    }    @GetMapping(value = "/byName/{bookName}")    public BookDTO getBookByName(@PathVariable("bookName") String bookName) {        return ObjectMapperUtils.map(bookService.findByName(bookName), BookDTO.class);    }    @PostMapping(value = "/save")    public ResponseEntity<?> saveOrUpdateBook(@RequestBody BookDTO bookDTO) {        bookService.save(ObjectMapperUtils.map(bookDTO, Book.class));        return new ResponseEntity("Book added successfully", HttpStatus.OK);    }    @DeleteMapping(value = "/delete/{bookName}")    public ResponseEntity<?> deleteBookByName(@PathVariable String bookName) {        bookService.delete(bookService.findByName(bookName));        return new ResponseEntity("Book deleted successfully", HttpStatus.OK);    }}

Конфигурация базы данных

Чтобы подключиться к базе данных H2, отредактируйте настройки вфайлеapplication.properties,который расположен в директори resources.Достаточно следующей конфигурации.

# Serverserver.port=9090# Enabling H2 Consolespring.h2.console.enabled=true# Datasourcespring.datasource.url=jdbc:h2:mem:testdbspring.datasource.driverClassName=org.h2.Driverspring.datasource.username=saspring.datasource.password=# JPAspring.jpa.show-sql=truespring.jpa.generate-ddl=truespring.jpa.database-platform=org.hibernate.dialect.H2Dialect

После запуска информационной системы библиотеки вы можете легко получить доступ к базе данных H2 по этойссылке.

Примечание. Каждый раз, когда запускается этот проект, будут выполняться скрипт в файле data.sql, который находится в директории resources.Конечно, для этого используется Spring Data.

insert into book values(1, 'SCI-FI', 'Frankenstein', 'Mary Shelley', '1818'); insert into book values(2, 'Fantastic', 'Harry Potter', 'J. K. Rowling', '1997');

Запуск службы и тестирование с помощью Postman

Вы можете легко запустить этот проект для тестирования службы REST.

Если вы хотите добавить книгу в информационную систему библиотеки, вы можете использоватьфайл data.jsonв корне проекта.

Запустите проект в IntelliJЗапустите проект в IntelliJ

После запуска проекта вы можете протестировать службу REST.Для этого откройте Postman, введите URL-адрес и выберитеметодGET,какпоказанониже.

Список книгСписок книг

Снова введите ссылку с названием книги в базу данных.

ФранкенштейнФранкенштейн

Вы можете добавить книгу в систему с помощьюметодаPOSTв Postman.Сначала вы должны ввестипару ключ-значение заголовкаContent-Type:application/json.Во-вторых, во вкладке body вы должны ввести информацию о книге, которую вы хотите сохранить в базе данных в формате JSON.

Добавление книгиДобавление книги

Если вы хотите удалить книгу, вам следует выбратьметодDELETEв Postman и добавить название книги, которую вы хотите удалить, в конец ссылки.

Удаление книгиУдаление книги

Ресурсы

Примечание. Вэтом руководстве рассматривается сочетание Spring Data, H2 в памяти и REST API.В проекте нет проверки для полей объекта.

Спасибо за прочтение!Ваши мысли очень ценны для меня.Пожалуйста, поделитесь.

Подробнее..
Категории: Java , Spring-boot , Lombok

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru