hibernate-使用data jpa

来源:互联网 发布:微信群淘宝优惠券代理 编辑:程序博客网 时间:2024/05/24 07:37

简介
笔者在前面的文章中提及到过 spring-data-jpa ,什么是 jpa ,什么是 spring-data-jpa 。jpa 是 Java Persistence API的简称,是 javaEE 的 orm 规范,spring-data-jpa 是依照 jap 规范的关于数据持久层的一系列接口,在 spring 中是这样介绍 data-jpa 的:spring-data-jpa 是 spring 数据持久层的一部分,能够更轻松方便实现基于 JPA 的库,更容易构建出 spring 应用。说白了,spring-data-jpa 让我们更方便地操作持久层。

使用
1.使用 data-jpa 主要是要使用 spring 提供的一系列 Repository 接口。

// 接口族Repository<T, ID extends Serializable>    CrudRepository<T, ID extends Serializable>        PagingAndSortingRepository<T, ID extends Serializable>            JpaRepository<T, ID extends Serializable>// 用于动态查询            JpaSpecificationExecutor<T>

2.先来看看这些接口声明了那些方法

空接口

public interface Repository<T, ID extends Serializable> {}

CrudRepository 声明了基本的 CRUD 方法

public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {    // 保存或更新    <S extends T> S save(S var1);    // 批量保存或更新    <S extends T> Iterable<S> save(Iterable<S> var1);    // 根据主键查找    T findOne(ID var1);    // 根据主键判断是否存在    boolean exists(ID var1);    // 查找所有符合条件的    Iterable<T> findAll();    Iterable<T> findAll(Iterable<ID> var1);    // 统计总量    long count();    // 根据主键或其他条件删除    void delete(ID var1);    void delete(T var1);    void delete(Iterable<? extends T> var1);    void deleteAll();}

PagingAndSortingRepository 接口继承 CrudRepository ,主要增加了分页和排序功能

public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {    // 排序查找    Iterable<T> findAll(Sort var1);    // 分页查找    Page<T> findAll(Pageable var1);}

JpaRepository 接口继承 PagingAndSortingRepository 接口,扩展了一些返回参数类型为 List 的方法

public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {    List<T> findAll();    List<T> findAll(Sort var1);    List<T> findAll(Iterable<ID> var1);    <S extends T> List<S> save(Iterable<S> var1);    void flush();    <S extends T> S saveAndFlush(S var1);    void deleteInBatch(Iterable<T> var1);    void deleteAllInBatch();    T getOne(ID var1);    <S extends T> List<S> findAll(Example<S> var1);    <S extends T> List<S> findAll(Example<S> var1, Sort var2);}

JpaSpecificationExecutor 是一个高级动态查询接口,不是 Repository 系列中的子接口

public interface JpaSpecificationExecutor<T> {    T findOne(Specification<T> var1);    List<T> findAll(Specification<T> var1);    Page<T> findAll(Specification<T> var1, Pageable var2);    List<T> findAll(Specification<T> var1, Sort var2);    long count(Specification<T> var1);}

3.如何使用这些接口,你仅需要继承这些接口即可,具体实现由 data-jpa 库帮你实现,和使用 mybatis 差不过。举个栗子:

@Repositorypublic interface UserRepository extends JpaRepository<User,Long> {}

这样就可以通过 spring 自动注入到类中使用了,你可以调用上述接口中声明的任意方法完成持久化操作。一般上述方法就可以满足一般的需求,但是我们可不可以扩展这些接口呢,答案是可以的,不过在扩展接口时的方法命名需要遵循 data-jpa 的规范约束,例如:

@Repositorypublic interface UserRepository extends JpaRepository<User,Long> {    // 根据 userId 查找 user    User findByUserId(String userId);    // 根据 username 查找 user    User findByUsername(String username);    User findByUsernameAndAge(String username,int age);}

基本是使用:findBy,deleteBy,countBy,existsBy,orderBy,between ,distinct,and,or…,实体类属性名 等这些关键字拼接起来。当这些都不满足需求时,就可以通过使用 @Query 注解自己拼 hql 了。例如:

@Repositorypublic interface UserRepository extends JpaRepository<User,Long> {    User findByUserId(String userId);    User findUserByUsername(String username);    @Query("select u from User u join u.department dept where dept.departmentId = :deptId")    Page<User> findByDeptId(@Param("deptId") String deptId, Pageable pageable);    @Query("select u from User u join  u.roles role where role.roleId = :roleId")    Page<User> findByRoleId(@Param("roleId") Long roleId, Pageable pageable);    @Query("select count(u.userId) from User u join u.department dept where dept.departmentId = :deptId")    int countByDeptId(@Param("deptId") String deptId);    /*注意update需要modifying*/    @Modifying    @Query("update User u set u.flag = :flag where u.userId = :userId")    int updateUserFlag(@Param("flag") Integer flag, @Param("userId") String userId);}

一般在做联表查询是就需要使用 hql 了,hql 也是面向对象的,实体类等价于对应的表,通过打点访问类中的关联实体类属性即可做到联表。此外,很多时候我们需要动态查询,以上方案 sql 都是固定的,怎样才能动态拼接 sql 实现动态查询呢,这就要使用到 JpaSpecificationExecutor 了。再来看看这个接口的方法:

 public interface JpaSpecificationExecutor<T> {    T findOne(Specification<T> var1);    List<T> findAll(Specification<T> var1);    Page<T> findAll(Specification<T> var1, Pageable var2);    List<T> findAll(Specification<T> var1, Sort var2);    long count(Specification<T> var1);}

在声明的方法中有三个参数:Sort,Pageable ,Specification ,关键是最后的这个。Specification 就是动态拼接的 sql 的数据结构,在使用动态查询,我们需要做的是如何创建 Specification 对象。

public interface Specification<T> {    Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);}

Specification 也是一个接口,其中仅有一个方法 toPredicate , 实际上 Predicate 才真正是动态拼接 sql 的数据结构。在组装 sql 时,使用的是 root,criteriaQuery,criteriaBuilder 这三个对象。例如:

@Repositorypublic interface UserRepository extends JpaRepository<User,Long>,JpaSpecificationExecutor<User> {    public static class Specs{        /**         * 根据查询条件构造动态查询sql         * username         * userId         * deptId         * roleId         *         *root : 可以获取实体类中的属性名(等价数据库中的表,列)         *criteriaQuery:构建子查询(等价于使用 orderBy,groupBy 等)         *criteriaBuilder:构建 predicate 对象,构建 sql 片段         *         */        public static Specification<User> newQueryParams(Map<String,String> paramsMap){            return new Specification<User>() {                @Override                public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {                    // 创建 Predicate                    Predicate predicate = criteriaBuilder.conjunction();                    // 组装条件                    if(StringUtils.isNotBlank(paramsMap.get("username"))){                        predicate.getExpressions().add(criteriaBuilder.like(root.<String>get("username")                                ,"%"+ paramsMap.get("username") + "%"));                    }                    if(StringUtils.isNotBlank(paramsMap.get("userId"))){                        predicate.getExpressions().add(criteriaBuilder.equal(root.<String>get("userId"),paramsMap.get("userId")));                    }                    if(StringUtils.isNotBlank(paramsMap.get("deptId"))){                        // 注意联表的问题                        predicate.getExpressions().add(criteriaBuilder.equal(root.get("department").get("departmentId"),paramsMap.get("deptId")));                    }                    if(StringUtils.isNotBlank(paramsMap.get("roleId"))){                        predicate.getExpressions().add(criteriaBuilder.equal(root.join("roles", JoinType.LEFT).get("roleId").as(String.class), paramsMap.get("roleId")));                    }                    return predicate;                }            };        }    }}

PS: 在 hibernate 中的级联注解中,一般使用 CascadeType.ALL,如@OneToMany(cascade={CascadeType.ALL}) , 但是往往不需要级联删除,这是可以使用 hibernate 提供的注解:@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})

PS : spring-data-example 多看官方栗子,多看官方栗子,多看官方栗子

原创粉丝点击