Spring Data JPA之快速入门
CrudRepository规定对于正在管理的实体类复杂的CRUD功能。public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> { <S extends T> S save(S entity); //保存或更新给定的实体,当实体的ID在数据库中存在时执行更新操作 Optional<T> findById(ID primaryKey); // 返回由给定ID标识的实体。 Iterable<T> findAll(); //返回所有实体。 long count(); //返回实体数量。 void delete(T entity); // 删除给定的实体。 boolean existsById(ID primaryKey); //指示是否存在具有给定ID的实体。 // … more functionality omitted. }
除此CrudRepository接口之外,JPA还提供了其他的特定的接口,如JpaRepository、PagingAndSortingRepository、MongoRepository等,这些接口都扩展了CrudRepository。以PagingAndSortingRepository为例,该接口扩充的分页、和排序的的功能。
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> { Iterable<T> findAll(Sort sort); Page<T> findAll(Pageable pageable); } //使用方式,要访问User页面大小为20 的第二页,您可以执行以下操作: PagingAndSortingRepository<User, Long> repository = // … get access to a bean Page<User> users = repository.findAll(new PageRequest(1, 20));//PageRequest是Pageable接口的实现类
另外,在实际开发中,我们也常常使用JpaSpecificationExecutor接口来实现更为复杂的分页、排序查询(动态条件查询),Specification接口就是用来构建查询条件的。
public interface JpaSpecificationExecutor<T> { T findOne(Specification<T> spec); List<T> findAll(Specification<T> spec); Page<T> findAll(Specification<T> spec, Pageable pageable); List<T> findAll(Specification<T> spec, Sort sort); long count(Specification<T> spec); }
上面是Specification接口的内部方法,下面则是Specification的具体使用:
Sort sort=new Sort(Sort.Direction.DESC,"createTime"); Pageable pageable=new PageRequest(page-1,pageSize,sort); Page<ScanLog> pageList=scanLogRepo.findAll(new MySpec(),pageable);//具体方法调用 //下面是通过 内部类封装的 条件查询类 /** * 建立查询条件 */ private class MySpec implements Specification<ScanLog> { @Override public Predicate toPredicate(Root<ScanLog> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Join<ScanLog,ProjectInfo> join = root.join("projectInfo", JoinType.INNER);//关联查询 Path<Integer> param1=root.get("scanType"); Path<Date> param2=root.get("createTime"); Path<String> param3 = join.get("projectName"); Predicate predicate=null; if(projectName==null||projectName.equals("")){ predicate = cb.and(cb.equal(param1,scanType),cb.between(param2,beginTime,endTime)); }else { predicate = cb.and(cb.equal(param1,scanType),cb.like(param3,projectName),cb.between(param2,beginTime,endTime)); } return predicate; } }
二、配置JPA的repositories支持
我们创建了基于JPA的实体类和repository接口,正如Spring管理service、controller中的类(bean),基于JPA创建的类和接口我们也需要在Spring容器中进行注册。可以采用以下几种方式进行配置:
1. XML配置
每一个Spring Data模块都包含repositories元素能够让你简单的基于base-package定义来进行Spring扫描。示例1。 通过XML来开启Spring Data repositories
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <repositories base-package="com.acme.repositories" /> </beans:beans>
2.javaConfig配置(注解配置)
你也可以在一个JavaConfig类中使用@Enable${store}Repositories声明来触发repository的构建。 一个简单的开启Spring Data repositories的配置看上去是这样的:
@Configuration @EnableJpaRepositories("com.acme.repositories") class ApplicationConfiguration { @Bean public EntityManagerFactory entityManagerFactory() { // … } }
如果是多数据源,我们也可以如下配置:
@Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef="entityManagerFactoryPrimary", transactionManagerRef="transactionManagerPrimary", basePackages= {"com.johnson.one.repository","com.johnson.two.repository" })//设置dao(repo)所在位置 public class RepositoryPrimaryConfig { @Autowired private JpaProperties jpaProperties; @Autowired @Qualifier("primaryDS") private DataSource primaryDS; @Bean(name = "entityManagerPrimary") @Primary public EntityManager entityManager(EntityManagerFactoryBuilder builder) { return entityManagerFactoryPrimary(builder).getObject().createEntityManager(); } @Bean(name = "entityManagerFactoryPrimary") @Primary public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) { /*.packages("com.johnson.one.model","com.johnson.two.model") //设置实体类所在位置*/ return builder .dataSource(primaryDS) .properties(getVendorProperties(primaryDS)) .packages("com.johnson.one.model","com.johnson.two.model") //设置实体类所在位置 .persistenceUnit("primaryPersistenceUnit") .build(); } private Map<String, String> getVendorProperties(DataSource dataSource) { return jpaProperties.getHibernateProperties(dataSource); } @Bean(name = "transactionManagerPrimary") @Primary PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) { return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject()); } }
3.独立使用
RepositoryFactorySupport factory =new JpaRepositoryFactory(entityManager); UserRepository repository = factory.getRepository(UserRepository.class);
三、定义查询方法
Repository代理有两种方法可以从方法名称派生于store的查询:
(1)通过直接从方法名称派生查询,如:
List<User> findByLastname(String lastname, Sort sort); List<User> findByLastnameAndAge(String lastname, int age); //从以上的方法的定义中,我们可以发现通过方法名派生查询,对于多条件,长属性名的派生查询,会生成很长的方法名,因此具有一定的局限性,对于一些简单的单表条件查询,比较适合这种方式
(2)通过使用手动定义的查询----通过@Query注解使用自定义sql或hql查询
对于以上通过方法派生查询的不足,很多时候,我们可以通过@Query自定义sql或hql语句的方式去解决。
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.emailAddress = ?1") User findByEmailAddress(String emailAddress); @Query("select u from User u where u.firstname like %?1") List<User> findByFirstnameEndsWith(String firstname);//使用Like表达式 @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true) User findByEmailAddress(String emailAddress);//使用本地Sql即原生sql查询时,需设置nativeQuery=true //使用@Modifying、@Query注解可以派生出 增删改操作 @Modifying @Query("update User u set u.firstname = ?1 where u.lastname = ?2") int setFixedFirstnameFor(String firstname, String lastname);//修改查询 @Modifying @Query("delete from User u where user.role.id = ?1") void deleteInBulkByRoleId(long roleId);//删除查询 @Modifying @Query(value = "insert into orders(name,uid) value(?1,?2)", nativeQuery = true) public void insertOrder(String name,int uid);//利用原生的SQL进行插入操作 }
但是,请注意:Spring Data JPA目前不支持对原生sql查询进行动态排序,因为它必须操纵声明的实际查询,这对于本机SQL无法可靠地执行。但是,您可以通过自己指定计数查询来使用本机查询进行分页,如下例所示:
public interface UserRepository extends JpaRepository<User, Long> { @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1", nativeQuery = true) Page<User> findByLastname(String lastname, Pageable pageable); }
如果要在@Query定义的接口中使用排序,可以通过提供PageRequest或Sort直接使用来进行排序。在需要匹配域模型的Order实例中实际使用的属性Sort,这意味着它们需要解析为查询中使用的属性或别名。默认情况下,Spring Data JPA拒绝任何Order包含函数调用的实例,但您可以使用它JpaSort.unsafe来添加可能不安全的排序。
//使用Sort和JpaSort public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.lastname like ?1%") List<User> findByAndSort(String lastname, Sort sort); @Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%") List<Object[]> findByAsArrayAndSort(String lastname, Sort sort); } repo.findByAndSort("lannister", new Sort("firstname")); //Sort指向域模型中的属性的有效表达式 repo.findByAndSort("stark", new Sort("LENGTH(firstname)")); //Sort包含函数调用无效。有例外 repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)")); //有效Sort包含明确不安全 Order repo.findByAsArrayAndSort("bolton", new Sort("fn_len")); //Sort指向别名函数的有效表达式
上面的 @Query查询语句中,基本上都是使用的是占位符的方式传参并对参数赋值的,除此之外,还可以使用命名参数的方式对参数赋值,示例如下所示:
public interface UserRepository extends JpaRepository<User, Long> { //使用命名参数的最大好处就是当方法参数(包括位置)发生变化时,不用修改查询语句,其次从版本4开始,@Param可以省略 @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname") User findByLastnameOrFirstname(@Param("lastname") String lastname,@Param("firstname") String firstname); }
(3)直接通过entityManager来实现数据库的操作
有些时候我们需要的Repository接口提供的功能是无法用SpringData的方法命名约定来描述,甚至无法用@Query注解来设置查询来实现,此时的SpringData对于我们来说具有一定的局限性,为了实现我们的功能,我们只能在更低的层级上(较SpringData低一级)使用JPA,即使用传统的JPA的方式(操作entityManager)去实现。
@RunWith(SpringRunner.class) @SpringBootTest public class JpademoApplicationTests { @PersistenceContext private EntityManager entityManager;//使用@PersistenceContext注解注入一个entityManager代理对象,如果该对象为null,创建新的对象 @Test public void contextLoads() { String sql="select * from orders where user_id=10"; List<Orders> list= entityManager.createNativeQuery(sql,Orders.class).getResultList(); for (int i=0;i<list.size();i++){ System.out.println(list.get(i).getCreateTime()); } } }
四、SpringData扩展
1、Querydsl扩展
Querydsl是一个框架,可以通过其流畅的API构建静态类型的SQL式查询。几个Spring数据模块通过QuerydslPredicateExecutor提供与Querydsl的集成,如下例所示:
public interface QuerydslPredicateExecutor<T> { Optional<T> findById(Predicate predicate); //查找并返回与之匹配的单个实体Predicate Iterable<T> findAll(Predicate predicate); //查找并返回与之匹配的所有实体Predicate long count(Predicate predicate); //返回匹配的实体数量Predicate boolean exists(Predicate predicate); //返回与Predicate 匹配的实体 // … more functionality omitted. }
要使用Querydsl支持,请QuerydslPredicateExecutor在repository接口上进行扩展,如以下示例所示:
interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> { } //以下是具体调用 Predicate predicate = user.firstname.equalsIgnoreCase("dave") .and(user.lastname.startsWithIgnoreCase("mathews"));//查询firstname为dave并且lastname以mathews开头的用户 userRepository.findAll(predicate);
2、web支持
由于JPA主要用于持久层的开发,所以对于web支持这儿不做介绍。

浙公网安备 33010602011771号