介绍
SpringDataJPA是SpringData项目下的一个模块。提供了基于JPA标准操作数据库的简化方案,底层默认依赖的是HibernateJPA。其特点为:只需要定义接口并集成SpringDataJPA中提供的接口即可,无需编写接口的实现类。
关键接口
Repository
SpringDataJPA提供的所有接口的顶层接口。本身是一个标志接口,提供了基于方法名称命名规则和基于@Query 注解两种操作方式支持。
java
/*
* Copyright 2011-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.repository;
import org.springframework.stereotype.Indexed;
/**
* Central repository marker interface. Captures the domain type to manage as well as the domain type's id type. General
* purpose is to hold type information as well as being able to discover interfaces that extend this one during
* classpath scanning for easy Spring bean creation.
* <p>
* Domain repositories extending this interface can selectively expose CRUD methods by simply declaring methods of the
* same signature as those declared in {@link CrudRepository}.
*
* @see CrudRepository
* @param <T> the domain type the repository manages
* @param <ID> the type of the id of the entity the repository manages
* @author Oliver Gierke
*/
@Indexed
public interface Repository<T, ID> {
}
CrudRepository
继承自Repository接口并且扩展了CRUD的相关功能。
java
/*
* Copyright 2008-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.repository;
import java.util.Optional;
import org.springframework.dao.OptimisticLockingFailureException;
/**
* Interface for generic CRUD operations on a repository for a specific type.
*
* @author Oliver Gierke
* @author Eberhard Wolff
* @author Jens Schauder
*/
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
/**
* Saves a given entity. Use the returned instance for further operations as the save operation might have changed the
* entity instance completely.
*
* @param entity must not be {@literal null}.
* @return the saved entity; will never be {@literal null}.
* @throws IllegalArgumentException in case the given {@literal entity} is {@literal null}.
* @throws OptimisticLockingFailureException when the entity uses optimistic locking and has a version attribute with
* a different value from that found in the persistence store. Also thrown if the entity is assumed to be
* present but does not exist in the database.
*/
<S extends T> S save(S entity);
/**
* Saves all given entities.
*
* @param entities must not be {@literal null} nor must it contain {@literal null}.
* @return the saved entities; will never be {@literal null}. The returned {@literal Iterable} will have the same size
* as the {@literal Iterable} passed as an argument.
* @throws IllegalArgumentException in case the given {@link Iterable entities} or one of its entities is
* {@literal null}.
* @throws OptimisticLockingFailureException when at least one entity uses optimistic locking and has a version
* attribute with a different value from that found in the persistence store. Also thrown if at least one
* entity is assumed to be present but does not exist in the database.
*/
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
/**
* Retrieves an entity by its id.
*
* @param id must not be {@literal null}.
* @return the entity with the given id or {@literal Optional#empty()} if none found.
* @throws IllegalArgumentException if {@literal id} is {@literal null}.
*/
Optional<T> findById(ID id);
/**
* Returns whether an entity with the given id exists.
*
* @param id must not be {@literal null}.
* @return {@literal true} if an entity with the given id exists, {@literal false} otherwise.
* @throws IllegalArgumentException if {@literal id} is {@literal null}.
*/
boolean existsById(ID id);
/**
* Returns all instances of the type.
*
* @return all entities
*/
Iterable<T> findAll();
/**
* Returns all instances of the type {@code T} with the given IDs.
* <p>
* If some or all ids are not found, no entities are returned for these IDs.
* <p>
* Note that the order of elements in the result is not guaranteed.
*
* @param ids must not be {@literal null} nor contain any {@literal null} values.
* @return guaranteed to be not {@literal null}. The size can be equal or less than the number of given
* {@literal ids}.
* @throws IllegalArgumentException in case the given {@link Iterable ids} or one of its items is {@literal null}.
*/
Iterable<T> findAllById(Iterable<ID> ids);
/**
* Returns the number of entities available.
*
* @return the number of entities.
*/
long count();
/**
* Deletes the entity with the given id.
* <p>
* If the entity is not found in the persistence store it is silently ignored.
*
* @param id must not be {@literal null}.
* @throws IllegalArgumentException in case the given {@literal id} is {@literal null}
*/
void deleteById(ID id);
/**
* Deletes a given entity.
*
* @param entity must not be {@literal null}.
* @throws IllegalArgumentException in case the given entity is {@literal null}.
* @throws OptimisticLockingFailureException when the entity uses optimistic locking and has a version attribute with
* a different value from that found in the persistence store. Also thrown if the entity is assumed to be
* present but does not exist in the database.
*/
void delete(T entity);
/**
* Deletes all instances of the type {@code T} with the given IDs.
* <p>
* Entities that aren't found in the persistence store are silently ignored.
*
* @param ids must not be {@literal null}. Must not contain {@literal null} elements.
* @throws IllegalArgumentException in case the given {@literal ids} or one of its elements is {@literal null}.
* @since 2.5
*/
void deleteAllById(Iterable<? extends ID> ids);
/**
* Deletes the given entities.
*
* @param entities must not be {@literal null}. Must not contain {@literal null} elements.
* @throws IllegalArgumentException in case the given {@literal entities} or one of its entities is {@literal null}.
* @throws OptimisticLockingFailureException when at least one entity uses optimistic locking and has a version
* attribute with a different value from that found in the persistence store. Also thrown if at least one
* entity is assumed to be present but does not exist in the database.
*/
void deleteAll(Iterable<? extends T> entities);
/**
* Deletes all entities managed by the repository.
*/
void deleteAll();
}
ListCrudRepository
继承自CrudRepository接口并且扩展了List的相关功能。
java
/*
* Copyright 2022-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.repository;
import java.util.List;
import org.springframework.dao.OptimisticLockingFailureException;
/**
* Interface for generic CRUD operations on a repository for a specific type. This an extension to
* {@link CrudRepository} returning {@link List} instead of {@link Iterable} where applicable.
*
* @author Jens Schauder
* @see CrudRepository
* @since 3.0
*/
@NoRepositoryBean
public interface ListCrudRepository<T, ID> extends CrudRepository<T, ID> {
/**
* Saves all given entities.
*
* @param entities must not be {@literal null} nor must it contain {@literal null}.
* @return the saved entities; will never be {@literal null}. The returned {@literal Iterable} will have the same size
* as the {@literal Iterable} passed as an argument.
* @throws IllegalArgumentException in case the given {@link Iterable entities} or one of its entities is
* {@literal null}.
* @throws OptimisticLockingFailureException when at least one entity uses optimistic locking and has a version
* attribute with a different value from that found in the persistence store. Also thrown if at least one
* entity is assumed to be present but does not exist in the database.
*/
<S extends T> List<S> saveAll(Iterable<S> entities);
/**
* Returns all instances of the type.
*
* @return all entities
*/
List<T> findAll();
/**
* Returns all instances of the type {@code T} with the given IDs.
* <p>
* If some or all ids are not found, no entities are returned for these IDs.
* <p>
* Note that the order of elements in the result is not guaranteed.
*
* @param ids must not be {@literal null} nor contain any {@literal null} values.
* @return guaranteed to be not {@literal null}. The size can be equal or less than the number of given
* {@literal ids}.
* @throws IllegalArgumentException in case the given {@link Iterable ids} or one of its items is {@literal null}.
*/
List<T> findAllById(Iterable<ID> ids);
}
PagingAndSortingRepository
继承自Repository接口并且扩展了分页和排序的相关功能。
java
/*
* Copyright 2008-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
/**
* Repository fragment to provide methods to retrieve entities using the pagination and sorting abstraction. In many
* cases this will be combined with {@link CrudRepository} or similar or with manually added methods to provide CRUD
* functionality.
*
* @author Oliver Gierke
* @author Jens Schauder
* @see Sort
* @see Pageable
* @see Page
* @see CrudRepository
*/
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends Repository<T, ID> {
/**
* Returns all entities sorted by the given options.
*
* @param sort the {@link Sort} specification to sort the results by, can be {@link Sort#unsorted()}, must not be
* {@literal null}.
* @return all entities sorted by the given options
*/
Iterable<T> findAll(Sort sort);
/**
* Returns a {@link Page} of entities meeting the paging restriction provided in the {@link Pageable} object.
*
* @param pageable the pageable to request a paged result, can be {@link Pageable#unpaged()}, must not be
* {@literal null}.
* @return a page of entities
*/
Page<T> findAll(Pageable pageable);
}
ListPagingAndSortingRepository
继承自PagingAndSortingRepository接口并且扩展了List的相关功能。
java
/*
* Copyright 2008-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.repository;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
/**
* Repository fragment to provide methods to retrieve entities using the pagination and sorting abstraction. This an
* extension to {@link PagingAndSortingRepository} returning {@link List} instead of {@link Iterable} where applicable.
*
* @author Oliver Gierke
* @author Jens Schauder
* @see Sort
* @see Pageable
* @see Page
* @see CrudRepository
*/
@NoRepositoryBean
public interface ListPagingAndSortingRepository<T, ID> extends PagingAndSortingRepository<T, ID> {
/**
* Returns all entities sorted by the given options.
*
* @param sort the {@link Sort} specification to sort the results by, can be {@link Sort#unsorted()}, must not be
* {@literal null}.
* @return all entities sorted by the given options
*/
List<T> findAll(Sort sort);
}
JpaRepository
继承了ListCrudRepository、ListPagingAndSortingRepository和QueryByExampleExecutor三个接口,是日常开发使用最多的接口。可以帮助我们将其他接口方法的返回值做适配处理,使我们在开发的过程中更加方便的使用这些方法。
java
/*
* Copyright 2008-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.repository;
import java.util.List;
import jakarta.persistence.EntityManager;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.ListCrudRepository;
import org.springframework.data.repository.ListPagingAndSortingRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.query.QueryByExampleExecutor;
/**
* JPA specific extension of {@link org.springframework.data.repository.Repository}.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
* @author Sander Krabbenborg
* @author Jesse Wouters
* @author Greg Turnquist
* @author Jens Schauder
*/
@NoRepositoryBean
public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
/**
* Flushes all pending changes to the database.
*/
void flush();
/**
* Saves an entity and flushes changes instantly.
*
* @param entity entity to be saved. Must not be {@literal null}.
* @return the saved entity
*/
<S extends T> S saveAndFlush(S entity);
/**
* Saves all entities and flushes changes instantly.
*
* @param entities entities to be saved. Must not be {@literal null}.
* @return the saved entities
* @since 2.5
*/
<S extends T> List<S> saveAllAndFlush(Iterable<S> entities);
/**
* Deletes the given entities in a batch which means it will create a single query. This kind of operation leaves JPAs
* first level cache and the database out of sync. Consider flushing the {@link EntityManager} before calling this
* method.
*
* @param entities entities to be deleted. Must not be {@literal null}.
* @deprecated Use {@link #deleteAllInBatch(Iterable)} instead.
*/
@Deprecated
default void deleteInBatch(Iterable<T> entities) {
deleteAllInBatch(entities);
}
/**
* Deletes the given entities in a batch which means it will create a single query. This kind of operation leaves JPAs
* first level cache and the database out of sync. Consider flushing the {@link EntityManager} before calling this
* method.
* <p>
* It will also NOT honor cascade semantics of JPA, nor will it emit JPA lifecycle events.
*</p>
* @param entities entities to be deleted. Must not be {@literal null}.
* @since 2.5
*/
void deleteAllInBatch(Iterable<T> entities);
/**
* Deletes the entities identified by the given ids using a single query. This kind of operation leaves JPAs first
* level cache and the database out of sync. Consider flushing the {@link EntityManager} before calling this method.
*
* @param ids the ids of the entities to be deleted. Must not be {@literal null}.
* @since 2.5
*/
void deleteAllByIdInBatch(Iterable<ID> ids);
/**
* Deletes all entities in a batch call.
*/
void deleteAllInBatch();
/**
* Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is
* implemented this is very likely to always return an instance and throw an
* {@link jakarta.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers
* immediately.
*
* @param id must not be {@literal null}.
* @return a reference to the entity with the given identifier.
* @see EntityManager#getReference(Class, Object) for details on when an exception is thrown.
* @deprecated use {@link JpaRepository#getReferenceById(ID)} instead.
*/
@Deprecated
T getOne(ID id);
/**
* Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is
* implemented this is very likely to always return an instance and throw an
* {@link jakarta.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers
* immediately.
*
* @param id must not be {@literal null}.
* @return a reference to the entity with the given identifier.
* @see EntityManager#getReference(Class, Object) for details on when an exception is thrown.
* @deprecated use {@link JpaRepository#getReferenceById(ID)} instead.
* @since 2.5
*/
@Deprecated
T getById(ID id);
/**
* Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is
* implemented this is very likely to always return an instance and throw an
* {@link jakarta.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers
* immediately.
*
* @param id must not be {@literal null}.
* @return a reference to the entity with the given identifier.
* @see EntityManager#getReference(Class, Object) for details on when an exception is thrown.
* @since 2.7
*/
T getReferenceById(ID id);
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)
*/
@Override
<S extends T> List<S> findAll(Example<S> example);
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
JpaSpecificationExecutor
提供了条件查询、分页和排序功能,无法单独使用,需要和其他的接口一块使用。
java
/*
* Copyright 2008-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.repository;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.repository.query.FluentQuery;
/**
* Interface to allow execution of {@link Specification}s based on the JPA criteria API.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Diego Krupitza
* @author Mark Paluch
*/
public interface JpaSpecificationExecutor<T> {
/**
* Returns a single entity matching the given {@link Specification} or {@link Optional#empty()} if none found.
*
* @param spec must not be {@literal null}.
* @return never {@literal null}.
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one entity found.
*/
Optional<T> findOne(Specification<T> spec);
/**
* Returns all entities matching the given {@link Specification}.
*
* @param spec must not be {@literal null}.
* @return never {@literal null}.
*/
List<T> findAll(Specification<T> spec);
/**
* Returns a {@link Page} of entities matching the given {@link Specification}.
*
* @param spec must not be {@literal null}.
* @param pageable must not be {@literal null}.
* @return never {@literal null}.
*/
Page<T> findAll(Specification<T> spec, Pageable pageable);
/**
* Returns all entities matching the given {@link Specification} and {@link Sort}.
*
* @param spec must not be {@literal null}.
* @param sort must not be {@literal null}.
* @return never {@literal null}.
*/
List<T> findAll(Specification<T> spec, Sort sort);
/**
* Returns the number of instances that the given {@link Specification} will return.
*
* @param spec the {@link Specification} to count instances for, must not be {@literal null}.
* @return the number of instances.
*/
long count(Specification<T> spec);
/**
* Checks whether the data store contains elements that match the given {@link Specification}.
*
* @param spec the {@link Specification} to use for the existence check, ust not be {@literal null}.
* @return {@code true} if the data store contains elements that match the given {@link Specification} otherwise
* {@code false}.
*/
boolean exists(Specification<T> spec);
/**
* Deletes by the {@link Specification} and returns the number of rows deleted.
* <p>
* This method uses {@link jakarta.persistence.criteria.CriteriaDelete Criteria API bulk delete} that maps directly to
* database delete operations. The persistence context is not synchronized with the result of the bulk delete.
* <p>
* Please note that {@link jakarta.persistence.criteria.CriteriaQuery} in,
* {@link Specification#toPredicate(Root, CriteriaQuery, CriteriaBuilder)} will be {@literal null} because
* {@link jakarta.persistence.criteria.CriteriaBuilder#createCriteriaDelete(Class)} does not implement
* {@code CriteriaQuery}.
*
* @param spec the {@link Specification} to use for the existence check, must not be {@literal null}.
* @return the number of entities deleted.
* @since 3.0
*/
long delete(Specification<T> spec);
/**
* Returns entities matching the given {@link Specification} applying the {@code queryFunction} that defines the query
* and its result type.
*
* @param spec must not be null.
* @param queryFunction the query function defining projection, sorting, and the result type
* @return all entities matching the given Example.
* @since 3.0
*/
<S extends T, R> R findBy(Specification<T> spec, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction);
}
命名规则
由于篇幅问题,下列表格中只梳理了一部分常用的内容。如无法满足开发要求可查看官方文档或自行推断命名。
| 关键字 | 示例 | 效果 |
|---|---|---|
| And | findByNameAndPassword(String name, String password) | WHERE name = ? AND password = ? |
| Or | findByNameOrPhone(String name, String phone) | WHERE name = ? OR phone = ? |
| Equal | findByNameEqual(String name) | WHERE name = ? |
| Between | findByIdBetween(Long start, Long end) | WHERE id BETWEEN ? AND ? |
| LessThan | findByIdLessThan(Long num) | WHERE id < ? |
| LessThanEqual | findByIdNameLessThanEqual(Long num) | WHERE id <= ? |
| GreaterThan | findByIdGreaterThan(Long num) | WHERE id > ? |
| GreaterThanEqual | findByIdGreaterThanEqual(Long num) | WHERE id >= ? |
| After | findByIdAfter(Long num) | WHERE id > ? |
| Before | findByIdBefore(Long num) | WHERE id < ? |
| IsNull | findByNameIsNull(String name) | WHERE name IS NULL |
| IsNotNull | findByNameIsNotNull(String name) | WHERE name IS NOT NULL |
| Like | findByNameLike(String name) | WHERE name LIKE ? |
| NotLike | findByNameNotLike(String name) | WHERE name NOT LIKE ? |
| StartingWith | findByNameStartingWith(String name) | WHERE name LIKE '?%' |
| EndingWith | findByNameEndingWith(String name) | WHERE name LIKE '%?' |
| Containing | findByNameContaining(String name) | WHERE name LIKE '%?%' |
| OrderBy | findByAgeOrderByNameDesc(Integer age) | WHERE age = ? ORDER BY name DESC |
| Not | findByNameNot(String name) | WHERE name <> ? |
| In | findByNameIn(Collection names) | WHERE name IN(?) |
| NotIn | findByNameNotIn(Collection names) | WHERE name NOT IN(?) |
| True | findByDeletedTrue() | WHERE deleted = true |
| False | findByDeletedFalse() | WHERE deleted = false |
| IgnoreCase | findByNameIgnoreCase(String name) | WHERE UPPER(name) = UPPER(?) |
代码示例
整体结构

pom
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
</parent>
<groupId>com.sumlv</groupId>
<artifactId>spring-boot-data-jpa-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.24</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
properties
bash
# 应用设置
spring.application.name=spring-boot-data-jpa-demo
# 数据源配置
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/sumlv?characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# jpa配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
entity
java
package com.sumlv.demo.entity;
import lombok.Data;
import jakarta.persistence.*;
/**
* 用户表
*
* @Auther: yuzhuo.song
* @Date: 2026-08-27
*/
@Data
@Entity
@Table(name = "users")
public class Users {
/**
* ID
*/
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 用户名
*/
@Column(name = "user_name")
private String userName;
/**
* 密码
*/
@Column(name = "password")
private String password;
/**
* 手机号
*/
@Column(name = "phone")
private String phone;
}
dao
java
package com.sumlv.demo.dao;
import com.sumlv.demo.entity.Users;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
/**
* 用户表持久层
*
* @Auther: yuzhuo.song
* @Date: 2026-08-27
*/
public interface UsersDao extends JpaRepository<Users, Long>, JpaSpecificationExecutor<Users> {
/**
* 自定义查询,无需实现但命名需要符合规则
*/
List<Users> findByUserName(String userName);
/**
* 使用JPQL的方式查询
*/
@Query("FROM Users WHERE id = :id")
Users queryById(@Param("id") Long id);
/**
* 使用常规SQL的方式查询
*/
@Query(value = "SELECT id, user_name, password, phone FROM users WHERE user_name LIKE :userName", nativeQuery = true)
List<Users> queryUsersLikeUserName(@Param("userName") String userName);
/**
* 使用JPQL的方式修改
*/
@Modifying(clearAutomatically = true)
@Query("UPDATE Users SET userName = :userName WHERE id = :id")
void updateById(@Param("id") Long id, @Param("userName") String userName);
/**
* 使用SQL的方式修改
*/
@Modifying(clearAutomatically = true)
@Query(value = "UPDATE users SET user_name = :userName, password = :password WHERE id = :id", nativeQuery = true)
void updateById(@Param("id") Long id, @Param("userName") String userName, @Param("password") String password);
}
test
java
package com.sumlv.demo;
import com.sumlv.demo.dao.UsersDao;
import com.sumlv.demo.entity.Users;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* JPA测试
*
* @Auther: yuzhuo.song
* @Date: 2026-08-27
*/
@SpringBootTest
class SpringBootDataJpaDemoApplicationTests {
/**
* 持久层注入
*/
@Autowired
private UsersDao dao;
/**
* 测试保存
*/
@Test
public void testSave() {
Users users = new Users();
users.setUserName("test3");
users.setPassword("333333");
users.setPhone("15666666666");
dao.save(users);
}
/**
* 测试列表查询
*/
@Test
public void testQuery() {
List<Users> list = dao.findByUserName("lisi");
System.out.println(list);
}
/**
* 测试单条查询
*/
@Test
public void testQueryById() {
Users users = dao.queryById(9L);
System.out.println(users);
}
/**
* 测试模糊查询
*/
@Test
public void testQueryUsersLikeUserName() {
List<Users> list = dao.queryUsersLikeUserName("%es%");
System.out.println(list);
}
/**
* 测试JPQL方式修改
* 修改操作如果不添加事务会报错,实际开发应该在顶层设计中控制事务
* 因为在测试包中,所以必须手动设置不回滚
*/
@Test
@Rollback(false)
@Transactional(rollbackFor = Exception.class)
public void testUpdateByJPQL() {
Long id = 8L;
Users users = dao.queryById(id);
System.out.println(users);
dao.updateById(id, "test");
users = dao.queryById(id);
System.out.println(users);
}
/**
* 测试SQL方式修改
* 修改操作如果不添加事务会报错,实际开发应该在顶层设计中控制事务
* 因为在测试包中,所以必须手动设置不回滚
*/
@Test
@Rollback(false)
@Transactional(rollbackFor = Exception.class)
public void testUpdateBySQL() {
Long id = 8L;
Users users = dao.queryById(id);
System.out.println(users);
dao.updateById(id, "test1", "123456");
users = dao.queryById(id);
System.out.println(users);
}
/**
* 测试Specification,支持条件查询、分页和排序功能
*/
@Test
public void test1(){
Specification<Users> specification = new Specification<>() {
/**
* 构建查询条件
*
* @param root 根对象,封装查询条件
* @param criteriaQuery 基本查询
* @param criteriaBuilder 基本查询构造器
* @return 构件结果
*/
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> list = new ArrayList<>();
list.add(criteriaBuilder.like(root.get("userName"), "%st%"));
list.add(criteriaBuilder.le(root.get("id"), 10));
return criteriaBuilder.and(list.toArray(new Predicate[0]));
}
};
Sort sort = Sort.by(Sort.Direction.DESC, "id");
Pageable pageable = PageRequest.of(0, 3, sort);
Page<Users> page = dao.findAll(specification, pageable);
System.out.println(page.getTotalElements());
System.out.println(page.getTotalPages());
List<Users> content = page.getContent();
System.out.println(content);
}
}