Spring Data Jpa 入门
介绍:
Spring Data JPA为Java Persistence API(JPA)提供了存储库支持。它简化了需要访问JPA数据源的应用程序的开发。
核心概念:
The central interface in the Spring Data repository abstraction is Repository.
CrudRepository接口,提供crud方法。
PagingAndSortingRepository接口,提供分页查询方法。
JpaRepository在CrudRepository和PagingAndSortingRepository上扩展更多抽象方法。
查询方法:
1)设计实体Entity
@Entity(name = "admin") @Getter @Setter public class Admin implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; @Column(nullable = false, unique = true) private String account; @Column(nullable = false) private String password; @Column(nullable = false) private short status; @Column(name = "create_time") @CreationTimestamp @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") private Date createTime; @Column(name = "update_time") @UpdateTimestamp @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") private Date updateTime; @Column(name = "login_failure_num") private short loginFailureNum; @Column private String address; @Column private String latitude; @Column private String longitude; @Column private String phone; }
2) 设计仓库repository
@Repository public interface AdminRepository extends CrudRepository<Admin, Long> { Page<Admin> findAll(Pageable pageable); Optional<Admin> findById(Long id); void deleteById(Long id); @Query(value = "select a.* from admin a where a.address= :address and a.account= :account", countQuery = "select count(*) from admin a where a.address= :address and a.account= :account", nativeQuery = true ) Page<Admin> searchData(@Param("address") String address, @Param("account") String account, Pageable pageable); @Query("select a from admin a where a.name = :name or a.account = :account") List<Admin> searchData2(@Param("name") String name, @Param("account") String account, Pageable pageable); }
3)查询方式:
3.1)内置jpa查询构造器:分为主语和谓语(find…By
, exists…By
),主谓之间除了(Distinct和Top/First)之外的都当成描述信息。
3.2)注解查询:@Query,可以自定义SQL语句,通过设置nativeQuery=true。
4)排序:
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.lastname like ?1%") List<User> findByAndSort(String lastname, Sort sort); @Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%") List<Object[]> findByAsArrayAndSort(String lastname, Sort sort); } repo.findByAndSort("lannister", Sort.by("firstname")); (1) repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)")); (2) repo.findByAsArrayAndSort("bolton", Sort.by("fn_len")); (3)
5)限制:
User findFirstByOrderByLastnameAsc();//order by lastname asc limit 1 User findTopByOrderByAgeDesc();//order by age desc limit 1 Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);//where lastname=:lastname limit 10 Slice<User> findTop3ByLastname(String lastname, Pageable pageable);// where lastname=:lastname limit 0,3 List<User> findFirst10ByLastname(String lastname, Sort sort);//where lastname=:lastname limit 0,10 List<User> findTop10ByLastname(String lastname, Pageable pageable);// wehre lastname=:lastname limit 0,10
6)联结(多表查询)
6.1) 一对一(one-to-one):
每个实体实例都与另一个实体的单个实例相关。例如,为了对每个存储仓都包含一个小部件的物理仓库进行建模,StorageBin和widget将具有一对一的关系。一对一关系在相应的持久属性或字段上使用javax.persistence.OneToOne注释。
@OneToOne @JoinColumn(name = "外键", referencedColumnName = "外键对应的主键") private Object role;
6.2)一对多(one-to-many):
一个实体实例可以与其他实体的多个实例相关。例如,一个销售订单可以有多个行项目。在订单应用程序中,order将与LineItem具有一对多关系。一对多关系在相应的持久属性或字段上使用javax.persistence.OneToMany注释。
@OneToMany(mappedBy = "联结字段", fetch = FetchType.EAGER) private Set<Admin> admins;
6.3) 多对一(many-to-one):
一个实体的多个实例可以与另一实体的单个实例相关。这种多样性与一对多关系相反。在刚才提到的例子中,从LineItem的角度来看,与Order的关系是多对一的。多对一关系在相应的持久属性或字段上使用javax.persistence.ManyToOne注释
@ManyToOne @JoinColumn(name="role_id") private AdminRole role;
6.4)多对多(many-to-many):
实体实例可以相互关联。例如,在大学里,每门课程都有很多学生,每个学生可以选修几门课程。因此,在注册申请中,课程和学生将具有多对多关系。多对多关系对相应的持久属性或字段使用 javax.persistence.ManyToMany 注释。
主表
@ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "中间表", joinColumns = @JoinColumn(name = "外键", referencedColumnName = "主键"), inverseJoinColumns = @JoinColumn(name = "外键", referencedColumnName = "主键"))
private List<Object> menus;
附表
@ManyToMany(mappedBy = "menus", fetch = FetchType.EAGER) private Set<Object> adminRoles;
问题:
1)一个实体内出现两个以上List类型集合关系查询,会导致异常org.hibernate.loader.MultipleBagFetchException。
2)关联查询时出现N+1问题,导致循环查询。解决:@NamedEntityGraph和@EntityGraph
3)出现查询数据流水异常(javax.persistence.EntityNotFoundException)。解决:@NotFound(action=NotFoundAction.IGNORE)。