Spring Boot第八章-Spring Data JPA(续)

Spring Boot第八章-Spring Data JPA(续)

                                                                          Spring Data JPA(续)

目录

1.jpa自定义sql查询

2.jpa更新

3.jpa删除

4.一些常用注解的理解

5.自定义查询简单实例:

6.JPA 关联表自定义动态查询


上一个博客介绍了Spring Data JPA,但是总感觉不够,因而加了此篇博客作为续,以后关于JPA的东西都写在这篇文章里,毕竟在实际运用中会遇到很多需要注意的地方。

 

1.jpa自定义sql查询

直接上代码:

 //自定义查询
    @Query(value = "select * from person where name=?1 and age>18",nativeQuery = true)
    List<Person> findByMyQuery(String name);

此处跟之前的@Query有区别,nativeQuery=true,这样的话就用本地的查询语句了,根据value里面的正常的sql语句进行查询,注意这里写的就是真实的表名了。而如果不是nativeQuery=true,写的是类名。

2.jpa更新

直接上代码:

 //更新操作(更新,删除的操作必须有事务,这个事务可以在这里写,也可以在service里写)
    @Transactional
    @Modifying
    @Query(value = "update Person set age=?2 where id=?1")
    int updatePersonAge(Long id,int age);

注意,我在这里加了事务,还有@Modifying,这个是必须的

3.jpa删除

直接上代码:

 @Transactional
    //删除操作
    int deleteByAge(int age);

同样的,也得加事务,这个事务也可以写在service里

4.一些常用注解的理解

@Entity和@Table的区别:

@Entity说明这个class是实体类,并且使用默认的orm规则,即class名即数据库表中表名,class字段名即表中的字段名
如果想改变这种默认的orm规则,就要使用@Table来改变class名与数据库中表名的映射规则

@Column:

改变class中字段名与db中表的字段名的映射规则

具体见以下描述:

@Entity注释指名这是一个实体Bean,@Table注释指定了Entity所要映射带数据库表,其中@Table.name()用来指定映射表的表名。
如果缺省@Table注释,系统默认采用类名作为映射表的表名。实体Bean的每个实例代表数据表中的一行数据,行中的一列对应实例中的一个属性。

@Column注释定义了将成员属性映射到关系表中的哪一列和该列的结构信息,属性如下:
1)name:映射的列名。如:映射tbl_user表的name列,可以在name属性的上面或getName方法上面加入;
2)unique:是否唯一;
3)nullable:是否允许为空;
4)length:对于字符型列,length属性指定列的最大字符长度;
5)insertable:是否允许插入;
6)updatetable:是否允许更新;
7)columnDefinition:定义建表时创建此列的DDL;
8)secondaryTable:从表名。如果此列不建在主表上(默认是主表),该属性定义该列所在从表的名字。
@Id注释指定表的主键,它可以有多种生成方式:

1)TABLE:容器指定用底层的数据表确保唯一;
2)SEQUENCE:使用数据库德SEQUENCE列莱保证唯一(Oracle数据库通过序列来生成唯一ID);
3)IDENTITY:使用数据库的IDENTITY列莱保证唯一;
4)AUTO:由容器挑选一个合适的方式来保证唯一;
5)NONE:容器不负责主键的生成,由程序来完成。

@GeneratedValue注释定义了标识字段生成方式。

@Temporal注释用来指定java.util.Date或java.util.Calender属性与数据库类型date、time或timestamp中的那一种类型进行映射。

@Temporal(value=TemporalType.TIME)

实例如下:

package com.just.springjpa.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;
import java.util.Date;

/**
 * 1.@Entity说明这个class是实体类,并且使用默认的orm规则,即class名即数据库表中表名,class字段名即表中的字段名
 * 如果想改变这种默认的orm规则,就要使用@Table来改变class名与数据库中表名的映射规则,
 * 2.@Column来改变class中字段名与db中表的字段名的映射规则
 */
@Entity
@Table(name = "t_student")
public class Student {
    @Id
    @GeneratedValue
    private Long id;
    /**
     * varchar类型长度50
     */
    @Column(name = "s_name",length = 50)
    private String name;
    /**
     * 不允许为空
     */
    @Column(name="s_age",nullable = false)
    private Integer age;
    /**
     * columnDefinition 创建表时,该字段创建的SQL语句,更灵活的创建表
     */
    @Column(name="created_time",columnDefinition = "datetime COMMENT '创建时间' ")
    private Date createdTime;
    /**
     * 精度12,小数点后2位
     */
    @Column(name="money",precision = 12,scale = 2)
    private BigDecimal money;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getCreatedTime() {
        return createdTime;
    }

    public void setCreateTime(Date createdTime) {
        this.createdTime = createdTime;
    }
}

具体效果可以看下自己生成的表:

Spring Boot第八章-Spring Data JPA(续)

2018-08-07       


5.自定义查询简单实例:

还是用到了上一篇博客的Specification,简化版,提供的是一种直接简单用的思路,java8的函数式编程风格

1.repository要继承JpaSpecificationExecutor

public interface PersonRepository extends JpaRepository<Person, Long>  ,JpaSpecificationExecutor<Person> {
}

来看看JpaSpecificationExecutor源码,我们可以用里面的一些方法查询,其中还有分页的查询:

/*
 * Copyright 2008-2011 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.jpa.repository;

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;

/**
 * Interface to allow execution of {@link Specification}s based on the JPA criteria API.
 * 
 * @author Oliver Gierke
 */
public interface JpaSpecificationExecutor<T> {

	/**
	 * Returns a single entity matching the given {@link Specification}.
	 * 
	 * @param spec
	 * @return
	 */
	T findOne(Specification<T> spec);

	/**
	 * Returns all entities matching the given {@link Specification}.
	 * 
	 * @param spec
	 * @return
	 */
	List<T> findAll(Specification<T> spec);

	/**
	 * Returns a {@link Page} of entities matching the given {@link Specification}.
	 * 
	 * @param spec
	 * @param pageable
	 * @return
	 */
	Page<T> findAll(Specification<T> spec, Pageable pageable);

	/**
	 * Returns all entities matching the given {@link Specification} and {@link Sort}.
	 * 
	 * @param spec
	 * @param sort
	 * @return
	 */
	List<T> findAll(Specification<T> spec, Sort sort);

	/**
	 * Returns the number of instances that the given {@link Specification} will return.
	 * 
	 * @param spec the {@link Specification} to count instances for
	 * @return the number of instances
	 */
	long count(Specification<T> spec);
}

2.在service方法里面使用,我是用的Page<T> findAll(Specification<T> spec, Pageable pageable);,自定义+分页

  @Override
    public Page<Person> findDynamically(Person person, Pageable pageable,Map<String,Object> otherParams) {
        return personRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            //如果是查名字,按照名称模糊查询
            if(StringUtils.isNotBlank(person.getNickName())){
               predicates.add(criteriaBuilder.like(root.get("nickName"),"%"+person.getNickName()+"%"));
            }
            //所在城市,精确搜索
            if(StringUtils.isNotBlank(person.getCity())){
                predicates.add(criteriaBuilder.equal(root.get("city"),person.getCity()));
            }
            //审核状态,精确搜索
            if(person.getAuditStatus()!=null){
                predicates.add(criteriaBuilder.equal(root.get("auditStatus"),person.getAuditStatus()));
            }
            //比较日期
            if(otherParams.containsKey("afterEndTime")){
               predicates.add(criteriaBuilder.greaterThan(root.get("authEndTime"),new Date()));
            }
            if(otherParams.containsKey("beforeEndTime")){
                predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("authEndTime"),new Date()));
            }
            //比较年龄范围,左闭右开
            if(otherParams.get("minAge")!=null){
                predicates.add(criteriaBuilder.greaterThan(root.get("age"),Integer.parseInt(otherParams.get("minAge").toString())));
            }
            if(otherParams.get("maxAge")!=null){
                predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("age"),Integer.parseInt(otherParams.get("maxAge").toString())));
            }

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        },pageable);
    }

一切尽在代码中…

本示例集合了几种常见查询,模糊搜索,精确搜索,日期范围搜索等等,比较实用,可以参考。criteriaBuilder有很多查询匹配的方法,可以满足绝大部分查询需求,具体的可以在使用中看下里面的方法。

其实Person的所有属性都可以作为查询条件,可以利用java反射拿到所有属性,然后来个循环每个属性都可以加进去。

2018-08-24


6.JPA 关联表自定义动态查询

在实际业务中,可能要关联表查询,并且查询条件是动态的,这就需要在自定义查询的基础上再来一波。

1.新建一个分数类,跟student是多对一的关系

package com.just.springjpa.domain;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
@Entity
@Table(name = "course_score")
public class CourseScore {
    @Id
    @GeneratedValue
    private Long id;
    private BigDecimal score;
    private Date createDate;
    private String course;
    @ManyToOne
    @JoinColumn(name = "student_id")
    private Student student;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public BigDecimal getScore() {
        return score;
    }

    public void setScore(BigDecimal score) {
        this.score = score;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public String getCourse() {
        return course;
    }

    public void setCourse(String course) {
        this.course = course;
    }

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }
}

2.student中可以再加个分数列表,一对多,懒加载,(这个list也可以不加,需要用到时可以再加)

    @JsonIgnore
    @OneToMany(fetch = FetchType.LAZY)
    private List<CourseScore> courseScoreList;

3.自定义查询,我直接写在了controller里了

repository就省略了,跟PersonRepository 一个写法

 /**
     * 根据学生姓名和最低分数查询
     * @param name 学生姓名,可为空
     * @param minScore 最低分数,可为空
     * @param pageable 分页参数
     * @return 查询到的分数结果
     */
    @GetMapping("/getScore")
    public Page<CourseScore> getScore(@RequestParam(required = false) String name,
                                      @RequestParam(required = false) BigDecimal minScore,
                                      @PageableDefault(size = 20) Pageable pageable){
        //关联动态查询
        Page<CourseScore> courseScores=courseScoreRepository.findAll(new Specification<CourseScore>() {
            @Override
            public Predicate toPredicate(Root<CourseScore> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicatesList = new ArrayList<>();
                //join里面的参数写法有好几种,这里采用最简单的一种,直接写属性名,关联的Student,内联
                Join<Student,CourseScore> join=root.join("student",JoinType.INNER);
                //注意name属性是在Student里面的,而join里面比较的属性都是关联的那张表里的字段,用join来获取
                if(!StringUtils.isEmpty(name)){
//                    predicatesList.add(
//                            criteriaBuilder.equal(
//                                    join.get("name"), name
//                            )
//                    );
                    predicatesList.add(
                            criteriaBuilder.like(
                                    join.get("name"), name
                            )
                    );
                }
                //分数是CourseScore里的属性,用root获取
                if(minScore!=null){
                    predicatesList.add(
                            criteriaBuilder.greaterThanOrEqualTo(
                                    root.get("score"), minScore
                            )
                    );
                }
                return criteriaBuilder.and(predicatesList.toArray(new Predicate[0]));
            }
        },pageable);
        return courseScores;
    }

4.测试结果

 

Spring Boot第八章-Spring Data JPA(续)

后台打印的sql:

Hibernate: select coursescor0_.id as id1_0_, coursescor0_.course as course2_0_, coursescor0_.create_date as create_d3_0_, coursescor0_.score as score4_0_, coursescor0_.student_id as student_5_0_ from course_score coursescor0_ inner join t_student student1_ on coursescor0_.student_id=student1_.id where (student1_.s_name like ?) and coursescor0_.score>=60 limit ?
Hibernate: select student0_.id as id1_2_0_, student0_.s_age as s_age2_2_0_, student0_.created_time as created_3_2_0_, student0_.money as money4_2_0_, student0_.s_name as s_name5_2_0_ from t_student student0_ where student0_.id=?

由此看出是inner join,并且join表的顺序也是对的,参数也是对的。当然这只是测试,不合理的地方请见谅。

2018-09-11

Spring Boot第八章-Spring Data JPA(续)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/100283.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 操作系统知识整理 – 进程控制块

    操作系统知识整理 – 进程控制块前提系统中需要有描述进程存在和能够反映其变化的物理实体,即进程的静态描述。进程的静态描述由3部分组成:进程控制块(ProcessControlBlock,PCB),有关程序段和该程序段操作的数据结构集。PCB是系统感知进程的唯一实体,用于描述进程的当前情况以及管理进程运行的全部信息,是操作系统中最重要的记录型数据结构。程序段以及数据结构集是进程完成所需功能的物质基础。一个进…

    2025年6月25日
    3
  • Nginx配置反向代理

    Nginx配置反向代理Nginx配置反向代理,什么是反向代理反向代理服务器决定哪台服务器提供服务。返回代理服务器不提供服务器。只是请求的转发。

    2022年7月14日
    24
  • C语言求最大公约数和最小公倍数(思路清晰+拓展)[通俗易懂]

    C语言求最大公约数和最小公倍数(思路清晰+拓展)[通俗易懂]最大公约数的求法首先了解它的一般求法(欧几里得算法):假设存在两个数A和B,假如A%B的结果不为0,那么A和B的最大公约数是B与A%B的最大公约数,一直往下计算,直到后者为0,此时的最大公约数为A’(注意不是A而是A’)。就比如上边的例子,当A%B==0的时候,最大公约数就是B了,这个A’就代表B。最大公约数的代码:(基于C++实现的函数)intgcd(inta,intb){ in…

    2022年5月16日
    52
  • Java数组及二维数组初始化与赋值方法总结

    Java数组及二维数组初始化与赋值方法总结1.定义数组直接赋值inta[]={1,2,3,4,5};2.用new在数组中创建元素inta[]=newint[]{1,2,3};注意:后面的[]不要忘了3.创建指定大小数组,但不赋值 intc[]=newint[3]; System.out.println(Arrays.toString(c));注意:基本类型会自动初始化为空值,int型则为0.输出结果为[0,0,0],Arrays.toString产生一维数组的可打印版本。4.先声明再赋值 intc[

    2022年5月25日
    112
  • pyyaml使用_pam模块

    pyyaml使用_pam模块pyyaml模块

    2022年9月13日
    2
  • initramfs-kernel_ubuntu initramfs

    initramfs-kernel_ubuntu initramfsLinux系统启动时使用initramfs(initramfilesystem),initramfs可以在启动早期提供一个用户态环境,借助它可以完成一些内核在启动阶段不易完成的工作。当然initramfs是可选的,Linux中的内核编译选项默认开启initrd。在下面的示例情况中你可能要考虑用initramfs。加载模块,比如第三方driver定制化启动过程(比如打印welcomeme…

    2022年8月11日
    8

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号