Spring Data套装基础之JPA

1. 简介

Spring Data套装基础之Repositories中我们介绍了Spring Data最核心和公共的部分,这里我们简单介绍一下Spring Data JPA。

Spring Data JPA主要是针对关系数据库的,Spring Data Repository的东西都能使用,在查询方面肯定更加强大。

2. JPA 与 Spring Data JPA

JPA(Java Persistence API)Java持久化API,JDK1.5开始支持Java持久化规范(JSR 338),即javax.persistence包相关接口。

JPA主要是为了规范ORM(对象关系映射技术,如Hibernate、TopLink、JDO等),统一抽象层,面向接口编程,这样更容易切换、维护、重构。

JPA主要包括:

  1. ORM(Object Relational Metadata)元数据的映射
  2. 实体对象来执行CRUD操作相关API
  3. JPQL(Java Persistence Query Language),面向对象查询语言,避免程序的SQL语句紧密耦合

JPA是ORM规范,Hibernate实现了JPA,Spring Data JPA在JPA之上添加另一层抽象,通过Repository简化了数据持久化的工作量。

3. Spring Data JPA 与 MyBatis

如果业务逻辑比较简单Spring Data JPA可能要好一些,因为很多时候不用自己写SQL。

如果业务逻辑比较复杂,MyBatis可能要更好一些,可以专注于SQL,方便统一管理,有问题好定位。

4. 接口与实现类

Repository接口:

  1. Repository:只要名字按规范,接口就可以直接使用,不需要写SQL
  2. CrudRepository:继承了Repository,添加了增删改查相关功能
  3. PagingAndSortingRepository:继承了CrudRepository,添加了了排序分页相关
  4. JpaRepository:继承了PagingAndSortingRepository,添加了更多增删改查的功能
  5. QueryByExampleExecutor:更底层的接口,JpaRepository继承了QueryByExampleExecutor,一些方法接受Example参数
  6. Specification:JPA中Root、CriteriaQuery、CriteriaBuilder的封装,用于构建过滤条件
  7. JpaSpecificationExecutor:更加灵活的查询方式,主要是为了更好的实现一些自定义的查询
  8. QueryDslPredicateExecutor:基于ORM、SQL等构建的通用查询API

实现类:

  1. SimpleJpaRepository:Spring Data JPA在处理Repository、UserCrudRepository、PagingAndSortingRepository、JpaRepository等接口使用的就是这个类
  2. QueryDslJpaRepository(弃用了,使用QuerydslJpaPredicateExecutor)
  3. QuerydslJpaPredicateExecutor:QueryDslPredicateExecutor实现类

5. 主键

@GenericGenerator:Hibernate主键生成策略注解
@GeneratedValue:Java注解,可以通过generator属性引用其他生成策略

也可以通过GenerationType指定,如@GeneratedValue(strategy = GenerationType.IDENTITY)

  1. AUTO:主键由程序控制,也是GenerationType的默认值
  2. TABLE:使用一个特定的数据库表格来保存主键
  3. SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列
  4. IDENTITY:主键由数据库自动生成

6. @Query

@Query可以直接指定SQL语句,只需要将nativeQuery设置为true

@Query(value = "select * from user where username like ?1",nativeQuery = true)
List<User> queryByUsername(String username);

在SQL语句中使用?1,?2…?n这样的占位符和方法参数对应。

默认,@Query是JPQL:

@Query("select u from User u where u.username like %?1%")
List<User> getByUsernameLike(String username);

注意:在JPQL查询使用的是类名User而不是表名user

当然,还可以使用@param方式的参数

@Query("select u from User u where u.id = :id")
User getById(@Param("id") String userId);

还可以使用@Modifying执行更新操作:

@Transactional()
@Modifying
@Query("update User set username = ?1 where id = ?2")
int updateUsernameById(Integer id, String username);

排序与分页:

@Query("select u from User u where u.username like %?1%")
Page<User> findByUsernameLikePageable(String username, Pageable pageable);

@Query("select u from User where username like %?1%")
List<User> findByUsernameAndSort(String username, Sort sort);

7. JpaSpecificationExecutor

Spring data JPA不会扫描JpaSpecificationExecutor,想要直接使用,需要继承一个Repository接口。

JpaSpecificationExecutor接受Specification做为参数,方面我们自定义查询。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.junit4.SpringRunner;
import vip.mycollege.mysql.jpa.entity.User;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserJpaSpecificationExecutorTest {

    @Resource
    private UserJpaSpecificationExecutor userJpaSpecificationExecutor;

    @Test
    public void testJpaSpecificationExecutor1() {
        Specification<User> specification = new Specification<User>() {
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Predicate predicate = criteriaBuilder.equal(root.get("username"), "tim");
                return predicate;
            }
        };
        List<User> list = userJpaSpecificationExecutor.findAll(specification);
        for (User users : list) {
            System.out.println(users);
        }
    }

    @Test
    public void combin() {
        Specification<User> specification = new Specification<User>() {
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> list = new ArrayList<>();
                list.add(criteriaBuilder.equal(root.get("username"), "tim"));
                list.add(criteriaBuilder.equal(root.get("age"), 20));
                Predicate[] arr = new Predicate[list.size()];
                return criteriaBuilder.and(list.toArray(arr));
            }
        };
        List<User> list = userJpaSpecificationExecutor.findAll(specification);
        for (User users : list) {
            System.out.println(users);
        }
    }

    @Test
    public void orAnd() {
        Specification<User> specification = new Specification<User>() {
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Predicate namePredicate = criteriaBuilder.equal(root.get("username"), "tim");
                Predicate agePredicate = criteriaBuilder.equal(root.get("age"), 20);
                Predicate nameAndAgePredicate = criteriaBuilder.and(namePredicate, agePredicate);
                Predicate idPredicate = criteriaBuilder.equal(root.get("id"), 1);
                return criteriaBuilder.or(nameAndAgePredicate, idPredicate);
            }
        };
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        List<User> list = userJpaSpecificationExecutor.findAll(specification, sort);
        for (User users : list) {
            System.out.println(users);
        }
    }

    @Test
    public void gt() {
        Specification<User> specification = new Specification<User>() {
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.gt(root.get("id").as(Integer.class), 2);
            }
        };
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        List<User> list = userJpaSpecificationExecutor.findAll(specification, sort);
        for (User users : list) {
            System.out.println(users);
        }
    }

    @Test
    public void lt() {
        Specification<User> specification = new Specification<User>() {
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.lt(root.get("id").as(Integer.class), 2);
            }
        };
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        List<User> list = userJpaSpecificationExecutor.findAll(specification, sort);
        for (User users : list) {
            System.out.println(users);
        }
    }

    @Test
    public void between() {
        Specification<User> specification = new Specification<User>() {
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.between(root.get("id").as(Integer.class), 2,10);
            }
        };
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        List<User> list = userJpaSpecificationExecutor.findAll(specification, sort);
        for (User users : list) {
            System.out.println(users);
        }
    }
}

8. 映射关系

8.1 @OneToMany

一对多的关系,例如,一个专业Speciality有多个User。

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.Set;

@Entity
@Table(name = "speciality")
public class Speciality {

@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;

@OneToMany(targetEntity = User.class, cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
@JoinColumn(name = "speciality_id")
private Set<User> Users;

@Column(name = "name",length = 20)
private String name;

public Integer getId() {
return id;
}

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

public Set<User> getUsers() {
return Users;
}

public void setUsers(Set<User> users) {
Users = users;
}

public String getName() {
return name;
}

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

@Override
public String toString() {
return "Speciality{" +
"id=" + id +
", Users=" + Users +
", name='" + name + '\'' +
'}';
}
}

然后,我们查询Speciality的时候就可以把关联的User都查出来。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import vip.mycollege.mysql.jpa.entity.Speciality;

import javax.annotation.Resource;
import java.util.Iterator;


@RunWith(SpringRunner.class)
@SpringBootTest
public class SpecialityCrudRepositoryTest {

@Resource
private SpecialityCrudRepository specialityCrudRepository;

@Test
public void findAll(){
Iterator<Speciality> iterator = specialityCrudRepository.findAll().iterator();
while (iterator.hasNext()){
Speciality speciality = iterator.next();
System.out.println(speciality);
}
}
}

8.2 其他

除了最常用的@OneToMany,还有@ManyToMany、@ManyToOne、@OneToOne

OneToOne、OneToMany和ManyToMany可以设置mappedBy

在属于拥有关系中,没有mappedBy属性的实体是拥有者

两个实体中都又@ManyToMany,如果都没有设置mappedBy,则默认将生成两个实体表和两个连接表

8.3 CascadeType

选项说明
CascadeType.ALL包含下面四种
CascadeType.MERGE级联合并,若A属性修改了,那么关联对象B保存时同时修改A对象
CascadeType.REMOVE级联删除,A对象删除,那么关联对象B中的对象也会删除
CascadeType.PERSIST级联新增,A对象保存时,关联对象B中的对象也会保存
CascadeType.REFRESH级联刷新,获取A对象里也同时,也重新获取最新的关联对象B

9. hibernate表处理策略

spring.jpa.hibernate.ddl-auto=update

参数说明
none禁用DDL处理,默认值
create每次运行程序时,都会重新创建表,故而数据会丢失
upadte每次运行程序,没有表时会创建表,如果对象发生改变会更新表结构,原有数据不会清空,只会更新
validate运行程序会校验数据与数据库的字段类型是否相同,字段不同会报错
create-drop每次运行程序时会先创建表结构,然后待程序结束时清空表

10. 注解

注解说明
@Id指定表主键
@Lob将属性映射成数据库支持的大对象类型
@Table声明表名
@Basic指定非约束明确的各个字段
@Entity声明类为实体
@Column持久字段属性
@IdClass联合主键
@OrderBy排序
@Embedded指定类或它的值是一个可嵌入的类的实例的实体的属性
@OneToOne定义了连接表之间有一个一对一的关系
@Transient表示该属性并非一个到数据库表的字段的映射,JPA将忽略该属性
@EmbeddedId可嵌入式联合主键
@Enumerated映射enum枚举类型的字段
@AccessTypeFIELD直接访问字段,PROPERTY通过getter、setter方法字段
@JoinColumn连接字段
@ManyToOne定义连接表之间多对一关系
@OneToMany定义连接表之间一对多关系
@ManyToMany定义连接表之间多对多关系
@NamedQuery在实体类上,声明JPQL配置方法
@JoinColumns里面有多个@JoinColumn
@EntityGraph为了提高查询效率。解决N+1条SQL的问题
@ColumnResult字段返回值
@EntityResult实体返回值
@NamedQueries在实体类上,声明式JPQL配置方法
@GeneratedValue指定主键生成方式
@TableGenerator指定主键生成器
@NamedNativeQuery在实体类上,声明原始SQL配置
@NamedEntityGraph为了提高查询效率,解决N+1条SQL的问题
@UniqueConstraint指定的字段和用于主要或辅助表的唯一约束
@TemporalType.DATE映射只有日期的date
@TemporalType.TIME映射只有时间的time
@SequenceGenerator指定在@GeneratedValue注解中指定的属性的值。它创建了- -个序列
@SqlResultSetMapping字段属性映射
@SqlResultSetMappings多个@SqlResultSetMapping
@TemporalType.TIMESTAMP映射为日期datetime

11. 文档资料

Spring Data JPA文档
Spring Data JPA
querydsl

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值