jpa文档笔记

这篇博客详细介绍了Spring JPA的核心类、使用方式、查询方法定义,包括自定义接口实现、事件驱动、REST查询语言与Spring Data JPA Specifications。同时深入探讨了Hibernate的@Query用法,如JPQL、原生查询、排序、分页等,以及更新查询和动态查询策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

未完待续


spring jpa

1. 参考链接

https://www.jianshu.com/p/c23c82a8fcfc
https://docs.spring.io/spring-data/jpa/docs/2.4.11/reference/html/#preface
https://www.baeldung.com/spring-rest-api-query-search-language-tutorial

2. 核心类

Repository, CrudRepository, JpaRepository
JpaSpecificationExecutor, PagingAndSortingRepository

3. 使用方式

3.1. 声明接口

interface PersonRepository extends Repository<Person, Long> { … }

3.2. 定义方法

interface PersonRepository extends Repository<Person, Long> {
  List<Person> findByLastname(String lastname);
}

3.3. 让spring开启代理

  • javaconfig方式
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableJpaRepositories
class Config { … }
  • xml方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:jpa="http://www.springframework.org/schema/data/jpa"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
     https://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/data/jpa
     https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

   <jpa:repositories base-package="com.acme.repositories"/>

</beans>

注意:不同的存储类型,使用相应的store(如jpa,mongodb), @Enable${store}Repositories

3.4. 使用

class SomeClient {

  private final PersonRepository repository;

  SomeClient(PersonRepository repository) {
    this.repository = repository;
  }

  void doSomething() {
    List<Person> persons = repository.findByLastname("Matthews");
  }
}

4. 查询方法定义

代理通过两种方式生成查询语句

  • 根据方法名自动生成
  • 指定查询语句
4.1. 策略

Enable${store}Repositories的queryLookupStrategy属性指定

  • create: 移除方法名的通用前缀,解析方法名获取
  • use_declared_query: 通过注解指定
  • Create_if_not_found:上面两种方式的混合

5. 自定义接口实现

参考本文下方的Dynamic Query

Repository默认spring是通过代理组装的,寻找的是Impl结尾的接口实现,可通过Enable${store}Repositories的repositoryImplementationPostfix设置

interface CustomizedUserRepository {
  void someCustomMethod(User user);
}

class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  public void someCustomMethod(User user) {
    // Your custom implementation
  }
}

// 会借由CustomizedUserRepositoryImpl实现someCustomMethod方法,且优先级高于CrudRepository
interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository {

  // Declare query methods here
}

6. 事件驱动

https://www.baeldung.com/spring-data-ddd

当有save进行时,通过ApplicationEventPublisher发布@DomainEvent标记的事件,也可以继承AbstractAggregateRoot

@Entity
class AnAggregateRoot {

		// 不能有参数
    @DomainEvents 
    Collection<Object> domainEvents() {
        // … return events you want to get published here
    }

    @AfterDomainEventPublication 
    void callbackMethod() {
       // … potentially clean up domain events list
    }
}

7. REST Query Language with Spring Data JPA Specifications

https://www.baeldung.com/rest-api-search-language-spring-data-specifications

和mybatisplus里的lambda类似, 需要继承JpaSpecificationExecutor

public interface UserRepository 
  extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {}
public class UserSpecification implements Specification<User> {

    private SearchCriteria criteria;

    @Override
    public Predicate toPredicate
      (Root<User> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
 
        if (criteria.getOperation().equalsIgnoreCase(">")) {
            return builder.greaterThanOrEqualTo(
              root.<String> get(criteria.getKey()), criteria.getValue().toString());
        } 
        else if (criteria.getOperation().equalsIgnoreCase("<")) {
            return builder.lessThanOrEqualTo(
              root.<String> get(criteria.getKey()), criteria.getValue().toString());
        } 
        else if (criteria.getOperation().equalsIgnoreCase(":")) {
            if (root.get(criteria.getKey()).getJavaType() == String.class) {
                return builder.like(
                  root.<String>get(criteria.getKey()), "%" + criteria.getValue() + "%");
            } else {
                return builder.equal(root.get(criteria.getKey()), criteria.getValue());
            }
        }
        return null;
    }
}
实现自定义搜索,类似es
@Controller
public class UserController {

    @Autowired
    private UserRepository repo;

    @RequestMapping(method = RequestMethod.GET, value = "/users")
    @ResponseBody
    public List<User> search(@RequestParam(value = "search") String search) {
        UserSpecificationsBuilder builder = new UserSpecificationsBuilder();
        // Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),", Pattern.UNICODE_CHARACTER_CLASS);
        Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),");
        Matcher matcher = pattern.matcher(search + ",");
        while (matcher.find()) {
            builder.with(matcher.group(1), matcher.group(2), matcher.group(3));
        }
        
        Specification<User> spec = builder.build();
        return repo.findAll(spec);
    }
}

8. 主键策略

@GenericGenerator(name = "idGenerator", strategy = "uuid")
@GeneratedValue(generator = "idGenerator")

9. 审计

https://docs.spring.io/spring-data/jpa/docs/2.4.11/reference/html/#auditing

https://www.baeldung.com/database-auditing-jpa

@EnableJpaAuditing
@EntityListeners
@CreatedDate @CreatedBy @LastModifiedDate @LastModifiedBy
9.1. 使用@EnableJpaAuditing注解开启审计
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories
@EnableJpaAuditing
public class PersistenceConfig { ... }
9.2. 在实体上配置监听
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Bar { ... }
9.3. 配置审计字段
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Bar {
    
    //...
    
    @Column(name = "created_date", nullable = false, updatable = false)
    @CreatedDate
    private long createdDate;

    @Column(name = "modified_date")
    @LastModifiedDate
    private long modifiedDate;
    
    @Column(name = "created_by")
    @CreatedBy
    private String createdBy;

    @Column(name = "modified_by")
    @LastModifiedBy
    private String modifiedBy;
    
    //...
    
}

10. 多EntityManager

  • 设置持久化单元名称
// 设置repository的属性
@EnableJpaRepositories(
        basePackages = {"com.fan.db.repository.common"},
        entityManagerFactoryRef = "commonEntityManagerFactory",
        transactionManagerRef = "commonTransactionManager"
)

---
	@Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setGenerateDdl(true);

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setPackagesToScan("com.acme.domain");
    factory.setDataSource(dataSource());
    factory.setPersistenceUnitName("fan");// 持久化单元名称
    return factory;
  }
  • 通过JpaContext获取
class UserRepositoryImpl implements UserRepositoryCustom {

  private final EntityManager em;

  @Autowired
  public UserRepositoryImpl(JpaContext context) {
    this.em = context.getEntityManagerByManagedType(User.class);
  }

  …
}

11. 类型转换

@javax.persistence.Convert(converter = ApiSourceConverter.class)

12. 通用注解

@Version
@Column
@Enumerated
@Basic
@Transient
@NoRepositoryBean
标记该类不会被Spring Data 创建实例,如CrudRepository上就有

14. 实体关系

@OneToMany
@ManyToOne @JoinColumn

hibernate

org.hibernate.annotations.Type

@Type(type = "pg-uuid")
PostUpdateEventListener, PostLoadEventListener, PersistEventListener, MergeEventListener

15. @Query

https://www.baeldung.com/spring-data-jpa-query

15.1. jpql & native
@Query("SELECT u FROM User u WHERE u.status = 1")
Collection<User> findAllActiveUsers();

@Query(
  value = "SELECT * FROM USERS u WHERE u.status = 1", 
  nativeQuery = true)
Collection<User> findAllActiveUsersNative();
15.2. order
  • 只针对jpql时,传递额外的Sort参数,会被自动翻译为 ORDER BY语句,否则出现如下异常
org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException: Cannot use native queries with dynamic sorting and/or pagination
  • Sort定义时,只支持对象的属性名,不是数据库表的字段名
userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));
// 如下会报异常:org.springframework.data.mapping.PropertyReferenceException: No property LENGTH(name) found for type User!
userRepository.findAll(Sort.by("LENGTH(name)"));
  • 包含部分函数时,需要采用JpaSort方式进行定义
userRepository.findAllUsers(JpaSort.unsafe("LENGTH(name)"));
15.3. pagination
  • jpql
    可以传递一个PageRequest参数
@Query(value = "SELECT u FROM User u ORDER BY id")
Page<User> findAllUsersWithPagination(Pageable pageable);
  • native
    需要增加一个countQuery去统计总数
@Query(
  value = "SELECT * FROM Users ORDER BY id", 
  countQuery = "SELECT count(*) FROM Users", 
  nativeQuery = true)
Page<User> findAllUsersWithPagination(Pageable pageable);
15.4. Indexed Query Parameters

从1开始,按参数顺序,注意?开头

@Query("SELECT u FROM User u WHERE u.status = ?1")
User findUserByStatus(Integer status);

@Query("SELECT u FROM User u WHERE u.status = ?1 and u.name = ?2")
User findUserByStatusAndName(Integer status, String name);
15.5. Named Parameters

需要@Param注解,注意:开头

@Query("SELECT u FROM User u WHERE u.status = :status and u.name = :name")
User findUserByStatusAndNameNamedParams(
  @Param("status") Integer status, 
  @Param("name") String name);
15.6. Collection Parameter
@Query(value = "SELECT u FROM User u WHERE u.name IN :names")
List<User> findUserByNameList(@Param("names") Collection<String> names);
15.7. Update Queries With @Modifying

The return value defines how many rows the execution of the query updated,需要添加Modifying注解

@Modifying
@Query("update User u set u.status = :status where u.name = :name")
int updateUserSetStatusForName(@Param("status") Integer status, 
  @Param("name") String name);

// 注意这里使用?传参
@Modifying
@Query(value = "update Users u set u.status = ? where u.name = ?", 
  nativeQuery = true)
int updateUserSetStatusForNameNative(Integer status, String name);

To perform an insert operation, we have to both apply @Modifying and use a native query since INSERT is not a part of the JPA interface:

@Modifying
@Query(
  value = 
    "insert into Users (name, age, email, status) values (:name, :age, :email, :status)",
  nativeQuery = true)
void insertUser(@Param("name") String name, @Param("age") Integer age, 
  @Param("status") Integer status, @Param("email") String email);
15.8. Dynamic Query
  • Custom Repositories and the JPA Criteria API
    定义接口及实现,注意实现类的命名规范,参考本文上方的自定义接口实现, 可以参考Criterial的使用
public interface UserRepositoryCustom {
    List<User> findUserByEmails(Set<String> emails);
}

public class UserRepositoryCustomImpl implements UserRepositoryCustom {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public List<User> findUserByEmails(Set<String> emails) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = cb.createQuery(User.class);
        Root<User> user = query.from(User.class);

        Path<String> emailPath = user.get("email");

        List<Predicate> predicates = new ArrayList<>();
        for (String email : emails) {
            predicates.add(cb.like(emailPath, email));
        }
        query.select(user)
            .where(cb.or(predicates.toArray(new Predicate[predicates.size()])));

        return entityManager.createQuery(query)
            .getResultList();
    }
}
  • Extending the Existing Repository
public interface UserRepository extends JpaRepository<User, Integer>, UserRepositoryCustom {
    //  query methods from section 2 - section 7
}
  • Using the Repository
Set<String> emails = new HashSet<>();
// filling the set with any number of items

userRepository.findUserByEmails(emails);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值