一、概念
JPA(Java Persistence API)是Sun官方提出的Java持久化规范。为Java开发人员提供了一种对象/关系映射工具来管理Java应用中的关系数据。
Spring Data JPA是spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,底层使用了Hibernate的JPA技术实现,可使开发者用极简的代码即可实现对数据的访问和操作。提供了包括增删改查等在内的常用功能,且易于扩展。
Spring Data JAP让我们解脱了DAO层的工作,基本上所有的CRUD都可以依赖于它来实现。
JAR包:spring-Boot-starter-data-jpa.jar
二、Repository
1.建立entity相关的注解:
@Entity:用于实体类上。
@Table:指定Entity所要映射的数据库表,如果缺省,系统默认采用类名作为映射表的表名。
@Id:声明此属性为主键。
@GeneratedValue(strategy=GenerationType, generator=""):
strategy表示主键生成策略,有AUTO, INDENTITY, SEQUENCE和TABLE四种,分别表示让ORM框架自动选择,根据数据库的Identity字段生成,根据数据库表的Sequence字段生成,根据一个额外的表生成主键,默认为AUTO。
generator表示主键生成器的名称,这个属性通常和ORM框架有关,例如,hibernate可以指定uuid等主键生成方式。
例:
1)SEQUENCE
@Id@GeneratedValue(strategy=GenarationType.SEQUENCE, genetator="aaa")@SequenceGenerator(name="aaa", sequenceName="seq_xxx")
2)IDENTIFY
@Id@GeneratedValue(strategy=GenarationType.IDENTIFY)
3)AUTO
@Id@GeneratedValue(strategy=GenarationType.AUTO)
@Column(nullable=false, unique=true……)
1.1JPA主键@Id、@IdClass、@Embeddable、@EmbeddedId
1)自动主键
@Id @GeneratedValue一起使用(以上介绍)。
2)应用设置主键
如果一个实体有一个没有@GeneratedValue标记的主键字段,则不会生成自动主键值,并且应用程序负责通过初始化主键字段来设置主键。这必须在持久化实体对象之前完成。
3)复合主键
由多个主键字段组成。当一个实体有多个主键字段时,JPA需要定义一个特殊的ID类,该类是使用@IdClass注释附加到实体类的。如果实体对象必须按照检索实体部分中所示的主键来检索实体对象,那么就需要ID类。
例:
实体类:
@Entity @IdClass(ProjectId.class)
public class Project{
@Id int departmentId;
@Id long projectId;}
ID类:
class ProjectId{
int departmentId;
long projectId;}
4)嵌入式主键
复合主键的另一种方式是使用嵌入式的类。主键字段是在可嵌入类中定义的。该实体包含一个单独的主键字段,该字段用@EmbeddedId注释,并包含一个可嵌入类的实例。当使用这个表单时,没有定义一个单独的ID类,因为可嵌入的类本身可以表示完整的主键值。
例:
实体类:
public class Project{
@EmbeddedId ProjectId id;
}
主键类:
@Embeddable
class ProjectId{
int departmentId;
long projectId;}
注意,复合主键类必须满足:实现Serializable接口;有默认的public无参数构造方法;重写equals和hashCode方法。
2.声明repository接口,可继承JpaRepository, PagingAndSortingRepository, CrudRepository
实际上,JpaRepository实现了PagingAndSortingRepository接口,PagingAndSortingRepository接口实现了CrudRepository接口,CrudRepository接口实现了Repository接口。
Repository接口是一个标识接口,里面是空的;
CrudRepository接口定义了增删改查方法;
PagingAndSortingRepository接口用于分页和排序;
由于JpaRepository接口继承了以上所有接口,所以拥有它们声明的所有方法。
3.直接声明接口不需要具体实现就能完成数据库的操作,其实现原理:
声明的repository接口被注入了一个动态代理,被代理的类是JpaRepository的一个实现SimpleJpaRepository。
Spring会在启动的时候扫描所有继承自Repository接口相关的DAO接口,然后为其实例化一个动态代理,同时根据它的方法名、参数等为其装配一系列DB操作组件,在需要注入的时候为对应的接口注入这个动态代理,在DAO方法被调用的时候会走这个动态代理,然后经过一系列的方法拦截路由到最终的DB操作执行器JpaQuertExecution,然后拼装SQL,执行相关操作,返回结果。
4.应用
1)基本查询
分为两种,一种是Spring Data默认已经实现(只继承JpaRepository),一种是根据查询的方法来自动解析成SQL。
2)复杂查询
在实际的开发中我们需要用到分页、删选、连表等查询的时候就需要特殊的方法或者自定义SQL。
分页查询:需要传入参数Pageable,返回Page对象。Pageable是Spring封装的分页实现类,使用的时候需要传入页数、每页条数和排序规则。
自定义SQL查询:在SQL的查询方法上面使用@Query注解,如果涉及到删除和修改,可以加上@Modifying。也可以根据需要添加@Transactional对事务的支持,查询超时的设置等。
多表查询:有两种实现方式,第一种是利用hibernate的级连查询来实现,第二种是创建一个结果集的接口来接收连表查询后的结果。
5. Spring Data JPA常用注解:
@Transient注解:
表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略此属性。
@NamedQuery注解:
用于实体类上,定义多个时使用@NamedQueries。如@NamedQuery(name="User.findByName", Query="select * from User where name=?1")。在自己实现的DAO的Repository 接口里面定义一个同名的方法,然后在使用中,Spring会找找是否有同名的NamedQuery,如果有,那么久不会按照接口定义的方法来解析。
@Query注解:
用于接口中自定义的查询方法上,可以使用JPQL,也可使用本地查询(nativeQuery=true)。所谓本地查询,就是使用原生SQL语句。
注意:方法的参数个数必须和@Query里需要的参数个数一致;
JPQL占位符"?"后的数字代表的是方法参数里的顺序;
如果是like查询,需要在参数的前面或后面加"%";
同样支持更新类的Query语句,添加@Modifying即可。在调用的地方必须加事务,没有事务不能正常执行???
使用@Param注解注入JPQL或者SQL的参数;可以利用SpEL表达式把实体类写成动态的#{#entityName}:是实体类的名称。实体类Book,使用@Entity注解后,Spring会将实体类Book纳入管理。默认#{#entityName}的值就是"Book"。但是如果使用了@Entity(name="book")来注解实体类Book,此时#{#entityName}的值就变成了"book"。因此,可以用@Entity注解实体类时指定name为比实体类对应的表名,在原生SQL语句中,就可以把#{#entityName}作为数据表名使用。
@Modifying注解:
1)可以通过自定义的JPQL完成UPDATE和DELETE操作。注意:JPQL不支持使用INSERT。
2)在@Query注解中编写JPQL语句,但必须使用@Modifying进行修饰,以通知Spring Data这是一个UPDATE或DELETE操作。
3)UPDATE或DELETE操作需要使用事务,此时需要定义Service层,在Service层的方法上添加事务操作。
4)默认情况下,Spring Data的每个方法上有事务,但都是一个只读事务,它们不能完成修改操作。
@EnableJpaRepositories注解:
6.查询策略
create-if-not-found(默认):如果通过@Query指定查询语句,则执行该语句,如果没有,则看看有没有@NamedQuery指定的查询语句,如果还没有,则通过解析方法名进行查询。
查询策略有三种,可更改。