JPA кажется простым в освоении, но при детальном рассмотрении оказывается, что в нем много нюансов. Большинство материалов о JPA в интернете не являются исчерпывающими, а используемые версии часто устарели. В данной статье я собрал наиболее важные аспекты JPA, которые помогут новичкам и тем, кто уже знаком с JPA, использовать его правильно в реальных проектах. Статья основана на официальной документации Spring Data JPA, полная документация доступна для ознакомления.
Проект построен на основе последней версии Spring Boot 2.1.9.RELEASE (на момент написания статьи).
Для работы с 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>
Используя встроенную h2-базу данных, при запуске проекта база данных будет автоматически создана.В конфигурации важно обратить внимание на параметр spring.jpa.hibernate.ddl-auto=create
.
Доступны следующие значения для этого параметра:
create
: при каждом перезапуске проекта таблицы будут пересозданы, что приведет к потере данных.create-drop
: при каждом запуске проекта таблицы будут созданы, а при завершении работы проекта — удалены.update
: при каждом запуске проекта таблицы будут обновлены.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
Мы добавили аннотацию @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);
@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.
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
Ниже приведены методы, которые вы можете определить в интерфейсе PersonRepository
, используя синтаксис JPA.
Если вы хотите найти пользователя по имени, вы можете сделать это так:```java Optional findByName(String name);
Если вы хотите найти пользователей, чей возраст больше определенного значения, вы можете сделать это так:
```java
List<Person> findByAgeGreaterThan(int age);
Часто использование пользовательских 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);
Если вам нужно создать асинхронные методы, это также довольно просто.
Асинхронные методы возвращают управление сразу после вызова, а затем передаются для выполнения 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
В данной статье были рассмотрены основные методы использования JPA:
В следующей статье о JPA будут рассмотрены два важных аспекта:
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )