Учебные материалы

Доступ к данным через JPA

Этот урок освещает процесс создания приложения, которое использует Spring Data JPA для сохранения и получения данных из реляционной БД.

Что вы создадите

Вы создадите приложение, которое сохраняет Customer POJO в БД в ОЗУ.

Что вам потребуется

  • Примерно 15 минут свободного времени
  • Любимый текстовый редактор или IDE
  • JDK 6 и выше
  • Gradle 1.11+ или Maven 3.0+
  • Вы также можете импортировать код этого урока, а также просматривать web-страницы прямо из Spring Tool Suite (STS), собственно как и работать дальше из него.

Как проходить этот урок

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

Чтобы начать с нуля, перейдите в Настройка проекта.

Чтобы пропустить базовые шаги, выполните следующее:

Когда вы закончите, можете сравнить получившийся результат с образцом в gs-accessing-data-jpa/complete.

Настройка проекта

Для начала вам необходимо настроить базовый скрипт сборки. Вы можете использовать любую систему сборки, которая вам нравится для сборки проетов Spring, но в этом уроке рассмотрим код для работы с Gradle и Maven. Если вы не знакомы ни с одним из них, ознакомьтесь с соответсвующими уроками Сборка Java-проекта с использованием Gradle или Сборка Java-проекта с использованием Maven.

Создание структуры каталогов

В выбранном вами каталоге проекта создайте следующую структуру каталогов; к примеру, командой mkdir -p src/main/java/hello для *nix систем:

└── src
    └── main
        └── java
            └── hello

Создание файла сборки Gradle

Ниже представлен начальный файл сборки Gradle. Файл pom.xml находится здесь. Если вы используете Spring Tool Suite (STS), то можете импортировать урок прямо из него.

Если вы посмотрите на pom.xml, вы найдете, что указана версия для maven-compiler-plugin. В общем, это не рекомендуется делать. В данном случае он предназначен для решения проблем с нашей CI системы, которая по умолчанию имеет старую(до Java 5) версию этого плагина.

build.gradle

buildscript {
    repositories {
        maven { url "http://repo.spring.io/libs-release" }
        mavenLocal()
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.8.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'

jar {
    baseName = 'gs-accessing-data-jpa'
    version =  '0.1.0'
}

repositories {
    mavenLocal()
    mavenCentral()
    maven { url "http://repo.spring.io/libs-release" }
    maven { url "https://repository.jboss.org/nexus/content/repositories/releases" }
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("com.h2database:h2")
    testCompile("junit:junit")
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.11'
}

Spring Boot gradle plugin предоставляет множество удобных возможностей:

  • Он собирает все jar'ы в classpath и собирает единое, исполняемое "über-jar", что делает более удобным выполнение и доставку вашего сервиса
  • Он ищет public static void main() метод, как признак исполняемого класса
  • Он предоставляет встроенное разрешение зависимостей, с определенными номерами версий для соответсвующих Spring Boot зависимостей. Вы можете переопределить на любые версии, какие захотите, но он будет по умолчанию для Boot выбранным набором версий

Описание простой сущности

В этом примере, вы сохраняете Customer объекты, аннотированные как JPA сущность.

src/main/java/hello/Customer.java

package hello;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long id;
    private String firstName;
    private String lastName;

    protected Customer() {}

    public Customer(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return String.format(
                "Customer[id=%d, firstName='%s', lastName='%s']",
                id, firstName, lastName);
    }

}

Здесь у вас есть класс Customer с тремя атрибутами id, firstName и lastName. У вас также есть два конструктора. Конструктор по умолчанию существует только из-за JPA. Вы не будете его использовать напрямую, потому что он определен как protected. Другой конструктор вы будете использовать для создания экземпляра Customer, чтобы сохранить его в БД.

В этом уроке типичные методы получения и установки опущены для краткости.

Класс Customer аннотирован как @Entity, означая, что он является JPA сущностью. Из-за отсутствия аннотации @Table, предполагается, что сущность будет соответствовать таблице с названием Customer.

Свойство id класса Customer аннотировано как @Id, так что JPA распознает его как ID объекта. Свойство id также аннотировано как @GeneratedValue, означая, что ID должен генерироваться автоматически.

Другие два свойства, firstName и lastName остались не аннотированными. Это значит, что они будут соответствовать колонкам с такими же названиями и параметрами.

Метод tostring будет печатать свойства объекта.

Создание простых запросов

Spring Data JPA ориентирована на использование JPA для сохранения данных в реляционную БД. Наиболее полезной её возможностью является автоматическое создание реализаций репозитория во время выполнения из интерфейса.

Чтобы увидеть, как это работает, создайте интерфейс репозитория, который работает с сущностями Customer:

src/main/java/hello/CustomerRepository.java

package hello;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

public interface CustomerRepository extends CrudRepository<Customer, Long> {

    List<Customer> findByLastName(String lastName);
}

CustomerRepository расширяет CrudRepository интерфейс. Тип сущности и ID, Customer и Long, указаны в общих параметрах CrudRepository. Расширяя CrudRepository, CustomerRepository наследует несколько методов для работы с Customer, включая методы сохранения, удаления и поиска.

Spring Data JPA позволяет также определить и другие методы запросов, просто описав сигнатуры метода. В данном случае, таковым является метод findByLastName().

В типичном Java приложении, вы бы написали класс, который реализовывал CustomerRepository. Но это то, что делает Spring Data JPA мощным инструментом: вам не нужно писать реализацию интерфейса репозитория. Spring Data JPA создает реализацию на лету, когда вы запускаете приложение.

Приступим к работе и посмотрим, что он найдет!

Создание класса Application

Создайте класс Application со всеми компонентами.

src/main/java/hello/Application.java

package hello;

import java.util.List;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(Application.class);
        CustomerRepository repository = context.getBean(CustomerRepository.class);

        // save a couple of customers
        repository.save(new Customer("Jack", "Bauer"));
        repository.save(new Customer("Chloe", "O'Brian"));
        repository.save(new Customer("Kim", "Bauer"));
        repository.save(new Customer("David", "Palmer"));
        repository.save(new Customer("Michelle", "Dessler"));

        // fetch all customers
        Iterable<Customer> customers = repository.findAll();
        System.out.println("Customers found with findAll():");
        System.out.println("-------------------------------");
        for (Customer customer : customers) {
            System.out.println(customer);
        }
        System.out.println();

        // fetch an individual customer by ID
        Customer customer = repository.findOne(1L);
        System.out.println("Customer found with findOne(1L):");
        System.out.println("--------------------------------");
        System.out.println(customer);
        System.out.println();

        // fetch customers by last name
        List<Customer> bauers = repository.findByLastName("Bauer");
        System.out.println("Customer found with findByLastName('Bauer'):");
        System.out.println("--------------------------------------------");
        for (Customer bauer : bauers) {
            System.out.println(bauer);
        }

        context.close();
    }

}

В конфигурацию вам необходимо добавить аннотацию @EnableAutoConfiguration. Эта аннотация говорит Spring Boot конфигурировать JPA репозитории и сущности из текущего пакета. Поэтому для любых интерфейсов, которые расширяют org.springframework.data.repository.Repository, будут автоматически сгенерированы реализации. Расширяя JpaRepository, ваш интерфейс CustomerRepository транзитивно расширяет Repository. Следовательно, Spring Data JPA будет находить его и создавать для вас реализацию.

Application включает метод main(), который использует CustomerRepository в нескольких тестах. Для начала, он получает CustomerRepository из контекста приложения Spring. Затем он сохраняет несколько объектов Customer через метод save(). Далее, вызывается findAll() для получения всех объектов Customer из БД. Затем вызывается finfOne() для получения единственного customer по его ID. В заключении, вызывается findByLastName() для получения всех объектов с фамилией "Bauer".

Сборка исполняемого JAR

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

./gradlew build

Затем вы можете запустить JAR-файл:

java -jar build/libs/gs-accessing-data-jpa-0.1.0.jar

Если вы используете Maven, вы можете запустить приложение, используя mvn spring-boot:run, либо вы можете собрать приложение с mvn clean package и запустить JAR примерно так:

java -jar target/gs-accessing-data-jpa-0.1.0.jar
Процедура, описанная выше, создает исполняемый JAR. Вы также можете вместо него собрать классический WAR-файл.

Если вы используете Gradle, вы можете запустить ваш сервис из командной строки:

./gradlew clean build && java -jar build/libs/gs-accessing-data-jpa-0.1.0.jar
Если вы используете Maven, то можете запустить ваш сервис таким образом: mvn clean package && java -jar target/gs-accessing-data-jpa-0.1.0.jar.

Как вариант, вы можете запустить ваш сервис напрямую из Gradle примерно так:

./gradlew bootRun
С mvn - mvn spring-boot:run.

Вы должны увидеть следующее:

== Customers found with findAll():
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=2, firstName='Chloe', lastName='O'Brian']
Customer[id=3, firstName='Kim', lastName='Bauer']
Customer[id=4, firstName='David', lastName='Palmer']
Customer[id=5, firstName='Michelle', lastName='Dessler']

== Customer found with findOne(1L):
Customer[id=1, firstName='Jack', lastName='Bauer']

== Customer found with findByLastName('Bauer'):
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=3, firstName='Kim', lastName='Bauer']

Итог

Поздравляем! Вы только что написали простое приложение с использованием Spring Data JPA для сохранения объектов в БД и для их получения - и все это без написания реализации репозитория.

С оригинальным текстом урока вы можете ознакомиться на spring.io.

comments powered by Disqus