предисловие
Когда я впервые использовал Spring JPA, я почувствовал, что эта штука просто артефакт, почти не нужно было писать код о доступе к базе данных, и получилась базовая функция CURD. Давайте используем пример для описания основных операций, используемых в следующем JPA.
Создайте новый проект и добавьте зависимости
Создайте новый пустой проект SpringBoot в Intellij IDEA. Обратитесь к конкретным шагамПервое знакомство со SpringBoot. Согласно требованиям этого примера нам нужно добавить следующие три зависимости
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
Подготовьте среду базы данных
Для этого проекта мы создадим новую базу данных springboot_jpa и авторизуем пользователя springboot.
create database springboot_jpa;
grant all privileges on springboot_jpa.* to 'springboot'@'%' identified by 'springboot';
flush privileges;
Конфигурация проекта
#通用数据源配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://10.110.2.56:3306/springboot_jpa?charset=utf8mb4&useSSL=false
spring.datasource.username=springboot
spring.datasource.password=springboot
# Hikari 数据源专用配置
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
# JPA 相关配置
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
Конфигурация источника данных перед этой статьей Ли и предыдущей статьей«Использование шаблона JDBC в SpringBoot»так же, как в. Следующие конфигурации должны быть объяснены
- spring.jpa.show-sql=true Настройте вывод информации о выполненном операторе SQL в журнал.
- Конфигурация spring.jpa.hibernate.ddl-auto=create указывает, что таблица, соответствующая классу сущностей, должна быть удалена и создана при запуске программы. Этот параметр опасен, поскольку он удалит соответствующую таблицу и перестроит ее. Поэтому не используйте его в среде сборки. Его можно использовать только один раз при инициализации структуры базы данных в тестовой среде.
- spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect . В SrpingBoot версии 2.0, когда Hibernate создавал таблицу данных, механизмом хранения базы данных по умолчанию был MyISAM (раньше это был InnoDB, что было странно). Этот параметр используется для переключения механизма хранения по умолчанию на InnoDB при создании таблицы.
Создайте первый класс сущности данных
Класс сущности базы данных является объектом POJO Bean. Здесь мы сначала создаем объект базы данных UserDO. Исходный код объекта базы данных выглядит следующим образом
package com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* 用户实体类
*
* @author 杨高超
* @since 2018-03-12
*/
@Entity
@Table(name = "AUTH_USER")
public class UserDO {
@Id
private Long id;
@Column(length = 32)
private String name;
@Column(length = 32)
private String account;
@Column(length = 64)
private String pwd;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
в:
- @Entity — это обязательная аннотация, которая объявляет, что этот класс соответствует таблице базы данных.
- @Table(name = "AUTH_USER") — необязательная аннотация. Объявите информацию о таблице, соответствующую объекту базы данных. Включая имя таблицы, информацию об индексе и т. д. Здесь объявляется, что имя таблицы, соответствующее этому классу сущностей, — AUTH_USER. Если не указано, имя таблицы и имя сущности совпадают.
- Аннотация @Id объявляет атрибут, соответствующий уникальному идентификатору объекта.
- @Column(length = 32) Определение полей таблицы, используемых для объявления свойств объекта. Каждый атрибут сущности по умолчанию соответствует полю таблицы. Имя поля совпадает с именем свойства по умолчанию (не обязательно равно). Тип поля автоматически выводится из типа свойства объекта. Здесь главное объявить длину символьного поля. Если не указано, система будет использовать 255 в качестве длины поля.
Все вышеперечисленные конфигурации верны, тогда, запустив этот проект в это время, мы увидим в логе следующее содержимое:
Hibernate: drop table if exists auth_user
Hibernate: create table auth_user (id bigint not null, account varchar(32), name varchar(32), pwd varchar(64), primary key (id)) engine=InnoDB
Система автоматически создала для нас таблицу данных. Просмотр таблиц и структур таблиц в базе данных
Описанный выше процесс в основном похож на процесс, который мы использовали ранее в Hibernate, будь то объявление сущностей базы данных или автоматическое создание таблиц. Теперь мы официально входим в мир Spring Data JPA, посмотрим, какая у него потрясающая производительность
Реализовать службу уровня сохраняемости
В мире Spring Data JPA реализация службы уровня сохраняемости — очень простое дело. Взяв в качестве примера вышеприведенный объект объекта UserDO, мы хотим реализовать службу уровня сохраняемости с функциями добавления, удаления, изменения и запроса, тогда мне нужно только объявить интерфейс, который наследует
Подойдет интерфейс org.springframework.data.repository.Repository
package com.yanggaochao.springboot.learn.springbootjpalearn.security.dao;
import com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao.UserDO;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* 用户服务数据接口类
*
* @author 杨高超
* @since 2018-03-12
*/
@Repository
package com.yanggaochao.springboot.learn.springbootjpalearn.security.dao;
import com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao.UserDO;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* 用户服务数据接口类
*
* @author 杨高超
* @since 2018-03-12
*/
@Repository
public interface UserDao extends JpaRepository<UserDO, Long> {
}
Не нужно писать ни строчки кода. Тогда для класса сущности UserDO у нас уже есть следующие функции
Например, мы используем следующий код для сохранения некоторых пользовательских сущностей в базе данных.
package com.yanggaochao.springboot.learn.springbootjpalearn;
import com.yanggaochao.springboot.learn.springbootjpalearn.security.dao.UserDao;
import com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao.UserDO;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Optional;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserDOTest {
@Autowired
private UserDao userDao;
@Before
public void before() {
UserDO userDO = new UserDO();
userDO.setId(1L);
userDO.setName("风清扬");
userDO.setAccount("fengqy");
userDO.setPwd("123456");
userDao.save(userDO);
userDO = new UserDO();
userDO.setId(3L);
userDO.setName("东方不败");
userDO.setAccount("bubai");
userDO.setPwd("123456");
userDao.save(userDO);
userDO.setId(5L);
userDO.setName("向问天");
userDO.setAccount("wentian");
userDO.setPwd("123456");
userDao.save(userDO);
}
@Test
public void testAdd() {
UserDO userDO = new UserDO();
userDO.setId(2L);
userDO.setName("任我行");
userDO.setAccount("renwox");
userDO.setPwd("123456");
userDao.save(userDO);
userDO = new UserDO();
userDO.setId(4L);
userDO.setName("令狐冲");
userDO.setAccount("linghuc");
userDO.setPwd("123456");
userDao.save(userDO);
}
@After
public void after() {
userDao.deleteById(1L);
userDao.deleteById(3L);
userDao.deleteById(5L);
}
}
Это должно использовать Junit для выполнения тестовых случаев. Аннотация @Before выполняет подготовленный код перед тестовым примером. Здесь сначала вставьте три информации о пользователе. Выполните этот тест, и когда он будет выполнен, посмотрите на базу данных, и вы увидите, что в базе данных есть 5 записей:
Мы также можем проверить правильность функции поиска объекта путем идентификации, функции запроса всех данных, функция запроса может даже выполнять сортировку и листание тестовых случаев.
@Test
public void testLocate() {
Optional<UserDO> userDOOptional = userDao.findById(1L);
if (userDOOptional.isPresent()) {
UserDO userDO = userDOOptional.get();
System.out.println("name = " + userDO.getName());
System.out.println("account = " + userDO.getAccount());
}
}
@Test
public void testFindAll() {
List<UserDO> userDOList = userDao.findAll(new Sort(Sort.Direction.DESC,"account"));
for (UserDO userDO : userDOList) {
System.out.println("name = " + userDO.getName());
System.out.println("account = " + userDO.getAccount());
}
}
Можно видеть, что все, что мы сделали, это добавили информацию о конфигурации базы данных в проект SpingBoot, объявили объект сущности базы данных UserDO, а затем объявили интерфейс уровня сохраняемости, который наследуется от org.springframework.data.jpa.repository.JpaRepository интерфейс. Затем система автоматически имеет множество функций добавления, удаления, изменения и запроса. Функция запроса даже имеет возможность сортировки и разбиения на страницы.
В этом сила JPA. В дополнение к этим интерфейсам у пользователей будут другие потребности, и JPA также может удовлетворить ваши потребности.
Развернуть запрос
На приведенном выше снимке экрана «Функция удаления объекта запроса UserDao» мы видим, что функция запроса неудовлетворительна, и многие функции запроса, которые нам нужны, еще недоступны. Но не волнуйтесь. JPA имеет очень удобный и элегантный способ решения
Запрос по свойству
Если мы хотим сделать запрос на основе свойства объекта, мы можем сделать объявление интерфейса в интерфейсе UserDao. Например, если мы хотим выполнить запрос на основе атрибута учетной записи объекта (который может использоваться в функции входа в систему). Мы можем добавить объявление интерфейса в com.yanggaochao.springboot.learn.springbootjpalearn.security.dao.UserDao.
UserDO findByAccount(String account);
Затем добавьте тестовый пример
@Test
public void testFindByAccount() {
UserDO userDO = userDao.findByAccount("wentian");
if (userDO != null) {
System.out.println("name = " + userDO.getName());
System.out.println("account = " + userDO.getAccount());
}
}
После запуска он будет распечатан в журнале
name = 向问天
account = wentian
Этот метод очень мощный, он может поддерживать не только один атрибут, но и несколько комбинаций атрибутов. Например, если мы хотим найти интерфейс, учетная запись и пароль которого одновременно удовлетворяют условиям запроса. Затем объявляем в интерфейсе UserDao
UserDO findByAccountAndPwd(String account, String pwd);
В другом примере, если мы хотим запросить список пользователей, чей идентификатор больше определенного условия, мы можем объявить следующий интерфейс
List<UserDO> findAllByIdGreaterThan(Long id);
Эту структуру оператора можно проиллюстрировать следующей таблицей.
пользовательский запрос
Если вышеуказанная ситуация не соответствует потребностям. Затем мы можем решить эту проблему, импортировав аннотацию org.springframework.data.jpa.repository.Query. Например, если мы хотим запросить список всех пользователей, имена которых равны двум именам, мы можем объявить следующий интерфейс
@Query("SELECT O FROM UserDO O WHERE O.name = :name1 OR O.name = :name2 ")
List<UserDO> findTwoName(@Param("name1") String name1, @Param("name2") String name2);
Вот запрос, определенный в синтаксисе PQL. Два имени параметра имеют : в заявлении для принятия решения о следующем платеже
Если вы привыкли писать операторы SQL для завершения запроса, вы также можете реализовать его следующим образом.
@Query(nativeQuery = true, value = "SELECT * FROM AUTH_USER WHERE name = :name1 OR name = :name2 ")
List<UserDO> findSQL(@Param("name1") String name1, @Param("name2") String name2);
Здесь, добавив атрибут nativeQuery = true в аннотацию @Query, вы можете использовать собственные операторы SQL для написания запросов.
первичный ключ союза
Из определения интерфейса org.springframework.data.jpa.repository.JpaRepository
Давайте расширим предыдущий сценарий. Предположим, у нас есть объект Role с двумя атрибутами, идентификатор и имя, соответствующие таблице данных auth_role, и объект отношения пользователя роли, RoleUser, указывающий отношение между ролями и пользователями, и два атрибута roleId, userId, соответствующие auth_role_user. стол . Затем нам нужно объявить объект RoleDO следующим образом.
package com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* 角色实体类
*
* @author 杨高超
* @since 2018-03-12
*/
@Entity
@Table(name = "AUTH_ROLE")
public class RoleDO {
@Id
private Long id;
@Column(length = 32)
private String name;
@Column(length = 64)
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
В случае, когда в качестве общего первичного ключа используется несколько атрибутов, обычно требуется создать отдельный класс первичного ключа, атрибуты которого совпадают с полями первичного ключа объекта базы данных.Для реализации интерфейса java.io.Serializable , объявление класса выглядит следующим образом
package com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao;
import java.io.Serializable;
/**
* 联合主键对象
*
* @author 杨高超
* @since 2018-03-12
*/
public class RoleUserId implements Serializable {
private Long roleId;
private Long userId;
}
Точно так же мы объявляем объект RoleUserDO следующим образом.
package com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
import java.io.Serializable;
/**
* 角色用户关系实体类
*
* @author 杨高超
* @since 2018-03-12
*/
@Entity
@IdClass(RoleUserId.class)
@Table(name = "AUTH_ROLE_USER")
public class RoleUserDO {
@Id
private Long roleId;
@Id
private Long userId;
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
}
Здесь, поскольку атрибуты класса объекта данных и класса первичного ключа объекта данных одинаковы, мы можем удалить класс первичного ключа объекта данных, а затем объявить класс первичного ключа класса объекта данных как самого себя. Конечно, вам придется реализовать интерфейс java.io.Serializable самостоятельно.
Таким образом, если мы хотим запросить список всех пользователей в роли, мы можем объявить следующий интерфейс
@Query("SELECT U FROM UserDO U ,RoleUserDO RU WHERE U.id = RU.userId AND RU.roleId = :roleId")
List<UserDO> findUsersByRole(@Param("roleId") Long roleId);
Разумеется, в этом случае мы увидим, что система автоматически создает таблицы AUTH_ROLE и AUTH_ROLE_USER. Структура их таблицы выглядит так
Обратите внимание, что в таблице auth_role_user имя атрибута userId преобразуется в user_id, а roleId — в role_id.
Если мы хотим реализовать вышеуказанные функции в виде операторов SQL, то мы изменим объявление интерфейса на следующую форму.
@Query("SELECT U.* FROM AUTH_USER U ,AUTH_ROLE_USER RU WHERE U.id = RU.user_id AND RU.role_id = :roleId")
List<UserDO> findUsersByRole(@Param("roleId") Long roleId);
постскриптум
Этот пример в основном описывает некоторые детали использования JPA. Мы видим, что. Очень удобно и быстро использовать JPA для выполнения функций добавления, удаления, изменения и запроса реляционных баз данных. Весь код загружен в репозиторий github.springboot-jpa-learnна
Первоначально опубликовано вкороткая книга