springdata教程
Spring Data
Spring 的一个子项目。用于简化数据库访问,支持NoSQL 和 关系数据存储。其主要目标是使数据库的访问变得方便快捷。
SpringData 项目所支持 NoSQL 存储:
MongoDB (文档数据库)
Neo4j(图形数据库)
Redis(键/值存储)
Hbase(列族数据库)
SpringData 项目所支持的关系数据存储技术
JDBC、JPA
JPA Spring Data
致力于减少数据访问层 (DAO) 的开发量.开发者唯一要做的,就只是声明持久层的接口,其他都交给 Spring Data JPA 。
Spring Data JPA 做的便是规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑。
例如:UserDao.findUserById() 这样一个方法声明,大致应该能判断出这是根据给定条件的 ID 查询出满足条件的User对象。
Repository 接口
Repository是 Spring Data 的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法 。
Spring Data可以让我们只定义接口,只要遵循 Spring Data的规范,就无需写实现类。
与继承 Repository 等价的一种方式,就是在持久层接口上使用 @RepositoryDefinition 注解,并为其指定 domainClass 和 idClass 属性。
Repository 的子接口
Repository: 仅仅是一个标识,表明任何继承它的均为仓库接口类
CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法
PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法
JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法
自定义的 XxxxRepository 需要继承 JpaRepository,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力。
JpaSpecificationExecutor: 不属于Repository体系,实现一组 JPA Criteria 查询相关的方法
SpringData 方法定义规范
按照 Spring Data 的规范,查询方法以 find | read | get 开头。
涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性以首字母大写。
例如:定义一个 Entity 实体类
class User{
private String firstName;
private String lastName;
}
使用And条件连接时,应这样写:
findByLastNameAndFirstName(String lastName,String firstName);
条件的属性名称与个数要与参数的位置与个数一一对应
支持的关键字
查询方法解析流程
对于精准属性查询可以明确在属性之间加上 "_" 以显式表达意图
例如:"findByUser_DepUuid()" 或者 "findByUserDep_uuid()"
“_”是对属性进行拆分。
特殊的参数: 还可以直接在方法的参数上加入分页或排序的参数,比如:
Page<UserModel> findByName(String name, Pageable pageable);
List<UserModel> findByName(String name, Sort sort);
@Query
可以在接口上面自定义查询语句。
@Query("SELECT p FROM Person p WHERE p.lastName = ?1 AND p.email = ?2")
List<Person> testQueryAnnotationParams1(String lastName, String email);
查询中 “?n” 个数需要与方法定义的参数个数相一致,并且顺序也要一致
也可以在查询中用@Param直接指定接受参数的name
@Query("SELECT p FROM Person p WHERE p.lastName = :lastName AND p.email = :email")
List<Person> testQueryAnnotationParams2(@Param("email") String email, @Param("lastName") String lastName);
通过显示指定nativeQuery为true实现本地查询
@Query(value="SELECT count(id) FROM jpa_persons", nativeQuery=true)
long getTotalCount();
@Modifying
@Modifying与@Query一起声明,用来指定@Query中的语句修改或删除操作。
注意: JPQL 不支持使用 INSERT。(保存操作调用JpaRepository save)
默认情况下, SpringData 的每个方法上有事务, 但都是一个只读事务. 他们不能完成修改操作!
事务
Spring Data 提供了默认的事务处理方式,即所有的查询均声明为只读事务。
如需改变 Spring Data 提供的事务默认方式,可以在方法上注解 @Transactional 声明
CrudRepository
该接口提供了最基本的对实体类的添删改查操作 。
T save(T entity);//保存单个实体
Iterable<T> save(Iterable<? extends T> entities);//保存集合
T findOne(ID id);//根据id查找实体
boolean exists(ID id);//根据id判断实体是否存在
Iterable<T> findAll();//查询所有实体,不用或慎用!
long count();//查询实体数量
void delete(ID id);//根据Id删除实体
void delete(T entity);//删除一个实体
void delete(Iterable<? extends T> entities);//删除一个实体集合
void deleteAll();//删除所有实体。
PagingAndSortingRepository
该接口提供了分页与排序功能 。
Iterable<T> findAll(Sort sort); //排序
Page<T> findAll(Pageable pageable); //分页查询(含排序功能)
JpaRepository
该接口提供了JPA的相关功能 。
List<T> findAll(); //查找所有实体
List<T> findAll(Sort sort); //排序、查找所有实体
List<T> save(Iterable<? extends T> entities);//保存集合
void flush();//执行缓存与数据库同步
T saveAndFlush(T entity);//强制执行持久化
void deleteInBatch(Iterable<T> entities);//删除一个实体集合
JpaSpecificationExecutor
该接口实现了一组 JPA Criteria 查询相关的方法 。
T findOne(Specification<T> t) :查询单结果。
Specification:封装JPA Criteria查询条件。
自定义 Repository 方法
1 为指定Repository 添加自定义方法
步骤示例:
(1) 定义自定义接口
public interface PersonDao {
//通过定义自定义方法
void test();
}
(2) 让当前xxxRepository接口继承PersonDao
public interface PersonRepository
extends JpaRepository<Person, Integer>, JpaSpecificationExecutor<Person>, PersonDao {
//直接在当前xxxRepository中自定义方法
void test1();
}
(3) 实现自定义方法
在当前xxxRepository接口package下创建xxxRepositoryImpl,并且实现自定义方法
public class PersonRepositoryImpl implements PersonDao{
@PersistenceContext
private EntityManager entityManager;
@Override
public void test() {
Person person = entityManager.find(Person.class, 11);
System.out.println("-->" + person);
}
public void test2() {
System.out.println(entityManager.createQuery("FROM Person o").getResultList().size());
}
}
默认情况下, Spring Data 会在 base-package 中查找 "接口名Impl" 作为实现类. 也可以通过 repository-impl-postfix 声明后缀.
<jpa:repositories base-package="com.springdata"
entity-manager-factory-ref="entityManagerFactory" repository-impl-postfix="Impl"></jpa:repositories>
2 为所有的 Repository 都添加自实现的方法
(1) 声明继承 Spring Data 的 Repository的接口, 在该接口中声明自定义方法。
@NoRepositoryBean
public interface CommonMethodTest<T, ID extends Serializable>
extends JpaRepository<T, ID>{
void method();
}
(2) 实现声明接口,并且继承SimpleJpaRepository
@NoRepositoryBean
public class CommonMethodTestImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements CommonMethodTest<T, ID> {
public CommonMethodTestImpl(Class<T> domainClass, EntityManager em) {
super(domainClass, em);
}
@Override
public void method() {
System.out.println("...METHOD TEST...");
}
}
(3) 创建继承JpaRepositoryFactoryBean 的类,指定自定义方法的接口。
public class CommonJpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
extends JpaRepositoryFactoryBean<T, S, ID> {
protected RepositoryFactorySupport createRepositoryFactory(
EntityManager entityManager) {
return new CommonRepositoryFactory(entityManager);
}
private static class CommonRepositoryFactory<T, I extends Serializable>
extends JpaRepositoryFactory {
private EntityManager entityManager;
public CommonRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new CommonMethodTestImpl<T, I>(
(Class<T>) metadata.getDomainType(), entityManager);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return CommonMethodTest.class;
}
}
}
(4) 配置 <jpa:repositories /> 节点的 factory-class 属性指向(3)创建的类
<jpa:repositories base-package="com.springdata.commonrepositorymethod"
entity-manager-factory-ref="entityManagerFactory"
factory-class="com.springdata.commonrepositorymethod.CommonJpaRepositoryFactoryBean"></jpa:repositories>