Spring-Data-JPA 爬坑记

时间:2022-09-11 16:09:08

一: 实体类的常用注解

2.1 @Entity 标识这个 pojo 是一个 jpa 实体

2.2 @Table(name = "表名") 指定类对应的数据库表名

2.3 @Id 标记某一字段为表主键

2.4 @GeneratedValue 标记主键生成策略

2.5 @Column 标记为字段,有如下属性

2.5.1 name 对应数据库的字段名,默认为 属性名

2.5.2 unique 是否唯一,默认 false

2.5.3 nullable 是否可以为空 默认为 true

2.5.4 inserttable 是否可以插入,即提交 insert 语句时是否持久化的数据库

2.5.5 updateable 是否可以跟新,即提交 update 语句时是否能修改该字段

2.5.6 length 字段长度

2.5.7 precision 和 scale 多少位数字,保留几位小数

2.5.8 secondaryTable 从表名,该字段不在当前表,可以指定其它从表的名称

2.5.9 columnDefinition 定义建表时创建此列的DDL,使用时,其它属性将失效

2.5.9.1 columnDefinition="TEXT" 设置为文本类

2.6 @Temporal(TemporalType.DATE) 设置为时间类型

2.6.1 TemporalType.DATE yyyy-mm-dd

2.6.2 TemporalType.TIME hh:mm:ss

2.6.3 TemporalType.TIMESTAMP yyyy-mm-dd hh:mm:ss

2.7 @JoinColumn 外联表字段

2.8.1 name 外键的名称,类似 column 的 name

2.8 @ManyToOne 单向多对一,会在当前类的对应表中生成外键

2.7.1  fetch 抓取策略,( SpringBoot 中 FetchType.LAZY 报错, FetchType.EAGER 无效,还是两条 SQL)

2.7.1.1 FetchType.EAGER 查询时使用左外联,参考 left Join

2.7.1.2 FetchType.LAZY 查询时使用懒加载,即会发送两条 SQL

2.7.2 mapperBy 多方外键的数据库字段名称参照一方的哪一个字段

2.7.3 属性 cascade

2.7.3.1 CascadeType.PERSIST 支持级联保存

2.9 @OneToMany 单向一对多,会在关联类中的对用表生成外键 (少用,性能较低)

2.9.1 必须配置 JoinColumn(name='id'),否则会生成关联表

2.9.2 属性 cascade

2.9.2.1 CascadeType.PERSIST 支持级联保存

2.9.2.2 CascadeType.REMOVE 支持级联删除 即删除 一方同时删除关联的多方

2.10 @OrderBy(" id DESC ") 排序字段,只有在多对一且是,使用 list 时才能使用

二: 持久层 @Repository

1,Repository<T, T> 基础接口, 第一个 T 实体类 第二个T 主键类型 如 <User, Long>

1.1 提供了以方法名为查询条件方法

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 findByFirstname,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<Age> ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection<Age> 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)

 

简单的列子, 以上的 findBy 也可以改为 getBy 或者 readBy

package com.hwq.jpa.entity;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Entity
@Setter
@Getter
public class Status {

    @Id
    @GeneratedValue
    @JoinColumn
    Long id;

    Long pId;

    @Column(length = 20)
    String name;

}
package com.hwq.jpa.dao;

import com.hwq.jpa.entity.Status;
import org.springframework.data.repository.Repository;

@org.springframework.stereotype.Repository  // 注意两个 Repository 不要冲突
public interface StatusDao extends Repository<Status, Long> {

    Status getById(Long id); // 根据 id Status 类

}

2, CrudRepository<T, T> 提供了简单的增删改查的方法, 继承自 Repository<T, T>

3, PagingAndSortingRepository<T, T> 提供了分页和排序的方法, 继承自 CrudRepository<T, T>

4,JpaRepository<T, T> 继承自  PagingAndSortingRepository<T, T> 

5,JpaSpecificationExecutor<T> 条件查询

简单的例子,依然使用上面的实体类

package com.hwq.jpa.dao;

import com.hwq.jpa.entity.Status;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StatusDao extends JpaRepository<Status, Long>, JpaSpecificationExecutor<Status> {

}
package com.hwq.jpa.controller;

import com.hwq.jpa.dao.StatusDao;
import com.hwq.jpa.entity.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.*;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.persistence.criteria.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/status")
public class StatusController {

    @Autowired
    StatusDao statusDao;

    @RequestMapping("/insert")
    public Status insert(Status status) {
        // 保存或者修改的方法
        // 当存在主键时为修改,不存在为保存
        return statusDao.save(status);
    }

    @RequestMapping("select")
    public Map<String, Object> select(Integer page, Integer size) {
        // 实例化 排序类, Sort 可以传入多个 Order
        // Direction.DESC 降序
        // Direction.ASC  升序
        // "id"           排序字段
        Order order1 = new Order(Direction.DESC, "id");
        Sort sort = new Sort(order1);

        // 实例化分页类
        // page 查询的当前页数
        // size 查询的每一页的最大记录数
        PageRequest pageAble = new PageRequest(page, size, sort);

        // 调用 findAll 方法
        Page<Status> result = statusDao.findAll(pageAble);

        // 获取需要的数据
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("rows", result.getTotalElements());    // 获取总记录数
        map.put("page", result.getNumber());           // 当前是第几页
        map.put("pages", result.getTotalPages());      // 总共多少页
        map.put("data", result.getContent());          // 当前页的数据
        map.put("size", result.getNumberOfElements()); // 当前页的记录数

        return map;
    }

    @RequestMapping("/select-if")
    public Map<String, Object> selectIf(Integer page, Integer size) {
        Order order1 = new Order(Direction.DESC, "id");
        Sort sort = new Sort(order1);
        PageRequest pageAble = new PageRequest(page, size, sort);
        Specification<Status> statusSpecification = new Specification<Status>() {
            @Override
            public Predicate toPredicate(Root<Status> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Predicate predicate = null;  // 最终返回的对象, 由 cb 创建
                Path path = root.get("id");  // 定位到实体类的 id
                predicate = cb.gt(path, 1);  // WHERE id > 1
                return predicate;
            }
        };

        // 调用 findAll 方法
        Page<Status> result = statusDao.findAll(statusSpecification, pageAble);

        // 获取需要的数据
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("rows", result.getTotalElements());    // 获取总记录数
        map.put("page", result.getNumber());           // 当前是第几页
        map.put("pages", result.getTotalPages());      // 总共多少页
        map.put("data", result.getContent());          // 当前页的数据
        map.put("size", result.getNumberOfElements()); // 当前页的记录数

        return map;
    }

}

6,自定义的 Repository 方法《 注意以下三点 》《 无特殊情况,少用 》

6.1 必须创建一个任意的 接口  XxxDao

6.2 定义的  XxxRepository 继承 XxxDao

6.3 实现 XxxDao 的类名称必须为 XxxRepositoryImpl

6.3.1 实现类中可以使用 EntityManager 完美接入 JPA 的内容 

简单的例子, 依然使用最开始的实体类为

package com.hwq.jpa.repository.dao;

import com.hwq.jpa.entity.Status;

public interface StatusDao {
    Status selectStatusById(Long id);
}
package com.hwq.jpa.repository;

import com.hwq.jpa.entity.Status;
import com.hwq.jpa.repository.dao.StatusDao;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

@Repository
public interface StatusRepository extends
        JpaRepository<Status, Long>, JpaSpecificationExecutor<Status>, StatusDao
{

}
package com.hwq.jpa.repository;

import com.hwq.jpa.entity.Status;
import com.hwq.jpa.repository.dao.StatusDao;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

public class StatusRepositoryImpl implements StatusDao {

    @PersistenceContext
    EntityManager entityManager;

    @Override
    public Status selectStatusById(Long id) {
        Status status = entityManager.find(Status.class, id);
        return status;
    }
}