Spring data jpa基本使用

Spring data jpa基本使用,第1张

Spring data jpa基本使用

文章目录
  • Spring data jpa基本使用
    • 一、核心方法
    • 二、详细查询语法
    • 三、内置方法
      • 1、Sort排序
      • 2、PageRequest分页
      • 3、Example实例查询
    • 四、Spring data jpa 注解
      • 1、Repository注解
      • 2、Entity注解
    • 五、继承JpaSpecificationExecutor接口进行复杂查询
      • 1、 JpaSpecificationExecutor提供了以下接口
      • 2、首先继承JpaSpecificationExecutor
      • 3、实体类task
      • 4、然后serviceImpl层
    • 六、Spring data jpa + QueryDSL 进行复杂查询
      • 1、实体类
      • 2、单表动态分页查询
      • 3、多表动态查询
      • 4、多表动态分页查询
    • 七、改造
    • 八、参考资料

一、核心方法
List<T> findAll();                                        
List<T> findAll(Sort var1);                               
List<T> findAllById(Iterable<ID> var1);                   
<S extends T> List<S> saveAll(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);
二、详细查询语法
关键词事例对应sql
AndfindByNameAndSex(String name,String sex)… where x.name = ?1 and x.sex = ?2
OrfindByNameOrSex(String name,String sex)… where x.name = ?1 or x.sex = ?2
Is,EqualsfindBySex(String sex),
findBySexIs(String sex),
findBySexEquals(String sex)
… where x.sex = ?1
BetweenfindByStartDateBetween(String time1,String time2)… where x.startDate between ?1 and ?2
LessThanfindByAgeLessThan(Integer age)… where x.age < ?1
LessThanEqualfindByAgeLessThanEqual(Integer age)… where x.age <= ?1
GreaterThanfindByAgeGreaterThan(Integer age)… where x.age > ?1
GreaterThanEqualfindByAgeGreaterThanEqual(Integer age)… where x.age >= ?1
AfterfindByStartDateAfter(String time)… where x.startDate > ?1
BeforefindByStartDateBefore(String time)… where x.startDate < ?1
IsNullfindByAgeIsNull()… where x.age is null
IsNotNull,
NotNull
findByAgeIsNotNull(),
findByAgeNotNull()
… where x.age is not null
… where x.age not null
LikefindBySexLike(String sex)… where x.sex like ?1
NotLikefindBySexNotLike(String sex)… where x.sex not like ?1
StartingWithfindBySexStartingWith(String sex)… where x.sex like ?1 (parameter bound with appended %)
EndingWithfindBySexEndingWith(String sex)… where x.sex like ?1 (parameter bound with prepended %)
ContainingfindBySexContaining(String sex)… where x.sex like ?1 (parameter bound wrapped in %)
OrderByfindByAgeOrderByNameDesc(String age)… where x.age = ?1 order by x.name desc
NotfindByNameNot(String name)… where x.name <> ?1
InfindByAgeIn(Collection ages)… where x.age in ?1
NotInfindByAgeNotIn(Collection ages)… where x.age not in ?1
TRUEfindByActiveTrue()… where x.active = true
FALSEfindByActiveFalse()… where x.active = false
IgnoreCasefindBySexIgnoreCase(String sex)… where UPPER(x.sex) = UPPER(?1)
First,Top
默认为1
findFirst10ByName(String name),
findTop10ByName(String name)
… where x.name = ?1 limit 0,10
三、内置方法 1、Sort排序
Sort sort =new Sort(Sort.Direction.ASC,"id");
//其中第一个参数表示是降序还是升序(此处表示升序)
//第二个参数表示你要按你的 entity(记住是entity中声明的变量,不是数据库中表对应的字段)中的那个变量进行排序
2、PageRequest分页
PageRequest pageRequest = new PageRequest(index, num, sort);
//index偏移量 num查询数量 sort排序
DemoBean demoBean = new DemoBean();
demoBean.setAppId(appId); //查询条件
//创建查询参数
Example<DemoBean> example = Example.of(demoBean);
//获取排序对象
Sort sort = new Sort(Sort.Direction.DESC, "id");
//创建分页对象
PageRequest pageRequest = new PageRequest(index, num, sort);
//分页查询
return demoRepository.findAll(example, pageRequest).getContent();
3、Example实例查询
Person person = new Person();                          
person.setFirstname("Dave");  //Firstname = 'Dave'                          
 
ExampleMatcher matcher = ExampleMatcher.matching()                     
                        .withMatcher("name", GenericPropertyMatchers.startsWith()) //姓名采用“开始匹配”的方式查询
                      .withIgnorePaths("int");  //忽略属性:是否关注。因为是基本类型,需要忽略掉
 
Example<Person> example = Example.of(person, matcher);  //Example根据域对象和配置创建一个新的ExampleMatcher  
四、Spring data jpa 注解 1、Repository注解

@Modifying //做update *** 作时需要添加
@Query // 自定义Sql
@Modifying // 执行修改时使用
@Transactional //事务
@Async //异步 *** 作

@Query(value = "SELECT * FROM USERS WHERE X = ?1", nativeQuery = true)
  User findByEmailAddress(String X);
@Query("select u from User u where u.firstname = :firstname") //不加nativeQuery应使用HQL(占位符)
  User findByLastnameOrFirstname(@Param("lastname") String lastname);

@Transactional 
@Modifying
@Query("update User set firstname = ?1 where id = ?2")
  User findByLastnameOrFirstname(String lastname, String id);

@Transactional 
@Modifying
@Query("delete from User where id = ?1")
  User findByLastnameOrFirstname(String id);
2、Entity注解
@Entity //不写@Table默认为user
@Table(name="t_user") //自定义表名
public class user {
 
    @Id //主键
    @GeneratedValue(strategy = GenerationType.AUTO)//采用数据库自增方式生成主键
    //JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO.
    //TABLE:使用一个特定的数据库表格来保存主键。
    //SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
    //IDENTITY:主键由数据库自动生成(主要是自动增长型)
    //AUTO:主键由程序控制。
 
    @Transient //此字段不与数据库关联
    @Version//此字段加上乐观锁
    //字段为name,不允许为空,用户名唯一
    @Column(name = "name", unique = true, nullable = false)
    private String name;
 
    @Temporal(TemporalType.DATE)//生成yyyy-MM-dd类型的日期
    //出参时间格式化
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    //入参时,请求报文只需要传入yyyymmddhhmmss字符串进来,则自动转换为Date类型数据
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
    private Date createTime;
 
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
五、继承JpaSpecificationExecutor接口进行复杂查询 1、 JpaSpecificationExecutor提供了以下接口
public interface JpaSpecificationExecutor {
 
    T findOne(Specification spec);
 
    List findAll(Specification spec);
 
    Page findAll(Specification spec, Pageable pageable);
 
    List findAll(Specification spec, Sort sort);
 
    long count(Specification spec);
}
 
//其中Specification就是需要我们传入查询方法的参数,它是一个接口
 
 
public interface Specification {
 
    Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb);
}

JPA 2.0 criteria api 参考:
http://blog.csdn.net/dracotianlong/article/details/28445725

http://developer.51cto.com/art/200911/162722.htm

2、首先继承JpaSpecificationExecutor
public interface TaskDao extends JpaSpecificationExecutor<Task>{
}
3、实体类task
@Entity
@Table(name = "tb_task")
public class Task {
 
    private Long id ;
    private String taskName ;
    private Date createTime ;
    private Project project;
    private String taskDetail ;
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
 
    @Column(name = "task_name")
    public String getTaskName() {
        return taskName;
    }
    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }
 
    @Column(name = "create_time")
    @DateTimeFormat(pattern = "yyyy-MM-dd hh:mm:ss")
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
 
 
    @Column(name = "task_detail")
    public String getTaskDetail() {
        return taskDetail;
    }
    public void setTaskDetail(String taskDetail) {
        this.taskDetail = taskDetail;
    }
 
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "project_id")
    public Project getProject() {
        return project;
    }
    public void setProject(Project project) {
        this.project = project;
    }
 
}
4、然后serviceImpl层

通过重写toPredicate方法,返回一个查询 Predicate,spring data jpa会帮我们进行查询。

@Service
public class TaskService {
 
    @Autowired TaskDao taskDao ;
 
 
    /**
     * 复杂查询测试
     * @param page
     * @param size
     * @return
     */
    public Page<Task> findBySepc(int page, int size){
 
        PageRequest pageReq = this.buildPageRequest(page, size);
        Page<Task> tasks = this.taskDao.findAll(new MySpec(), pageReq);
        //传入了new MySpec() 既下面定义的匿名内部类 其中定义了查询条件
        return tasks;
 
    }
 
     /**
      * 建立分页排序请求 
      * @param page
      * @param size
      * @return
      */
     private PageRequest buildPageRequest(int page, int size) {
           Sort sort = new Sort(Direction.DESC,"createTime");
           return new PageRequest(page,size, sort);
     }
 
    /**
     * 建立查询条件
     */
    private class MySpec implements Specification<Task>{
 
        @Override
        public Predicate toPredicate(Root<Task> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
 
     //1.混合条件查询
          Path<String> exp1 = root.get("taskName");
            Path<Date>  exp2 = root.get("createTime");
            Path<String> exp3 = root.get("taskDetail");
            Predicate predicate = cb.and(cb.like(exp1, "%taskName%"),cb.lessThan(exp2, new Date()));
            return cb.or(predicate,cb.equal(exp3, "kkk"));
 
           /* 类似的sql语句为:
            Hibernate: 
                select
                    count(task0_.id) as col_0_0_ 
                from
                    tb_task task0_ 
                where
                    (
                        task0_.task_name like ?
                    ) 
                    and task0_.create_time
 
    //2.多表查询
        Join<Task,Project> join = root.join("project", JoinType.INNER);
            Path<String> exp4 = join.get("projectName");
            return cb.like(exp4, "%projectName%");
 
           /* Hibernate: 
            select
                count(task0_.id) as col_0_0_ 
            from
                tb_task task0_ 
            inner join
                tb_project project1_ 
                    on task0_.project_id=project1_.id 
            where
                project1_.project_name like ?*/ 
           return null ;  
        }
    }
}

如果你觉得,每次都要写一个类来实现Specification很麻烦,你可以这么写

public class TaskSpec {
 
    public static Specification<Task> method1(){
 
        return new Specification<Task>(){
            @Override
            public Predicate toPredicate(Root<Task> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return null;
            }
 
        };
    }
 
    public static Specification<Task> method2(){
 
        return new Specification<Task>(){
            @Override
            public Predicate toPredicate(Root<Task> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return null;
            }
 
        };
    }
 
}

那么用的时候,我们就这么用

Page<Task> tasks = this.taskDao.findAll(TaskSpec.method1(), pageReq);
六、Spring data jpa + QueryDSL 进行复杂查询
  1. QueryDSL仅仅是一个通用的查询框架,专注于通过Java API构建类型安全的SQL查询。
  2. Querydsl可以通过一组通用的查询API为用户构建出适合不同类型ORM框架或者是SQL的查询语句,也就是说QueryDSL是基于各种ORM框架以及SQL之上的一个通用的查询框架。
  3. 借助QueryDSL可以在任何支持的ORM框架或者SQL平台上以一种通用的API方式来构建查询。目前QueryDSL支持的平台包括JPA,JDO,SQL,Java Collections,RDF,Lucene,Hibernate Search。
1、实体类
//城市类
@Entity
@Table(name = "t_city", schema = "test", catalog = "")
public class TCity {
    //省略JPA注解标识
    private int id;
    private String name;
    private String state;
    private String country;
    private String map;
}

//旅馆类
@Entity
@Table(name = "t_hotel", schema = "test", catalog = "")
public class THotel {
    //省略JPA注解标识
    private int id;
    private String name;
    private String address;
    private Integer city;//保存着城市的id主键
}
2、单表动态分页查询

Spring Data JPA中提供了QueryDslPredicateExecutor接口,用于支持QueryDSL的查询 *** 作

public interface tCityRepository extends JpaRepository<TCity, Integer>, QueryDslPredicateExecutor<TCity> {
}

这样的话单表动态查询就可以参考如下代码:

//查找出Id小于3,并且名称带有`shanghai`的记录.
 
//动态条件
QTCity qtCity = QTCity.tCity; //SDL实体类
//该Predicate为querydsl下的类,支持嵌套组装复杂查询条件
Predicate predicate = qtCity.id.longValue().lt(3).and(qtCity.name.like("shanghai"));
//分页排序
Sort sort = new Sort(new Sort.Order(Sort.Direction.ASC,"id"));
PageRequest pageRequest = new PageRequest(0,10,sort);
//查找结果
Page<TCity> tCityPage = tCityRepository.findAll(predicate,pageRequest);
3、多表动态查询
 /**
     * 关联查询示例,查询出城市和对应的旅店
     * @param predicate 查询条件
     * @return 查询实体
     */
    @Override
    public List<Tuple> findCityAndHotel(Predicate predicate) {
        JPAQueryFactory queryFactory = new JPAQueryFactory(em);
        JPAQuery<Tuple> jpaQuery = queryFactory.select(QTCity.tCity,QTHotel.tHotel)
                                        .from(QTCity.tCity)
                                        .leftJoin(QTHotel.tHotel)
                                        .on(QTHotel.tHotel.city.longValue().eq(QTCity.tCity.id.longValue()));
        //添加查询条件
        jpaQuery.where(predicate);
        //拿到结果
        return jpaQuery.fetch();
    }

城市表左连接旅店表,当该旅店属于这个城市时查询出两者的详细字段,存放到一个Tuple的多元组中.相比原生sql,简单清晰了很多.
那么该怎么调用这个方法呢?

@Test
    public void findByLeftJoin(){
        QTCity qtCity = QTCity.tCity;
        QTHotel qtHotel = QTHotel.tHotel;
        //查询条件
        Predicate predicate = qtCity.name.like("shanghai");
        //调用
        List<Tuple> result = tCityRepository.findCityAndHotel(predicate);
        //对多元组取出数据,这个和select时的数据相匹配
        for (Tuple row : result) {
            System.out.println("qtCity:"+row.get(qtCity));
            System.out.println("qtHotel:"+row.get(qtHotel));
            System.out.println("--------------------");
        }
        System.out.println(result);
    }

这样做的话避免了返回Object[]数组,下面是自动生成的sql语句:

select
        tcity0_.id as id1_0_0_,
        thotel1_.id as id1_1_1_,
        tcity0_.country as country2_0_0_,
        tcity0_.map as map3_0_0_,
        tcity0_.name as name4_0_0_,
        tcity0_.state as state5_0_0_,
        thotel1_.address as address2_1_1_,
        thotel1_.city as city3_1_1_,
        thotel1_.name as name4_1_1_ 
    from
        t_city tcity0_ 
    left outer join
        t_hotel thotel1_ 
            on (
                cast(thotel1_.city as signed)=cast(tcity0_.id as signed)
            ) 
    where
        tcity0_.name like ? escape '!'
4、多表动态分页查询

分页查询对于queryDSL无论什么样的sql只需要写一遍,会自动转换为相应的count查询

 @Override
 public QueryResults<Tuple> findCityAndHotelPage(Predicate predicate,Pageable pageable) {
    JPAQueryFactory queryFactory = new JPAQueryFactory(em);
    JPAQuery<Tuple> jpaQuery = queryFactory.select(QTCity.tCity.id,QTHotel.tHotel)
                                               .from(QTCity.tCity)
                                               .leftJoin(QTHotel.tHotel)
                                               .on(QTHotel.tHotel.city.longValue().eq(QTCity.tCity.id.longValue()))
                                               .where(predicate)
                                               .offset(pageable.getOffset())
                                               .limit(pageable.getPageSize());
    //拿到分页结果
    return jpaQuery.fetchResults();
}

和上面不同之处在于这里使用了offsetlimit限制查询结果.并且返回一个QueryResults,该类会自动实现count查询和结果查询,并进行封装.
调用形式如下:

    @Test
    public void findByLeftJoinPage(){
        QTCity qtCity = QTCity.tCity;
        QTHotel qtHotel = QTHotel.tHotel;
        //条件
        Predicate predicate = qtCity.name.like("shanghai");
        //分页
        PageRequest pageRequest = new PageRequest(0,10);
        //调用查询
        QueryResults<Tuple> result = tCityRepository.findCityAndHotelPage(predicate,pageRequest);
        //结果取出
        for (Tuple row : result.getResults()) {
            System.out.println("qtCity:"+row.get(qtCity));
            System.out.println("qtHotel:"+row.get(qtHotel));
            System.out.println("--------------------");
        }
        //取出count查询总数
        System.out.println(result.getTotal());
    }

生成的原生count查询sql,当该count查询结果为0的话,则直接返回,并不会再进行具体数据查询:

select
    count(tcity0_.id) as col_0_0_ 
from
    t_city tcity0_ 
left outer join
    t_hotel thotel1_ 
        on (
            cast(thotel1_.city as signed)=cast(tcity0_.id as signed)
        ) 
where
    tcity0_.name like ? escape '!'
select
    tcity0_.id as id1_0_0_,
    thotel1_.id as id1_1_1_,
    tcity0_.country as country2_0_0_,
    tcity0_.map as map3_0_0_,
    tcity0_.name as name4_0_0_,
    tcity0_.state as state5_0_0_,
    thotel1_.address as address2_1_1_,
    thotel1_.city as city3_1_1_,
    thotel1_.name as name4_1_1_ 
from
    t_city tcity0_ 
left outer join
    t_hotel thotel1_ 
        on (
            cast(thotel1_.city as signed)=cast(tcity0_.id as signed)
        ) 
where
    tcity0_.name like ? escape '!' limit ?

查看打印,可以发现对应的city也都是同一个对象,hotel是不同的对象.

七、改造
JPAQueryFactory factory = new JPAQueryFactory(entityManager);
        factory.select($.pcardCardOrder)
               .select($.pcardVcardMake.vcardMakeDes)
               .select($.pcardVtype.cardnumRuleId,$.pcardVtype.vtypeNm)
               .select($.pcardCardbin)
               .leftJoin($.pcardVcardMake).on($.pcardCardOrder.makeId.eq($.pcardVcardMake.vcardMakeId))
               //......省略

查询条件使用Predicate代替,放在service拼接,或者写一个生产条件的工厂都可以.

 jpaQuery.where(predicate);

最后的分页处理就和之前的一样了

jpaQuery.offset(pageable.getOffset())
    .limit(pageable.getPageSize());
return jpaQuery.fetchResults();
八、参考资料
  1. 使用QueryDSL
  2. Spring Data JPA 实例查询
  3. Spring Data JPA - Reference Documentation
  4. Querydsl Reference Guide

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/737702.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-28
下一篇 2022-04-28

发表评论

登录后才能评论

评论列表(0条)

保存