Skip to content

❤️ 基接口 Repository

使用基接口 Repository 可以复用方法名

  • 定义基接口
java
@NoRepositoryBean
public interface BaseRepository<T> {
    List<T> findAllByCustName(String name);
}
  • 继承
java
public interface CustomerRepository extends BaseRepository<Customer>, JpaRepository<Customer, Long> {
}

❤️ 分页

静态方法

  • PageRequest of(第几页,一页的大小,Sort 排序对象) 页码从 0 开始
java
public interface CustomerRepository extends BaseRepository<Customer>, JpaRepository<Customer, Long> {
    Page<Customer> findAll(Pageable pageable);
}

@Test
public void test_findAll_pageable() {
	// 定义分页参数
	PageRequest pageRequest = PageRequest.of(  
        1,  
        5,  
        Sort.by("no").descending()  
	);
	Page<Customer> all = customerRepository.findAll(pageRequest);

	JsonNode jsonNode = objectMapper.valueToTree(all);
	System.out.println(jsonNode.toString());
}

❤️ 映射关系

如果是使用 Jpa Buddy 生成的 JPQL,则不支持映射关系,比如要执行一个 update,很麻烦,还是乖乖地手动维护吧

💛 多对多

  • 多对多中一方为维护端,一方为被维护端。关系的绑定和解除都由维护端来完成,关系被维护端不能绑定关系
  • 如果 A,B 表已经绑定了多对多的关系,那么不能直接删除从表,必须由主表解除关系后,才能删除从表;但是可以直接删除主表,删除时会先解除关系然后再删除

@ManyToMany

  • cascade[] 该属性定义了级联操作的类型,即在执行操作时,是否将这些操作级联到关联的实体上
    • CascadeType.ALL 包括以下所有
    • CascadeType.PERSIST 当持久化操作发生时,级联保存关联的实体
    • CascadeType.MERGE 当合并操作发生时,级联更新关联的实体
    • CascadeType.REMOVE 当删除操作发生时,级联删除关联的实体
    • CascadeType.REFRESH 当刷新操作发生时,级联刷新关联的实体
    • CascadeType.DETACH 当分离操作发生时,级联分离关联的实体
  • fetch 指定关联实体的加载策略
    • FetchType.LAZY 懒加载,表示只有在实际访问该关联时才会从数据库加载相关的实体
    • FetchType.EAGER 立即加载,表示在加载当前实体时会同时加载关联的实体
  • mappedBy 映射关系的被维护方,需要添加 mappedBy 来指定维护方
java
@EqualsAndHashCode(exclude = {"authors"})
@Entity
public class Book implements Serializable {
    @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
    @JoinTable(name = "book_author",
            joinColumns = {@JoinColumn(name = "book_id", referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "author_id", referencedColumnName = "id")})
    private Set<Author> authors;
}

@ToString(exclude = {"books"})
@EqualsAndHashCode(exclude = {"books"})  
@Entity  
public class Author implements Serializable {  
    @ManyToMany(mappedBy = "authors")  
    private Set<Book> books;  
}

`@ManyTOMany` 时 Lombok 生成的 @ToString,@EqualsAndHashCode 会导致两个集合循环比较,造成内存溢出,要么手写 equals 和 hashcode,要么排除该属性

java
// 主表
@EqualsAndHashCode(exclude = {"authors"})
public class Book {
    @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
    @JoinTable(name = "book_author",
            joinColumns = {@JoinColumn(name = "book_id", referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "author_id", referencedColumnName = "id")})
    private Set\<Author> authors;
}

// 从表
@ToString(exclude = {"books"})
@EqualsAndHashCode(exclude = {"books"})  
public class Author {  
    @ManyToMany(mappedBy = "authors")  
    private Set\<Book> books;  
}

触发器

#问题 jpa 怎么写触发器

存储引擎

查询时抓取

[!quote] 抓取 抓取 是从数据库中获取实体,及其关联属性的数据

实体类上

  • @NamedEntityGraph 在实体类上定义实体图,指定要抓取的属性
    • name 指定实体图的名字
    • attributeNodes 指定抓取哪个属性

仓储接口的查询方法上

  • @EntityGraph 在查询方法上应用实体图
    • value 指定实体图的名字
    • type 指定抓取策略
      • FETCH 仅抓取指定的属性,其他属性将使用默认的懒加载
      • LOAD 不仅抓取指定的属性,还会抓取其他默认设置为立即加载的属性

复用实体图

如果有一个实体图需要复用,那么可以将其定义在 Entity 上

  • 定义命名实体图
java
@Entity
@NamedEntityGraph(name = "GroupInfo.detail", attributeNodes = @NamedAttributeNode("members"))
public class GroupInfo {
	@ManyToMany
	List<GroupMember> members = new ArrayList<GroupMember>();
	// 默认抓取模式是懒加载。
}
  • 在查询方法中引用命名实体图
java
public interface GroupRepository extends CrudRepository<GroupInfo, String> {
  @EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD)
  GroupInfo getByGroupName(String name);
}

临时实体图

如果某个实体图,只在当前查询方法上使用,那就直接在当前方法上定义

java
public interface GroupRepository extends CrudRepository<GroupInfo, String> {
  @EntityGraph(attributePaths = { "members" })
  GroupInfo getByGroupName(String name);
}

不推荐 - OneToMany

  • @OneToMany
    • cascade
      • CascadeType.ALL - 对 Many 端的所有持久化实体的操作,都会自动应用到 One 端
    • orphanRemoval
      • true - One 端删除了集合中的 Many item,那在 Many 端,会删除 item 记录
java
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)  
@JoinColumn(name = "awardId")  // 指定Award表中的外键为awardId  todo  
private List<Award> awards; // 绑定的奖品集合

推荐 - Convert 存 id 集合

java
@Entity
public class RafflePool {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    
    @Column(columnDefinition = "TEXT")
    @Convert(converter = LongListToJsonConverter.class)
    private List<Long> awardIds; // 绑定的奖品集合
}

@Converter
public class LongListToJsonConverter implements AttributeConverter<List<Long>, String> {

    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 实体属性 --->>> 数据库列
     */
    @Override
    public String convertToDatabaseColumn(List<Long> longs) {
        try {
            return objectMapper.writeValueAsString(longs);
        } catch (Exception e) {
            throw new RuntimeException("Failed to convert list to JSON", e);
        }
    }

    /**
     * 数据库列 --->>> 实体属性
     */
    @Override
    public List<Long> convertToEntityAttribute(String dbData) {
        try {
            return objectMapper.readValue(dbData, new TypeReference<>() {
            });
        } catch (Exception e) {
            throw new RuntimeException("Failed to convert JSON to list", e);
        }
    }

}