1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/GiteeOS-springboot-guide

Клонировать/Скачать
springboot-jpa.md 14 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 04.06.2025 10:20 f496caa

Понимание JPA в Spring Boot: правильное использование

JPA кажется простым в освоении, но при детальном рассмотрении оказывается, что в нем много нюансов. Большинство материалов о JPA в интернете не являются исчерпывающими, а используемые версии часто устарели. В данной статье я собрал наиболее важные аспекты JPA, которые помогут новичкам и тем, кто уже знаком с JPA, использовать его правильно в реальных проектах. Статья основана на официальной документации Spring Data JPA, полная документация доступна для ознакомления.

Проект построен на основе последней версии Spring Boot 2.1.9.RELEASE (на момент написания статьи).

1. Зависимости

Для работы с JPA нам потребуются следующие зависимости:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2. Конфигурация подключения к базе данных и JPA

Используя встроенную h2-базу данных, при запуске проекта база данных будет автоматически создана.В конфигурации важно обратить внимание на параметр spring.jpa.hibernate.ddl-auto=create.

Доступны следующие значения для этого параметра:

  1. create: при каждом перезапуске проекта таблицы будут пересозданы, что приведет к потере данных.
  2. create-drop: при каждом запуске проекта таблицы будут созданы, а при завершении работы проекта — удалены.
  3. update: при каждом запуске проекта таблицы будут обновлены.
  4. validate: проверка структуры таблиц, без изменения базы данных. Однако, никогда не используйте DDL для автоматического создания структуры таблиц в продакшн-среде, обычно рекомендуется вручную писать SQL-запросы и использовать Flyway для этих целей.
# URL адреса базы данных
spring.datasource.url=jdbc:h2:mem:jpa-demo
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.platform=h2
spring.datasource.driverClassName=org.h2.Driver

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
# Выводить SQL-запросы в лог
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.open-in-view=false
server.port=8080
# Включить консоль H2
spring.h2.console.enabled=true

3. Энтити класс

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

import lombok.Data;
import lombok.NoArgsConstructor;

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

@Entity
@Data
@NoArgsConstructor
public class Person {
``````java
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String name;
private Integer age;

public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
}

Как проверить, что вы правильно выполнили эти три шага? Очень просто, запустите проект и проверьте данные. Если вы видите, что в логах выводятся SQL-запросы для создания таблицы, и таблица действительно создана в базе данных, значит вы успешно завершили предыдущие три шага.

Выводимые в консоли SQL-запросы могут выглядеть следующим образом:

drop table if exists person;
CREATE TABLE `person` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `age` int(11) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
alter table person add constraint UK_p0wr4vfyr2lyifm8avi67mqw5 unique (name);

4. Создание репозитория для работы с базой данных

@Repository
public interface PersonRepository extends JpaRepository<Person, Long> {
}

В этом интерфейсе используется аннотация @Repository, что указывает на его связь с операциями над базой данных. Кроме того, он наследует интерфейс JpaRepository<Person, Long>, который выглядит следующим образом:

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    List<T> findAll();

    List<T> findAll(Sort var1);

    List<T> findAllById(Iterable<ID> var1);

    <S extends T> List<S> saveAll(Iterable<S> var1);

    void flush();

    <S extends T> S saveAndFlush(S var1);

    void deleteInBatch(Iterable<T> var1);

    void deleteAllInBatch();

    T getOne(ID var1);

    <S extends T> List<S> findAll(Example<S> var1);
}
```    <S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

Это указывает на то, что, если мы наследуемся от JpaRepository<T, ID>, мы получаем методы CRUD (создание, чтение, обновление, удаление), пагинации и сортировки, а также методы для выполнения запросов по примеру, которые предоставляет JPA.

4.1 Практика использования встроенных методов JPA

1) CRUD операции

1. Сохранение пользователя в базе данных

    Person person = new Person("SnailClimb", 23);
    personRepository.save(person);

Метод save() соответствует SQL-запросу: insert into person (age, name) values (23, "snailclimb")

2. Поиск пользователя по ID

    Optional<Person> personOptional = personRepository.findById(id);

Метод findById() соответствует SQL-запросу: select * from person p where p.id = id

3. Удаление пользователя по ID

    personRepository.deleteById(id);

Метод deleteById() соответствует SQL-запросу: delete from person where id = id

4. Обновление пользователя

Для обновления объекта также используется метод save(), например:

    Person person = new Person("SnailClimb", 23);
    Person savedPerson = personRepository.save(person);
    // Обновляем имя объекта person
    savedPerson.setName("UpdatedName");
    personRepository.save(savedPerson);

Здесь метод save() соответствует SQL-запросу: update person set name = "UpdatedName" where id = id

2) Поиск с условиями

Ниже приведены методы, которые вы можете определить в интерфейсе PersonRepository, используя синтаксис JPA.

Если вы хотите найти пользователя по имени, вы можете сделать это так:```java Optional findByName(String name);


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

```java
    List<Person> findByAgeGreaterThan(int age);

4.2 Практика использования пользовательских SQL-запросов

Часто использование пользовательских SQL-запросов может быть очень полезным. По имени name искать Person:

    @Query("select p from Person p where p.name = :name")
    Optional<Person> findByNameCustomeQuery(@Param("name") String name);

Частичный запрос к атрибутам Person, чтобы избежать select * операции:

    @Query("select p.name from Person p where p.id = :id")
    String findPersonNameById(@Param("id") Long id);

Обновление имени Person по id:

    @Modifying
    @Query("update Person p set p.name = ?1 where p.id = ?2")
    void updatePersonNameById(String name, Long id);

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

Если вам нужно создать асинхронные методы, это также довольно просто.

Асинхронные методы возвращают управление сразу после вызова, а затем передаются для выполнения TaskExecutor. Конечно, вы также можете выбрать, чтобы вернуть результат клиенту только после завершения выполнения. Если вас интересует асинхронное программирование в Spring Boot, вы можете посмотреть эту статью: «Новичок сможет разобраться: руководство по асинхронному программированию в SpringBoot».

@Async
Future<User> findByName(String name);               

@Async
CompletableFuture<User> findByName(String name); 
```## 5. Тестовый класс и адрес исходного кода

Тестовый класс:

```java

@SpringBootTest
@RunWith(SpringRunner.class)
public class PersonRepositoryTest {
    @Autowired
    private PersonRepository personRepository;
    private Long id;

    /**
     * Сохранение person в базу данных
     */
    @Before
    public void setUp() {
        assertNotNull(personRepository);
        Person person = new Person("SnailClimb", 23);
        Person savedPerson = personRepository.saveAndFlush(person); // Обновление имени person
        savedPerson.setName("UpdatedName");
        personRepository.save(savedPerson);

        id = savedPerson.getId();
    }

    /**
     * Поиск person с помощью встроенных методов JPA
     */
    @Test
    public void should_get_person() {
        Optional<Person> personOptional = personRepository.findById(id);
        assertTrue(personOptional.isPresent());
        assertEquals("SnailClimb", personOptional.get().getName());
        assertEquals(Integer.valueOf(23), personOptional.get().getAge());

        List<Person> personList = personRepository.findByAgeGreaterThan(18);
        assertEquals(1, personList.size());
        // Очистка базы данных
        personRepository.deleteAll();
    }
/**
 * Пользовательский запрос SQL для поиска person
 */

@Test
public void should_get_person_use_custom_query() {
    // Поиск всех полей
    Optional<Person> personOptional = personRepository.findByNameCustomeQuery("SnailClimb");
    assertTrue(personOptional.isPresent());
    assertEquals(Integer.valueOf(23), personOptional.get().getAge());
    // Поиск части полей
    String personName = personRepository.findPersonNameById(id);
    assertEquals("SnailClimb", personName);
    System.out.println(id);
    // Обновление
    personRepository.updatePersonNameById("UpdatedName", id);
    Optional<Person> updatedName = personRepository.findByNameCustomeQuery("UpdatedName");
    assertTrue(updatedName.isPresent());
    // Очистка базы данных
    personRepository.deleteAll();
}

Адрес исходного кода: https://github.com/Snailclimb/springboot-guide/tree/master/source-code/basis/jpa-demo

6. Заключение

В данной статье были рассмотрены основные методы использования JPA:

  1. Использование встроенных методов JPA для выполнения операций CRUD и условных запросов.
  2. Создание пользовательских SQL-запросов для выполнения запросов или обновления базы данных.
  3. Создание асинхронных методов.

В следующей статье о JPA будут рассмотрены два важных аспекта:

  1. Реализация базовой пагинации.
  2. Объединение нескольких таблиц и пагинация при объединении нескольких таблиц.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/GiteeOS-springboot-guide.git
git@api.gitlife.ru:oschina-mirror/GiteeOS-springboot-guide.git
oschina-mirror
GiteeOS-springboot-guide
GiteeOS-springboot-guide
master