SpringBoot之SpringDataJpa-入门学习-2

1. JPA自定义

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

KeyWordSampleJPQL Snippet
AndfindByLastnameAndFirstnamewhere x.lastname = ?1 and x.firstname = ?2
OrfindByLastnameOrFirstnamewhere x.lastname = ?1 or x.firstname = ?2
Is,EqualsfindByFirstnameIs,findByFirstnameEqualswhere x.firstname = ?1
BetweenfindByStartDateBetweenwhere x.startDate between ?1 and ?2
LessThanfindByAgeLessThanwhere x.age < ?1
LessThanEqualfindByAgeLessThanEqualwhere x.age ⇐ ?1
GreaterThanfindByAgeGreaterThanwhere x.age > ?1
GreaterThanEqualfindByAgeGreaterThanEqualwhere x.age >= ?1
AfterfindByStartDateAfterwhere x.startDate > ?1
BeforefindByStartDateBeforewhere x.startDate < ?1
IsNullfindByAgeIsNullwhere x.age is null
IsNotNull,NotNullfindByAge(Is)NotNullwhere x.age not null
LikefindByFirstnameLikewhere x.firstname like ?1
NotLikefindByFirstnameNotLikewhere x.firstname not like ?1
StartingWithfindByFirstnameStartingWithwhere x.firstname like ?1 (parameter bound with appended %)
EndingWithfindByFirstnameEndingWithwhere x.firstname like ?1 (parameter bound with prepended %)
ContainingfindByFirstnameContainingwhere x.firstname like ?1 (parameter bound wrapped in %)
OrderByfindByAgeOrderByLastnameDescwhere x.age = ?1 order by x.lastname desc
NotfindByLastnameNotwhere x.lastname <> ?1
InfindByAgeIn(Collection ages)where x.age in ?1
NotInfindByAgeNotIn(Collection age)where x.age not in ?1
TRUEfindByActiveTrue()where x.active = true
FALSEfindByActiveFalse()where x.active = false
IgnoreCasefindByFirstnameIgnoreCasewhere 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 liangjie@itcast.cn
 * @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 liangjie@itcast.cn
 * @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 liangjie@itcast.cn
 * @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 liangjie@itcast.cn
 * @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("98354834@qq.com");
        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);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值