Spring系列之Spring Data JPA介绍

本文详细介绍了Java持久化API (JPA) 的基本概念、状态管理和策略选择,并对比了MyBatis与JPA的特点。同时深入探讨了Hibernate作为JPA的主要实现及其内部工作原理,最后讲解了Spring Data JPA的使用方法和自定义查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在讲述Spring Data JPA之前,又必要梳理一下,JSR-220、JPA、Hibernate、MyBatis等关系。

JPA

JSR-220引入,Java Persistence API,通过JDK 5.0注解或XML描述对象和关系表的映射,并将运行期的实体对象持久化到数据库中。JPA的宗旨是为POJO提供持久化标准规范。

总的来说,JPA包括以下3方面的技术:

  • ORM映射元数据:JPA支持XML和JDK5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;
  • API:用来操作实体对象,执行CRUD操作,框架在后台完成所有事情,开发者从繁琐的JDBC和SQL代码中解脱出来;
  • 查询语言:通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。

实体Entity,具有ORM元数据的领域对象,POJO是实体的条件:

  1. 必须使用javax.persistence.Entity注解或XML映射文件中有对应的<entity>元素;
  2. 必须具有一个不带参数的构造函数,类不能声明为final,方法和需要持久化的属性也不能声明为final;
  3. 如果游离态的实体对象需要以值的方式进行传递(如通过Session bean的远程业务接口传递),则必须实现Serializable接口;
  4. 需要持久化的属性,起访问修饰符不能是public,它必须通过实体类方法进行访问。

实体共有4种状态:

  1. 新建态:新创建的实体对象,尚未拥有持久化主键,没有和一个持久化上下文关联起来
  2. 受控态:已经拥有持久化主键和持久化上下文建立联系
  3. 游离态:拥有持久化主键,但尚未和持久化上下文建立联系
  4. 删除态:拥有持久化主键,已经和持久化上下文建立联系,但已经被安排从数据库中删除

注解

@Entity:用于实体类,指明该Java类为实体类,将映射到指定的数据库表;

@Table:当实体类与其映射的数据库表名不同名时,可使用@Table注解加以说明,与@Entity注解并列使用。常用选项name,用于指明数据库的表名;catalog和schema用于设置表所属的数据库目录或模式,通常为数据库名。

@GeneratedValue:用于注解主键的生成策略,通过strategy属性指定。默认情况下,JPA自动选择一个最适合底层数据库的主键生成策略:Sql Server对应Identity,MySQL对应Auto Increment。

@Basic:表示一个简单的属性到数据库表字段的映射,对于没有任何注解的getter方法,默认即为@Basic。两个属性:

  • fetch:表示该属性的读取策略,有EAGER(默认)和LAZY两种,分别表示主动抓取和延迟加载;
  • optional:表示该属性是否允许为null,默认为true。

@Column:用于当实体的属性与其映射的数据库表的列不同名时。用于属性字段,也可置于属性的getter方法上,可与@Id注解一起使用。常用属性:

  • name:用于设置映射数据库表的列名
  • unique:字段是否唯一的标识,默认为false
  • nullable:字段是否允许为null,默认为true
  • length:字段大小,仅对String类型有效
  • insertable:表示ORM框架在执行插入操作时,该字段是否应出现INSETRT语句中,默认为true
  • updateable:表示ORM框架在执行更新操作时,该字段是否应该出现在UPDATE语句中,默认为true。对于一经创建就不可更改的字段非常有用,如birthday字段
  • columnDefinition:表示该字段在数据库中的实际类型。通常ORM框架可以根据属性类型自动判断数据库中字段的类型,但是对于Date类型仍无法确定数据库中字段类型究竟是DATE、TIME还是TIMESTAMP。String的默认映射类型为VARCHAR,如果要将String类型映射到特定数据库的BLOB或TEXT字段类型。

@OneToOne:描述一对一关联,属性cascade:表示级联操作策略

@OneToMany:描述一个一对多的关联

@ManyToMany:描述一个多对多关联,属性:

  • targetEntity:表示多对多关联的另一个实体类的全名
  • mappedBy:表示多对多关联的另一个实体类的对应集合属性名称

以用户-角色为例,利用ORM工具自动生成的表除user和role表外,还自动生成user_role表,用于实现多对多关联。

@Transient:表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性。如果属性没有标记该注解,则ORM框架默认其注解为@Basic。

@Temporal:在核心的Java API中并没有定义Date类型的精度(Temporal Precision)。而在数据库中表示Date类型的数据有DATE、TIME和TIMESTAMP三种精度(即单纯的日期、时间、或两者兼备)。在进行属性映射时可使用@Temporal注解来调整精度。

GenerationType中定义几种可以供选择的策略:

  1. IDENTITY:主键自增,依赖于具体的数据库,Oracle不支持这种方式;
  2. AUTO:JPA自动选择合适的策略,默认选项;
  3. SEQUENCE:通过序列产生主键,通过@SequenceGenerator注解指定序列名。依赖于数据库是否有SEQUENCE,如果没有就不能用。MySQL不支持这种方式;
  4. TABLE:通过表产生主键,框架借由表模拟序列产生主键,不依赖于具体的数据库,使用该策略可使应用更易于数据库的切换。

TemporalType枚举中定义三种时间类型:

  • Date:即java.sql.Date
  • Time:即java.sql.Time;
  • TimeStamp:即java.sql.Timestamp

InheritanceType定义3种映射策略:

  • SINGLE_TABLE:父子类都保存在同一个表中,通过字段值进行区分;
  • JOINED:父子类相同的部分保存在同一个表中,不同的部门分开存放,通过连接不同的表获取完整数据;
  • TABLE_PER_CLASS:每一个类对应自己的表,一般不推荐采用这种方式。

Hibernate

JPA是Hibernate功能的一个子集,Hibernate是JPA的一个参考实现。

Hibernate主要有三个组件:

  • hibernate-core:核心实现,提供核心功能;
  • hibernate-annotation:Hibernate支持annotation方式配置的基础,它包括标准的JPA annotation以及Hibernate自身特殊功能的annotation
  • hibernate- entitymanager:实现标准的JPA,可以把它看成hibernate-core和JPA之间的适配器,它并不直接提供ORM的功能,而是对hibernate-core进行封装,使得Hibernate符合JPA的规范。

HibernatePersistence实现JPA的PersistenceProvider接口,提供createEntityManagerFactory和createContainerEntityManagerFactory两个方法来创建EntityManagerFactory对象,这两个方法底层都是调用的EJB3Configuration对象的buildEntityManagerFactory方法,来解析JPA配置文件persistence.xml,并创建EntityManagerFactory对象。

EntityManagerFactory对象的实现是EntityManagerFactoryImpl类,这个类有一个最重要的属性就是Hibernate的核心对象之一SessionFactory。这个类最重要的方法是createEntityManager,来返回EntityMnagaer对象,而sessionFactory属性也传入该方法。

EntityManager对象的实现是EntityManagerImpl类,这个类继承自AbstractEntityManagerImpl类,在AbstractEntityManager类中有一个抽象方法getSession来获得Hibernate的Session对象,正是在这个Session对象的实际支持下,EntityManagerImpl类实现JPA的EntityManager接口的所有方法,并完成实际的ORM操作。

此外,hibernate-entitymanager包中还有QueryImpl类利用EntityManagerImpl的支持实现JPA的Query接口;TransactionImpl利用EntityManagerImpl的支持实现JPA的EntityTransaction接口。

JPA vs MyBatis

JPA是J2EE规范,实现有Hibernate和Spring Data JPA。

  1. JPA是对象与对象之间的映射,而MyBatis是对象和结果集的映射。
  2. JPA移植性比较好,不用关心用什么数据库,因为MyBatis自由写SQL语句,所以当项目移植的时候还需要改sql。(及时判断数据库类型,不嫌累么)。
  3. 当需要修改字段的时候MyBatis改起来特别费事,而JPA就相对简单。
  4. hibernate学习曲线陡峭,Spring Data JPA改善这一状况;MyBatis比较简单。

Spring Data JPA

Spring Data作为SpringSource的一个子项目,旨在统一和简化对各类型持久化存储和访问,而不拘泥于是关系型数据库还是NoSQL数据存储,使得对数据库的访问变得方便快捷,并支持MapReduce框架及云计算服务;对于拥有海量数据的项目,可以用Spring Data来简化项目的开发,就如Spring Framework对JDBC、ORM的支持一样,Spring Data会让数据的访问变得更加方便,极大提高开发效率。

Spring Data包括很多不同数据库的工程,如Redis,neo4j。

使用

示例:

public interface UserRepository extends JpaRepository<User, Long> {
	User findByName(String name);
	@Query("from User u where u.name=:name")
	User findUser(@Param("name") String name);
}

只需要通过编写一个继承自JpaRepository的接口就能完成数据访问。
特性:通过解析方法名创建查询。另外,提供通过使用@Query来创建查询,需要编写JPQL语句,并通过类似“:name”来映射@Param指定的参数。@Query默认不支持原生态sql,需要用nativeQuery=true开启。
还有诸如@Modifying操作、分页排序、原生SQL支持以及与Spring MVC的结合使用。

Repository子接口

接口用途
CrudRepository extends Repository实现CRUD相关方法
PagingAndSortingRepository extends CrudRepository实现分页排序相关方法
JpaRepository extends PagingAndSortingRepository实现JPA规范相关方法

其他:

  1. Repository是一个空接口,即标记接口,没有包含方法的接口
  2. 如果定义的(dao层)接口没有继承Repository运行时会报错
  3. 也可以通过注解方式定义dao层接口
    @RepositoryDefinition(domainClass=实体类.class,idClass=主键类型.class)

常用关键字

  • And:等价于SQL中的and关键字,如findByUsernameAndPassword(String user, Striang pwd)
  • Or:等价于SQL中的or关键字,如findByUsernameOrAddress(String user, String addr)
  • Between:等价于SQL中的between关键字,如findBySalaryBetween(int max, int min)
  • LessThan:等价于SQL中的<,如findBySalaryLessThan(int max)
  • GreaterThan:等价于SQL中的>,如findBySalaryGreaterThan(int min)
  • After:等价于where startDate>?,如findByStartDateAfter
  • Before:等价于where startDate<?,如findByStartDateBefore
  • IsNull:等价于SQL中的is null,如findByUsernameIsNull()
  • IsNotNull:等价于SQL中的is not null,如findByUsernameIsNotNull()
  • NotNull:等价于IsNotNull
  • Like:等价于SQL中的like,如findByUsernameLike(String user)
  • NotLike:等价于SQL中的not like,如findByUsernameNotLike(String user)
  • OrderBy:等价于SQL中的order by,如findByUsernameOrderBySalaryAsc(String user)
  • Not:等价于SQL中的!=,如findByUsernameNot(String user)
  • In:等价于SQL中的in,如findByUsernameIn(Collection<String> userList),方法参数可以是Collection类型,数组或不定长参数;
  • NotIn:等价于SQL中的not in,如findByUsernameNotIn(Collection<String> userList),方法参数可以是Collection类型,数组或不定长参数;
  • StartingWith:findByFirstnameStartingWith等价于where firstname like (parameter bound with appended %)
  • EndingWith:findByFirstnameEndingWith等价于where firstname like (parameter bound with prepended %)
  • Containing:findByFirstnameContaining等价于where firstname like (parameter bound wrapped in %)
  • TRUE:findByActiveTrue等价于where active = true
  • FALSE:findByActiveFalse等价于where active = false

原理

Spring Data JPA依赖于Hibernate:spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop
其主要作用是:自动创建、更新、验证数据库表结构。该参数的几种配置如下:

  • create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
  • create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭表就自动删除。
  • update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
  • validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

自定义接口

Spring Data JPA提供方便快捷的查询数据库方式,只要按照它的约定,编写接口和函数定义,即可很方便的从数据库中查询到想用的数据。但是每个应用业务逻辑的复杂度不同,有时必须要自定义JPQL甚至Native SQL来做自己的查询。

  1. 自定义查询接口。
public interface CustomizedLogRepository {
	List<LogDto> searchLogs(String appId, String keyword);
	long searchLogCount(String appId, String keyword);
}
  1. 创建一个接口继承JpaRepository或CurdRepository,以及自定义接口CustomizedLogRepository:
public interface LogRepository extends CrudRepository<LogDto, Integer>, CustomizedLogRepository {
}
  1. 实现LogRepository
public class LogRepositoryImpl implements CustomizedLogRepository {
	@Autowired
	@PersistenceContext
	private EntityManager entityManager;
	
	@Override
	public List<Ankonlog> searchLogs(String appId, String keyword) {
	}
	@Override
	public long searchLogCount(String appId, String keyword) {
	}
}

至此,自定义JPQL查询完成。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

johnny233

晚饭能不能加鸡腿就靠你了

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值