Статьи

Пример Spring Data JPA с Spring Boot

1. Введение

В этом посте мы продемонстрируем, как использовать мощные API-интерфейсы Spring Data JPA для взаимодействия с базой данных, базой данных H2 в памяти, для этого урока.

Spring Data JPA предлагает набор очень мощных и сильно абстрагированных интерфейсов, которые используются для взаимодействия с любой базовой базой данных. Базы данных могут быть MySQL, MongoDB, Elasticsearch или любой другой поддерживаемой базой данных. Другие преимущества для Spring Data JPA включают в себя:

  • Поддержка создания расширенных репозиториев на основе JPA Convention
  • Встроенная поддержка пагинации и динамическое выполнение запросов
  • Поддержка сопоставления сущностей на основе XML

В этом примере мы будем использовать базу данных H2 в памяти. Выбор базы данных не должен влиять на определения Spring Data, которые мы создадим, поскольку это является основным преимуществом Spring Data JPA. Это позволяет нам полностью отделить запросы к базе данных от логики приложения.

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

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

1
mvn archetype:generate -DgroupId=com.javacodegeeks.example -DartifactId=JCG-SpringDataJPA-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

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

Настройка проекта JPA с использованием Maven

Обратите внимание, что теперь у вас будет новый каталог с тем же именем, что и artifactId внутри выбранного каталога. Теперь не стесняйтесь открывать проект в вашей любимой IDE.

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

3. Maven Зависимости

Для начала нам нужно добавить соответствующие зависимости Maven в наш проект. Мы добавим следующую зависимость в наш файл pom.xml :

pom.xml

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.10.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>
 
<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <java.version>1.8</java.version>
</properties>
 
<dependencies>
 
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
 
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
 
  <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
  </dependency>
   
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
   
</dependencies>
 
<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

Найдите последние связанные с Spring зависимости здесь .

Обратите внимание, что мы также добавили сюда зависимость базы данных H2, а также ее область действия и время выполнения, поскольку данные H2 смываются сразу после остановки приложения. В этом уроке мы не будем фокусироваться на том, как на самом деле работает H2, но ограничимся API-интерфейсами Spring Data JPA. Вы также можете увидеть, как мы можем настроить встроенную консоль H2 с приложением Spring .

4. Структура проекта

Прежде чем мы продолжим работу и начнем работать над кодом для проекта, давайте представим здесь структуру projet, которая будет у нас после завершения добавления всего кода в проект:

Структура проекта

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

5. Определение модели

Мы начнем с добавления в наш проект очень простой модели Person . Его определение будет очень стандартным, как:

Person.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
 
@Entity
public class Person {
 
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private int age;
 
    public Person() {
    }
 
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    //standard getters and setters
 
    @Override
    public String toString() {
        return String.format("Person{id=%d, name='%s', age=%d}", id, name, age);
    }
}

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

Аннотация @Entity помечает этот POJO как объект, который будет управляться API-интерфейсами Spring Data, а его поля будут обрабатываться как столбцы таблицы (если не помечены как временные ).

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

6. Определение репозитория JPA

JPA предоставляет нам очень простой способ определения интерфейса репозитория JPA.

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

PersonRepository.java

1
2
3
4
5
6
7
import com.javacodegeeks.jpaexample.model.Person;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
 
@Repository
public interface PersonRepository extends JpaRepository<Person, Long> {
}

Хотя приведенное выше определение интерфейса пустое, у нас все же есть некоторые моменты, которые нам нужно понять:

  • Аннотация @Repository помечает этот интерфейс как Spring Bean, который инициализируется при запуске приложения. С этой аннотацией Spring позаботится об управлении бросками взаимодействия с базой данных исключений
  • Мы использовали Person в качестве параметра, чтобы показать, что этот интерфейс JPA будет управлять Person Entity
  • Наконец, мы также передали тип данных Long в качестве параметра. Это означает, что сущность Person содержит уникальный идентификатор типа Long

7. Создание Сервисного интерфейса

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

Вот определение контракта, которое мы будем использовать:

PersonService.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
import com.javacodegeeks.jpaexample.model.Person;
import java.util.List;
 
public interface PersonService {
 
    Person createPerson(Person person);
    Person getPerson(Long id);
    Person editPerson(Person person);
    void deletePerson(Person person);
    void deletePerson(Long id);
    List getAllPersons(int pageNumber, int pageSize);
    List getAllPersons();
    long countPersons();
}

Мы упомянули все четыре операции CRUD в этом контракте вместе с понятием пагинации.

API с pageSize по pageNumber важно создавать, когда мы представляем получение всех объектов из базы данных на основе pageSize и pageNumber . pageSize указывает число объектов, которые выбираются из базы данных, тогда как атрибут pageNumber действует как часть запроса, пропускаемая . Для подробного урока о том, как работает Pagination весной, прочитайте этот урок .

8. Предоставление услуг по внедрению

Мы будем использовать приведенное выше определение интерфейса для обеспечения его реализации, чтобы мы могли выполнять операции CRUD, связанные с Person мы определили ранее. Мы сделаем это здесь:

PersonServiceImpl.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import com.javacodegeeks.jpaexample.model.Person;
import com.javacodegeeks.jpaexample.repository.PersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
@Service
public class PersonServiceImpl implements PersonService {
 
    @Autowired
    private PersonRepository personRepository;
 
    @Override
    public Person createPerson(Person person) {
        return personRepository.save(person);
    }
 
    @Override
    public Person getPerson(Long id) {
        return personRepository.findOne(id);
    }
 
    @Override
    public Person editPerson(Person person) {
        return personRepository.save(person);
    }
 
    @Override
    public void deletePerson(Person person) {
        personRepository.delete(person);
    }
 
    @Override
    public void deletePerson(Long id) {
        personRepository.delete(id);
    }
 
    @Override
    public List<Person> getAllPersons(int pageNumber, int pageSize) {
        return personRepository.findAll(new PageRequest(pageNumber, pageSize)).getContent();
    }
 
    @Override
    public List<Person> getAllPersons() {
        return personRepository.findAll();
    }
 
    @Override
    public long countPersons() {
        return personRepository.count();
    }
}

Удивительно, что все реализации метода представляют собой только одну строку кода. Это показывает уровень абстракции, которые нам предоставляют репозитории JPA.

Большинство описанных выше операций просты для понимания. Главное, чтобы мы заметили, что в Repository мы никогда не предоставляли никаких методов, таких как getAllPersons() т. Д.! Тогда как эти методы появились вообще? Ответ, опять же, заключается в абстракции, которую нам предоставляют репозитории JPA. Все методы, такие как findAll() , delete() , save(...) и т. Д., JpaRepository в JpaRepository мы расширили в нашем определении интерфейса репозитория.

9. Использование CommandLineRunner

Чтобы протестировать весь код, который мы написали сейчас, вместе с CommandLineRunner взаимодействия с базой данных, мы будем использовать CommandLineRunner в нашем основном классе нашего приложения Spring Boot. CommandLineRunner запускается непосредственно перед CommandLineRunner метода main() для приложения Spring Boot, и поэтому он является идеальным пространством для выполнения любых шагов инициализации или тестирования кода.

Чтобы протестировать приложение, мы будем использовать служебный компонент для выполнения операций с базой данных в нашем классе:

pom.xml

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import com.javacodegeeks.jpaexample.model.Person;
import com.javacodegeeks.jpaexample.service.PersonService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class DataJpaApp implements CommandLineRunner {
 
  private static final Logger LOG = LoggerFactory.getLogger("JCG");
 
  @Autowired
  private PersonService service;
 
  public static void main(String[] args) {
    SpringApplication.run(DataJpaApp.class, args);
  }
 
  @Override
  public void run(String... strings) {
 
    LOG.info("Current objects in DB: {}", service.countPersons());
 
    Person person = service.createPerson(new Person("Shubham", 23));
    LOG.info("Person created in DB : {}", person);
 
    LOG.info("Current objects in DB: {}", service.countPersons());
 
    person.setName("Programmer");
    Person editedPerson = service.editPerson(person);
    LOG.info("Person edited in DB  : {}", person);
 
    service.deletePerson(person);
    LOG.info("After deletion, count: {}", service.countPersons());
  }
}

В приведенном выше примере кода мы просто сделали простые вызовы некоторых важных методов, которые мы создали в нашем сервисе, таких как создание некоторых данных и доступ к ним при последующих вызовах методов.

Теперь мы, наконец, запустим наш проект, используя сам Maven (снова будучи независимым от любой IDE для запуска нашего проекта).

10. Запуск приложения

С приложением maven легко запустить приложение, просто используйте следующую команду:

Запуск приложения

1
mvn spring-boot:run

Как только мы запустим проект, мы увидим следующий результат:

Выходные данные проекта JPA

Как и ожидалось, мы сначала создали несколько примеров данных и подтвердили их, вызвав вызов метода count() . Наконец, мы удалили данные и снова подтвердили их с помощью вызова метода count() .

11. Загрузите исходный код

Это пример API-интерфейсов Spring Boot и Spring Data JPA вместе с базой данных H2 в памяти.

Скачать
Вы можете скачать полный исходный код этого примера здесь: Spring Data JPA Example