Spring Data JPA用法浅析
一:介绍
Spring Data JPA是Spring框架的一部分,它为Java Persistence API (JPA) 提供了一套高度抽象的数据访问层实现。它的目的是使得开发者能够更加方便快捷地实现对数据库的操作,而不需要编写大量的重复性SQL语句或者复杂的JPA查询。
二:环境准备
1,添加依赖
<dependencies> <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> </dependencies>
2,配置数据库连接
spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=mysecretpassword spring.jpa.hibernate.ddl-auto=update
3,创建实体类
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String email; // Getter and Setter methods public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
4,定义Repository接口
import org.springframework.data.repository.CrudRepository; public interface UserRepository extends JpaRepository<T, I>, JpaSpecificationExecutor<T> { // Custom query method example List<User> findByUsername(String username); }
5,使用repository接口
在服务类中注入Repository并使用它进行数据库操作:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public User createUser(User user) { return userRepository.save(user); } public List<User> getUsersByUsername(String username) { return userRepository.findByUsername(username); } // Add more methods for update, delete, etc. }
三,用法
1,根据方法名自动生成查询:
1)JpaRepository提供部分默认实现方法,我们可以直接使用。
package org.springframework.data.jpa.repository; import java.util.List; import org.springframework.data.domain.Example; import org.springframework.data.domain.Sort; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.QueryByExampleExecutor; @NoRepositoryBean public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { List<T> findAll(); List<T> findAll(Sort sort); List<T> findAllById(Iterable<ID> ids); <S extends T> List<S> saveAll(Iterable<S> entities); void flush(); <S extends T> S saveAndFlush(S entity); <S extends T> List<S> saveAllAndFlush(Iterable<S> entities); /** @deprecated */ @Deprecated default void deleteInBatch(Iterable<T> entities) { this.deleteAllInBatch(entities); } void deleteAllInBatch(Iterable<T> entities); void deleteAllByIdInBatch(Iterable<ID> ids); void deleteAllInBatch(); /** @deprecated */ @Deprecated T getOne(ID id); /** @deprecated */ @Deprecated T getById(ID id); T getReferenceById(ID id); <S extends T> List<S> findAll(Example<S> example); <S extends T> List<S> findAll(Example<S> example, Sort sort); }
2)如果默认未实现的,我们可以按照字段名称去查询。
List<User> findByUsernameAndEmail(String username, String email); //根据用户的名称和邮箱查询
List<User> findByUsernameIn(List<String> usernames); //查询用户名在某个集合中的数据
2,使用Example API查询
1)创建一个User
实例作为查询模板,并使用Example.of()
方法配合Repository执行查询。
public List<User> findUsersByExample() { User exampleUser = new User(); exampleUser.setUsername("John Doe"); exampleUser.setEmail("2531323@qq.com"); Example<User> example = Example.of(exampleUser); // 创建Example实例 return userRepository.findAll(example); // 执行查询 }
2)使用ExampleMatcher
来定制查询条件的匹配方式。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.domain.ExampleMatcher.StringMatcher; import org.springframework.stereotype.Service; @Service public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } // 精确匹配 public List<User> findUsersByUsernameExact(String username) { User probe = new User(); probe.setUsername(username); ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("username", ExampleMatcher.GenericPropertyMatchers.exact()); Example<User> example = Example.of(probe, matcher); return userRepository.findAll(example); } // 模糊匹配(like %keyword%) public List<User> findUsersByUsernameLike(String keyword) { User probe = new User(); probe.setUsername(keyword); ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("username", ExampleMatcher.GenericPropertyMatchers.contains()); Example<User> example = Example.of(probe, matcher); return userRepository.findAll(example); } // 忽略大小写的精确匹配 public List<User> findUsersByUsernameIgnoreCase(String username) { User probe = new User(); probe.setUsername(username); ExampleMatcher matcher = ExampleMatcher.matching().withIgnoreCase().withMatcher("username", ExampleMatcher.GenericPropertyMatchers.exact()); Example<User> example = Example.of(probe, matcher); return userRepository.findAll(example); } }
3,手动写sql语句
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import java.util.List; public interface UserRepository extends CrudRepository<User, Long>, JpaSpecificationExecutor<User> { // 自定义查询方法,使用@Query注解来指定SQL查询语句 @Query("SELECT u FROM User u WHERE u.username IN (:usernames)") List<User> findByUsernamesIn(List<String> usernames); }
4,使用Specification结构化查询
1)Specification的用法主要是依赖于JpaSpecificationExecutor接口
package org.springframework.data.jpa.repository; import java.util.List; import java.util.Optional; 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.lang.Nullable; public interface JpaSpecificationExecutor<T> { Optional<T> findOne(@Nullable Specification<T> spec); List<T> findAll(@Nullable Specification<T> spec); Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable); List<T> findAll(@Nullable Specification<T> spec, Sort sort); long count(@Nullable Specification<T> spec); boolean exists(Specification<T> spec); }
2)一般用于构建分页查询:
public Page<User> getEndpointPage(String username, String email, Pageable pageable) { Page<User> userPage = userRepository.findAll((Specification<User>) (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();// name模糊查询 if (StringUtils.hasText(username)) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.like(root.get("username"), username)); } if (StringUtils.hasText(email)) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("email"), email)); }return predicate; // 查询username包含xxx且emial为xxx的用户 }, pageable); }
public Page<User> getEndpointPage(String username, String email, Pageable pageable) { Page<User> userPage = userRepository.findAll((Specification<User>) (root, query, criteriaBuilder) -> { Predicate predicate = criteriaBuilder.conjunction(); // name模糊查询 if (StringUtils.hasText(username)) { predicateUsername = criteriaBuilder.like(root.get("username"), "%".concat(username).concat("%")); } if (StringUtils.hasText(email)) { predicateEmail = criteriaBuilder.like(root.get("email"), "%".concat(email).concat("%")); } return criteriaBuilder.add(predicate, criteriaBuilder.or(predicateUsername, predicateEmail)); // 查询username包含xxx或者emial包含xxx的用户 }, pageable); }
3)使用CriteriaBuilder和Predicate还可以这样使用:
import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.List; public List<User> searchUsers(Long id, String username, String email, Boolean active) { EntityManager entityManager = // 获取EntityManager实例,通常是通过注入的方式获得 CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class); Root<User> userRoot = criteriaQuery.from(User.class); // 初始化一个Predicate列表,用于存放所有条件 List<Predicate> predicates = new ArrayList<>(); // 添加ID条件,如果id不为null if (id != null) { Predicate idPredicate = criteriaBuilder.equal(userRoot.get("id"), id); predicates.add(idPredicate); } // 添加用户名条件,如果username不为空 if (StringUtils.hasText(username)) { Predicate usernamePredicate = criteriaBuilder.like(userRoot.get("username"), "%" + username + "%"); predicates.add(usernamePredicate); } // 添加邮箱条件,如果email不为空 if (StringUtils.hasText(email)) { Predicate emailPredicate = criteriaBuilder.equal(userRoot.get("email"), email); predicates.add(emailPredicate); } // 添加激活状态条件,如果active不为null if (active != null) { Predicate activePredicate = criteriaBuilder.equal(userRoot.get("active"), active); predicates.add(activePredicate); } // 使用Predicate数组组合所有条件,这里使用逻辑与(AND)连接 Predicate finalPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); // 将最终的Predicate应用到查询中 criteriaQuery.where(finalPredicate); // 执行查询并返回结果 TypedQuery<User> typedQuery = entityManager.createQuery(criteriaQuery); return typedQuery.getResultList(); }
4)当然,Predicate
和CriteriaBuilder
的使用远不止上述示例所展示的那样。它们可以用于构建非常复杂和灵活的查询逻辑。以下是几个额外的用法示例:
1. 逻辑或(OR)条件:
// 如果你想根据多个条件中的任意一个进行查询(逻辑“或”),可以使用criteriaBuilder.or()来组合Predicate。 Predicate usernameOrEmailPredicate = criteriaBuilder.or( criteriaBuilder.like(userRoot.get("username"), "%" + username + "%"), criteriaBuilder.like(userRoot.get("email"), "%" + email + "%") );
2. 分组和优先级:
// 使用criteriaBuilder.and()和criteriaBuilder.or()可以组合多个Predicate,并使用括号来改变逻辑优先级。 Predicate groupPredicate = criteriaBuilder.and( criteriaBuilder.equal(userRoot.get("active"), true), criteriaBuilder.or( criteriaBuilder.equal(userRoot.get("username"), username), criteriaBuilder.equal(userRoot.get("email"), email) ) );
3. 排序
// 可以使用criteriaQuery.orderBy()方法来添加排序条件。 criteriaQuery.orderBy(criteriaBuilder.desc(userRoot.get("creationDate")));
4. 分页查询
// 在TypedQuery上调用setFirstResult()和setMaxResults()来实现分页。 typedQuery.setFirstResult(pageNumber * pageSize); typedQuery.setMaxResults(pageSize);
5. 聚合函数
// 使用CriteriaQuery的multiselect和criteriaBuilder.count()等方法来实现聚合查询。 CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class); countQuery.select(criteriaBuilder.count(countQuery.from(User.class))); Long totalUsers = entityManager.createQuery(countQuery).getSingleResult();
6. 子查询
// 可以构造子查询来作为查询条件的一部分。 Subquery<Long> subquery = criteriaQuery.subquery(Long.class); Root<User> subqueryRoot = subquery.from(User.class); subquery.select(subqueryRoot.get("id")); subquery.where(criteriaBuilder.equal(subqueryRoot.get("email"), email)); Predicate inSubquery = criteriaBuilder.in(userRoot.get("id")).value(subquery);
以上主要是JPA对单表操作的常见用法,多表之间的操作请见下一篇解析。