SpringBoot之SpringDataJpa-入门学习-2

1. JPA自定义

具体的关键字,使用方法和生产成SQL如下表:

KeyWord Sample JPQL Snippet
And findByLastnameAndFirstname where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstnameIs,findByFirstnameEquals where x.firstname = ?1
Between findByStartDateBetween where x.startDate between ?1 and ?2
LessThan findByAgeLessThan where x.age < ?1
LessThanEqual findByAgeLessThanEqual where x.age ⇐ ?1
GreaterThan findByAgeGreaterThan where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual where x.age >= ?1
After findByStartDateAfter where x.startDate > ?1
Before findByStartDateBefore where x.startDate < ?1
IsNull findByAgeIsNull where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull where x.age not null
Like findByFirstnameLike where x.firstname like ?1
NotLike findByFirstnameNotLike where x.firstname not like ?1
StartingWith findByFirstnameStartingWith where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc where x.age = ?1 order by x.lastname desc
Not findByLastnameNot where x.lastname <> ?1
In findByAgeIn(Collection ages) where x.age in ?1
NotIn findByAgeNotIn(Collection age) where x.age not in ?1
TRUE findByActiveTrue() where x.active = true
FALSE findByActiveFalse() where x.active = false
IgnoreCase findByFirstnameIgnoreCase where UPPER(x.firstame) = UPPER(?1)

2. 动态SQL

Spring-data-jpa已经很方便了,但是有时候我们有的参数为空,这时我们不想让这些参数作为条件查询,笨办法就是去写n个方法,当然也有好办法了,那就是JpaSpecificationExecutor:

继承JpaSpecificationExecutor

package com.yb.dao;

import com.yb.domain.HiTaskinst;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

/**
 * @author [email protected]
 * @version 1.0
 * @date 2020/7/31
 */
public interface HiTaskinstRepository extends JpaRepository<HiTaskinst,String>
                , JpaSpecificationExecutor<HiTaskinst> {
    
    
    /**
     * 通过名称查询
     * @param name
     * @return
     */
    List<HiTaskinst> findByNameLike(String name);

    /**
     * 原生SQL查询
     * @param name
     * @return
     */
    @Query(value = "select ht.* from `act_hi_taskinst` ht WHERE ht.`NAME_` = ?1",
           nativeQuery = true)
    @Modifying
    List<HiTaskinst> findHiTaskinstByName(String name);

    /**
     * 分页查询
     * @param pageable
     * @return
     */
    Page<HiTaskinst> findAll(Pageable pageable);

    /**
     * 分页查询+条件(根据名称查询)
     * @param name
     * @param pageable
     * @return
     */
    Page<HiTaskinst> findByNameLike(String name,Pageable pageable);
}

JpaController

/**
     * 分页查询+条件+动态
     * @return
     */
    @PostMapping("/test07")
    private List<HiTaskinst> test07(@RequestBody HiTaskinstVo hiTaskinstVo) {
    
    
        System.out.println("条件分页+动态SQL:"+hiTaskinstVo);

        Page<HiTaskinst> pages = jpaService.test07(hiTaskinstVo);
        List<HiTaskinst> content = pages.getContent();
        System.out.println(content.size());
        return content;
    }

vo

package com.yb.vo;

import lombok.Data;

/**
 * @author [email protected]
 * @version 1.0
 * @date 2020/8/3
 */
@Data
public class HiTaskinstVo {
    
    
    private String name;
    private Integer pageNum;
    private Integer pageSize;
}

JpaServiceImpl

/**
     * 动态Sql
     * @param hiTaskinstVo
     * @return
     */
    @Override
    public Page<HiTaskinst> test07(HiTaskinstVo hiTaskinstVo) {
    
    
        //动态查询

        Specification<HiTaskinst> specification = new Specification<HiTaskinst>() {
    
    
            @Override
            public Predicate toPredicate(Root<HiTaskinst> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
    
    
                Predicate p = null;
                //判断是否为空或者null,进行动态的查询
                if(!hiTaskinstVo.getName().equals("")&&hiTaskinstVo.getName()!=null){
    
    
                    //不为空或者null就进行模糊查询条件设置
                    p = criteriaBuilder.like(root.get("name"),"%"+hiTaskinstVo.getName()+"%");
                }else {
    
    
                    //空或者null就进行全部查询条件设置
                    p = criteriaBuilder.like(root.get("name"),"%%");
                }
                criteriaQuery.where(p);
                return null;
            }
        };
        //分页,从0开始
        PageRequest of = PageRequest.of(hiTaskinstVo.getPageNum()-1, hiTaskinstVo.getPageSize());
        //查询
        Page<HiTaskinst> all = hiTaskinstRepository.findAll(specification, of);
        return all;
    }

测试
查询条件为 “班”,进行模糊查询
在这里插入图片描述
查询条件为空,进行全部查询,第一页,一页三条数据。
在这里插入图片描述
在这里插入图片描述

3. JPA中的一对多(多对一)

3.1 示例分析

我们采用的示例为客户和联系人。
客户:指的是一家公司,我们记为A。
联系人:指的是A公司中的员工。
在不考虑兼职的情况下,公司和员工的关系即为一对多,反之就是多对一。

3.2 表关系建立

在一对多关系中,我们习惯把一的一方称之为主表,把多的一方称之为从表。在数据库中建立一对多的关系,需要使用数据库的外键约束。

什么是外键?
    指的是从表中有一列,取值参照主表的主键,这一列就是外键。

一对多数据库关系的建立,如下图所示:
在这里插入图片描述

/*创建客户表*/
CREATE TABLE cst_customer (
	cust_id BIGINT (32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
	cust_name VARCHAR (32) NOT NULL COMMENT '客户名称(公司名称)',
	cust_source VARCHAR (32) DEFAULT NULL COMMENT '客户信息来源',
	cust_industry VARCHAR (32) DEFAULT NULL COMMENT '客户所属行业',
	cust_level VARCHAR (32) DEFAULT NULL COMMENT '客户级别',
	cust_address VARCHAR (128) DEFAULT NULL COMMENT '客户联系地址',
	cust_phone VARCHAR (64) DEFAULT NULL COMMENT '客户联系电话',
	PRIMARY KEY (`cust_id`)
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;

/*创建联系人*/
CREATE TABLE `cst_linkman` (
	`lkm_id` BIGINT (32) NOT NULL AUTO_INCREMENT,
	`lkm_name` VARCHAR (32) DEFAULT NULL,
	`lkm_gender` VARCHAR (32) DEFAULT NULL,
	`lkm_phone` VARCHAR (32) DEFAULT NULL,
	`lkm_mobile` VARCHAR (32) DEFAULT NULL,
	`lkm_email` VARCHAR (32) DEFAULT NULL,
	`lkm_position` VARCHAR (32) DEFAULT NULL,
	`lkm_memo` VARCHAR (32) DEFAULT NULL,
	`lkm_cust_id` BIGINT (32) DEFAULT NULL,
	PRIMARY KEY (`lkm_id`),
	FOREIGN KEY (`lkm_cust_id`) REFERENCES cst_customer(`cust_id`) ON DELETE SET NULL
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;

3.3 实体类关系建立以及映射配置

在实体类中,由于客户是少的一方,它应该包含多个联系人,所以实体类要体现出客户中有多个联系人的信息,代码如下:

package com.yb.domain;

import lombok.Data;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Set;

/**
 * @author [email protected]
 * @version 1.0
 * @date 2020/8/3
 */
@Data
@Entity
@Table(name = "cst_customer")
public class Customer implements Serializable {
    
    
    @Id//表明当前私有属性是主键
    @GeneratedValue(strategy= GenerationType.IDENTITY)//指定主键的生成策略
    @Column(name="cust_id")//指定和数据库表中的cust_id列对应
    private Long custId;
    @Column(name="cust_name")//指定和数据库表中的cust_name列对应
    private String custName;
    @Column(name="cust_source")//指定和数据库表中的cust_source列对应
    private String custSource;
    @Column(name="cust_industry")//指定和数据库表中的cust_industry列对应
    private String custIndustry;
    @Column(name="cust_level")//指定和数据库表中的cust_level列对应
    private String custLevel;
    @Column(name="cust_address")//指定和数据库表中的cust_address列对应
    private String custAddress;
    @Column(name="cust_phone")//指定和数据库表中的cust_phone列对应
    private String custPhone; //配置客户和联系人的一对多关系


    @OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
    @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
    private Set<LinkMan> linkmans;

    @Override
    public String toString() {
    
    
        return "Customer{" +
                "custId=" + custId +
                ", custName='" + custName + '\'' +
                ", custSource='" + custSource + '\'' +
                ", custIndustry='" + custIndustry + '\'' +
                ", custLevel='" + custLevel + '\'' +
                ", custAddress='" + custAddress + '\'' +
                ", custPhone='" + custPhone + '\'' +
                ", linkmans=" + linkmans +
                '}';
    }
}

}

由于联系人是多的一方,在实体类中要体现出,每个联系人只能对应一个客户,代码如下:

package com.yb.domain;

import javax.persistence.*;
import java.io.Serializable;

/**
 * @author [email protected]
 * @version 1.0
 * @date 2020/8/3
 */
@Entity
@Table(name = "cst_linkman")
public class LinkMan implements Serializable {
    
    
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="lkm_id")
    private Long lkmId;
    @Column(name="lkm_name")
    private String lkmName;
    @Column(name="lkm_gender")
    private String lkmGender;
    @Column(name="lkm_phone")
    private String lkmPhone;
    @Column(name="lkm_mobile")
    private String lkmMobile;
    @Column(name="lkm_email")
    private String lkmEmail;
    @Column(name="lkm_position")
    private String lkmPosition;
    @Column(name="lkm_memo")
    private String lkmMemo;
    //多对一关系映射:多个联系人对应客户
    @ManyToOne
    @JoinColumn(name="lkm_cust_id")
    private Customer customer;

    public Long getLkmId() {
    
    
        return lkmId;
    }

    public void setLkmId(Long lkmId) {
    
    
        this.lkmId = lkmId;
    }

    public String getLkmName() {
    
    
        return lkmName;
    }

    public void setLkmName(String lkmName) {
    
    
        this.lkmName = lkmName;
    }

    public String getLkmGender() {
    
    
        return lkmGender;
    }

    public void setLkmGender(String lkmGender) {
    
    
        this.lkmGender = lkmGender;
    }

    public String getLkmPhone() {
    
    
        return lkmPhone;
    }

    public void setLkmPhone(String lkmPhone) {
    
    
        this.lkmPhone = lkmPhone;
    }

    public String getLkmMobile() {
    
    
        return lkmMobile;
    }

    public void setLkmMobile(String lkmMobile) {
    
    
        this.lkmMobile = lkmMobile;
    }

    public String getLkmEmail() {
    
    
        return lkmEmail;
    }

    public void setLkmEmail(String lkmEmail) {
    
    
        this.lkmEmail = lkmEmail;
    }

    public String getLkmPosition() {
    
    
        return lkmPosition;
    }

    public void setLkmPosition(String lkmPosition) {
    
    
        this.lkmPosition = lkmPosition;
    }

    public String getLkmMemo() {
    
    
        return lkmMemo;
    }

    public void setLkmMemo(String lkmMemo) {
    
    
        this.lkmMemo = lkmMemo;
    }

    public Customer getCustomer() {
    
    
        return customer;
    }

    public void setCustomer(Customer customer) {
    
    
        this.customer = customer;
    }


    @Override
    public String toString() {
    
    
        return "LinkMan{" +
                "lkmId=" + lkmId +
                ", lkmName='" + lkmName + '\'' +
                ", lkmGender='" + lkmGender + '\'' +
                ", lkmPhone='" + lkmPhone + '\'' +
                ", lkmMobile='" + lkmMobile + '\'' +
                ", lkmEmail='" + lkmEmail + '\'' +
                ", lkmPosition='" + lkmPosition + '\'' +
                ", lkmMemo='" + lkmMemo + '\'' +
                '}';
    }
}

}

3.4 映射的注解说明

3.4.1 @OneToMany

作用:
   建立一对多的关系映射
属性:
   targetEntityClass:指定多的多方的类的字节码
   mappedBy:指定从表实体类中引用主表对象的名称。
   cascade:指定要使用的级联操作
   fetch:指定是否采用延迟加载
   orphanRemoval:是否使用孤儿删除

3.4.2 @ManyToOne

作用:
  建立多对一的关系
属性:
  targetEntityClass:指定一的一方实体类字节码
  cascade:指定要使用的级联操作
  fetch:指定是否采用延迟加载
  optional:关联是否可选。如果设置为false,则必须始终存在非空关系。

3.4.3 @JoinColumn

作用:
  用于定义主键字段和外键字段的对应关系。
属性:
  name:指定外键字段的名称
  referencedColumnName:指定引用主表的主键字段名称
  unique:是否唯一。默认值不唯一
  nullable:是否允许为空。默认值允许。
  insertable:是否允许插入。默认值允许。
  updatable:是否允许更新。默认值允许。
  columnDefinition:列的定义信息。

3.5 一对多的操作

3.5.1 添加-级联添加

/**
     * 添加+级联添加
     */
    @Test
    public void demo1(){
    
    
        //创建客户和联系人对象
        Customer c = new Customer();
        c.setCustName("TBD云集中心");
        c.setCustLevel("VIP客户");
        c.setCustSource("网络");
        c.setCustIndustry("商业办公");
        c.setCustAddress("昌平区北七家镇");
        c.setCustPhone("010-84389340");

        LinkMan l = new LinkMan();
        l.setLkmName("TBD联系人");
        l.setLkmGender("male");
        l.setLkmMobile("13811111111");
        l.setLkmPhone("010-34785348");
        l.setLkmEmail("[email protected]");
        l.setLkmPosition("老师");
        l.setLkmMemo("还行吧");

        l.setCustomer(c);
        c.getLinkmans().add(l);
        customerRepository.save(c);
    }

在这里插入图片描述

3.5.2 查询-级联查询

查询客户的同时把属于该客户的联系人查询出来
需要在实体类中配置
Customer

 	@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
    @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
    private Set<LinkMan> linkmans;
/**
     * 查询客户表(同时级联查询联系人)
     */
    @Test
    public void demo02(){
    
    
        //通过id查询
        Optional<Customer> byId = customerRepository.findById(5L);
        Customer customer = byId.get();
        System.out.println(customer);
    }
     /**
     * 查询联系人(同时级联查询该联系人的客户)
     */
    @Test
    public void demo03(){
    
    
        //通过id查询
        Optional<LinkMan> byId = linkManRespository.findById(5L);
        LinkMan linkMan = byId.get();
        System.out.println(linkMan);
        System.out.println("客户:"+linkMan.getCustomer().getCustName());
    }

在这里插入图片描述
在这里插入图片描述
注意:

  • 在测试级联查询时,进行展示的时候,两个实体类只能有一个使用lombok的注解,本人测试的时候,一直报错,就是因为在两个实体类中都是用了lombok的注解,例如:@Data,@ToString。
  • 在展示的时候实现toString方法最好不要实现级联的属性,例如:
    在这里插入图片描述
    如果非要实现,那么只能实现这两个实体类中的其中一个类,另一个类不能实现,否则打印数据的时候报错。

3.5.3 级联删除

/**
     *级联删除
     */
    @Test
    public void demo04(){
    
    
        customerRepository.deleteById(5L);
    }

猜你喜欢

转载自blog.csdn.net/Lv_vI/article/details/107756238