Spring Data JPA

Spring Data JPA是基于HibernateJPA规范的一套ORM框架。目前主流的ORM框架有:

  • MyBatis
  • Hibernate
  • Spring Data JPA
  • Spring JDBC Template

1. Spring Data

Spring Data旨在简化对数据库或者数据存储(比如缓存)的持久化操作,且不拘泥于关系型SQL,同时支持NoSQL数据库,比如文档数据库MongoDB、图数据库Neo4j、键/值存储Redis等。

上图是Spring Data的项目架构,包含Spring Data JPASpring Data MongoDBSpring Data Neo4j等子项目,以分别匹配不同类型的数据库。

Repository接口是Spring Data的核心接口,不提供任何方法,是一个空接口,也就是一个标记接口。当我们声明的接口继承该接口时,会自动将我们的接口归入Spring容器管理。

  • Repository<T, ID>
@Indexed
public interface Repository<T, ID> {
}
  • CrudRepository<T, ID> 继承自Repository<T, ID>,封装了简单的增删改查操作
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S var1);

<S extends T> Iterable<S> saveAll(Iterable<S> var1);

Optional<T> findById(ID var1);

boolean existsById(ID var1);

Iterable<T> findAll();

Iterable<T> findAllById(Iterable<ID> var1);

long count();

void deleteById(ID var1);

void delete(T var1);

void deleteAll(Iterable<? extends T> var1);

void deleteAll();
}
  • PagingAndSortingRepository<T, ID> 继承自CrudRepository<T, ID>,实现了分页与排序功能
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort var1);

Page<T> findAll(Pageable var1);
}

2.JPA(Java Persistence API,Java持久化API)

JPA是一个Java持久化接口规范,为Java开发人员提供了一种对象/关系映射(ORM)工具来管理Java应用中的关系数据。但是JPA只是一种接口规范,并不是一套直接可用的产品(比如Hibernate)。

3.Spring Data JPA

Spring Data JPA是Spring Data的一个子项目,是基于Hibernate、JPA规范的基础上封装的一套ORM框架,可以说是JPA规范的一个实践落地的产品。内置实现了包括增删改查、分页、自定义SQL的常用功能,以简洁的代码快速实现关系数据库访问。

JPARepository<T, ID>是Spring Data JPA的核心接口,它继承自PagingAndSortingRepository<T, ID>接口,同时实现了JPA的相关规范:

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();

List<T> findAll(Sort var1);

List<T> findAllById(Iterable<ID> var1);

<S extends T> List<S> saveAll(Iterable<S> var1);

//JPA或Hibernate中都对应有会话中的状态之类的标记,这里可刷新
void flush();

<S extends T> S saveAndFlush(S var1);

void deleteInBatch(Iterable<T> var1);

void deleteAllInBatch();

T getOne(ID var1);

<S extends T> List<S> findAll(Example<S> var1);

<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

在使用Spring Data JPA时,只需要继承JPARepository接口,在声明方法时按照特定规则进行声明,Spring框架底层会自动帮我们实现这些方法,从而实现快速的增删改查、分页排序功能。

但是对于一些复杂查询(比如对两张数据表的级联查询),很难通过方法名来构造,这时需要@Query注解,通过自定义SQL来实现复杂查询:

@Query("select user from User u where u.userName=?1 and u.age=?2 ")
User getUserByUserNameAndAge2(String userName,Integer age);

当需要更新和删除操作,加一个@Modifying注解:

@Modifying
@Query("UPDATE User u set u.userName =: userName where u.age =:age")
void update(@Param(value = "userName") String userName, @Param(value = "age")Integer age);

4.Spring Data MongoDB

Spring Data MongoDBSpring Data JPA同级,都是Spring Data的子项目,其核心是MongoRepository+MongoTemplate。它的核心接口MongoRepository<T, ID>也是继承自PagingAndSortingRepository<T, ID>接口:

@NoRepositoryBean
public interface MongoRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
<S extends T> List<S> saveAll(Iterable<S> var1);

List<T> findAll();

List<T> findAll(Sort var1);

<S extends T> S insert(S var1);

<S extends T> List<S> insert(Iterable<S> var1);

<S extends T> List<S> findAll(Example<S> var1);

<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

与JPARepository同理,通过继承MongoRepository,并按规则声明方法,Spring底层会自动实现方法,从而快速地对MongoDB进行增删改查、分页排序等操作:

@Repository
public interface MetadataRepository extends MongoRepository<Metadata, String>, CustomMetadataRepository {
Boolean existsByIdAndOwnerId(String id, String ownerId);

List<Metadata> findAllByImageId(String imageId);
}

遇到复杂查询问题,与Spring Data JPA不同,Spring Data MongoDB提供了MongoTemplate,支持自定义的复杂查询操作,核心类是Query与Criteria:

@Repository
public class InstanceRepositoryImpl implements CustomInstanceRepository {
@Autowired
MongoTemplate mongoTemplate;

@Override
public Instance queryByIp(String ip) {
Query query = new Query();
query.addCriteria(Criteria.where("ip").is(ip));
Instance instance = mongoTemplate.findOne(query, Instance.class);
if (instance == null) {
throw new ObjectNotFoundException("服务实例");
}
return instance;
}

@Override
public List<Instance> findAllByMetadataId(String metadataId) {
Query query = new Query();
query.addCriteria(Criteria.where("metadataId").is(metadataId));
return mongoTemplate.find(query, Instance.class);
}

@Override
public List<Instance> findAllByImageId(String imageId) {
Query query = new Query();
query.addCriteria(Criteria.where("imageId").is(imageId));
return mongoTemplate.find(query, Instance.class);
}

}

5.MyBatis

Mybatis是一款优秀的持久层框架,他支持自定义SQL、存储过程以及高级映射。Mybatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。Mybatis可以通过简单的XML或注解来配置和映射原始类型、接口和Java POJO(Plain Old Java Objects,普通老式Java对象)为数据库中的记录。

6.Spring Data JPA与MyBatis对比

JPA默认使用Hibernate作为ORM实现,Hibernate是面向对象的,而MyBatis是面向关系的

  • 面向对象考虑的是对象的整个生命周期,包括在对象的创建、持久化、状态的改变和行为等,对象的持久化只是对象的一种状态,而面向关系型数据库的概念则更关注数据的高效存储和读取
  • 面向对象更强调对象状态的封装性,对象封装自己的状态(或数据)不允许外部对象随意修改,只暴露一些合法的行为方法供外部对象调用;而关系型数据库则是开放的,可以供用户随意读取和修改关系,并可以和其他表任意的关联(只要sql正确允许的情况下)。
  • 面向对象试图为动态的世界建模,他要描述的是世界的过程和规律,进而适应发展和变化,面向对象总是在变化中处理各种各样的变化。而关系型模型为静态世界建模,它通过数据快照记录了世界在某一时候的状态,它是静态的。

选型规则:

  • 表关联较多的项目,优先使用MyBatis
  • 持续维护开发、迭代较快的项目建议使用MyBatis,因为一般这种项目需要变化很灵活,对sql的灵活修改要求较高
  • 对于传统项目或者关系模型较为清晰稳定的项目,建议JPA
  • 目前微服务比较火,基于其职责的独立性(可能一个微服务只和一张表打交道),如果模型清晰,可以考虑使用JPA,但如果数据量较大全字段返回数据量大的话可能在性能有影响,需要根据实际情况进行分析
文章作者: Moon Lou
文章链接: https://loumoon.github.io/2021/03/14/Spring Data JPA/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Moon's Blog