25-SpringBoot——核心-Spring Data JPA
2017-08-27 10:46
741 查看
SpringBoot——核心-Spring Data JPA
【博文目录>>>】
【项目源码>>>】
【Spring Data JPA】
JPA 即Java Persistence API。JPA 是一个基于O/R映射的标准规范(目前最新版本是JPA 2.1 )。所谓规范即只定义标准规则(如注解、接口),不提供实现,软件提供商可以按照标准规范来实现,而使用者只需按照规范中定义的方式来使用,而不用和软件提供商的实现打交道。JPA 的主要实现由Hibernate、EclipseLink 和OpenJPA 等,这也意味着我们只要使用JPA 来开发,无论是哪一个开发方式都是一样的。Spring Data JPA 是Spring Data 的一个子项目,它通过提供基于JPA 的Repository 极大地减少了JPA 作为数据访问方案的代码量。
定义数据访问层
使用Spring Data JPA 建立数据访问层十分简单,只需定义一个继承JpaRepository 的接口即可,定义如下:public interface PersonRepository extends JpaRepository<Person, Long>{}
继承JpaRepository 接口意味着我们默认已经有了下面的数据访问操作方法:
@NoRepositoryBean public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { List<T> findAll(); List<T> findAll(Sort sort); List<T> findAll(Iterable<ID> ids); <S extends T> List<S> save(Iterable<S> entities); void flush(); <S extends T> S saveAndFlush(S entity); void deleteInBatch(Iterable<T> entities); void deleteAllInBatch(); T getOne(ID id); @Override <S extends T> List<S> findAll(Example<S> example); @Override <S extends T> List<S> findAll(Example<S> example, Sort sort); }
配置使用Spring Data JPA
在Spring 环境中,使用Spring Data JPA 可通过@EnableJpaRepositories 注解来开启SpringDataJPA 的支持,@EnableJpaRepositories 接收的value 参数用来扫描数据访问层所在包下的数据访问的接口定义。定义查询方法
在讲解查询方法前,假设我们有一张数据表名叫PERSON ,有ID (Number)、NAME(Varchar 2)、AGE (Number)、ADDRESS(Varchar2)几个字段;对应的实体类叫Person ,分别有id (Long)、name (String)、age (Integer)、address (String)。( 1 )根据属性名查询
Spring Data JPA 支持通过定义在Repository 接口中的方法名来定义查询,而方法名是根据实体类的属性名来确定的。
1) 常规查询。根据属性名来定义查询方法,示例如下:
从代码可以看出,这里使用了findBy 、Like 、And 这样的关键字。其中findBy 可以用find 、read、readBy 、query 、queryBy 、get、getBy 来代替。而Like 和and 这类查询关键字,如表所示:
2 )限制结果数量。结果数量是用top 和first 关键字来实现的。
(2 )使用JPA 的 NamedQuery 查询
Spring Data JPA 支持用 JPA 的NameQuery 来定义查询方法,即一个名称映射一个查询语句。定义如下:
(3)使用@Query查询
1 )使用参数索引。Spring Data JPA 还支持用@Query 注解在接口的方法上实现查询,例如:
2 )使用命名参数。上面的例子是使用参数的索引号来查询的,在Spring Data JPA 里还支持在语句里用名称来匹配查询参数。
3 )更新查询。Spring Data JPA 支持@Modifying 和@Query 注解组合来实现更新查询,其中返回值int 表示更新语句影响的行数
(4) Specification
JPA 提供了基于准则查询的方式,即Criteria 查询。而Spring Data JPA 提供了一个Specification (规范)接口让我们可以更方便地构造准则查询, Specification 接口定义了一个toPredicate 方法用来构造查询条件。
1) 定义。我们的接口类必需实现JpaSpecificationExecutor 接口,代码如下:
然后需要定义Criterial查询,代码如下
(5)排序与分页
Spring Data JPA 充分考虑了在实际开发中所必需的排序和分页的场景,为我们提供了Sort类以及Page 接口和Pageable 接口。
自定义Repository 的实现
Spring Data 提供了和CrudRepository 、PagingAndSortingRepository; Spring Data JPA 也提供了JpaRepository。我们也可以自定义Repository 的实现Spring Boot 的JPA支持
Spring boot-starter-data-jpa 依赖于spring-boot-starter-jdbc ,而Spring Boot 对JDBC做了一些白动配置。源码放置在org.springframework. boot.autoconfigure.jdbc。从源码分析可以看出,我们通过spring.datasource 为前缀的属性自动配置dataSource。Spring Boot 自动开启了注解事务的支持( @EnableTransactionManagement );还配置了一个jdbcTemplate 。Spring Boot 还提供了一个初始化数据的功能:放置在类路径下的schema.sql文件会自动用来初始化表结构;放置在类路径下的data.sql 文件会在动用来填充表数据。
对JPA 的自动配置
Spring Boot 对JPA 的自动配置放置在org.springframework. boot.autocon figure. orm.jpa 下从HibemateJpaAutoConfiguration 可以看出, Spring Boot 默认JPA 的实现者是Hibernate,HibemateJpaAutoCorifiguration 依赖于DataSourceAutoConfiguration 。从JpaProperties 的源码可以看出,配置JPA 可以使用spring.jpa 为前缀的属性在. application.properties 中配置。从JpaBaseConfiguration 的源码中可以看出, Spring Boot 为我们配置了transactionManager、jpaVendorAdapter、entityManagerFactory 等Bean。JpaBaseConfiguration 还有一个getPackagesToScan方法,可以自动扫描注解有@Entity 的实体类。在Web 项目中我们经常会遇到在控制器或者页面访问数据的时候出现会话连接己关闭的错误,这时候我们会配置一个Open Entity Manager (Session) In View 这个过滤器。令人惊喜的是, Spring Boot 为我们自动配置了OpenEntityManagerlnViewInterceptor 这个Bean ,并注册SpringMVC 的拦截器中。
对Spring Data JPA 的自动配置
Spring Boot 对Spring Data JPA 的自动配置放置在org.springframework.boot.autoconfigure.data.jpa下。从JpaRepositoriesAutoConfiguration 和JpaRepositoriesAutoConfigureRegistrar 源码可以看出, JpaRepositoriesAutoConfiguration 是依赖于HibernateJpaAutoConfiguration 配置的,且Spring Boot 自动开启了对Spring Data JPA 的支持,即我们无须在配置类中显示声明@EnableJpaRepositories。
通过上面的分析可知,我们在Spring Boot 下使用Spring Data JPA ,在项目的Maven 依赖里添加spring-boot-stater-data-jpa,然后只需定义DataSource、实体类和数据访问烂,并在且在需要使用数据访问的地方注入数据访问层的Bean 即可,无须任何额外自己置。
【代码实现】
application.propertiesspring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.url=jdbc\:mysql\://localhost\:3306/springboot spring.datasource.username=root spring.datasource.password=123456 #1 spring.jpa.hibernate.ddl-auto=update #2 spring.jpa.show-sql=true spring.jackson.serialization.indent_output=true
data.sql
INSERT INTO person (id, name, age, address) VALUES (1, '王俊超', 32, '合肥'); INSERT INTO person (id, name, age, address) VALUES (2, 'xx', 31, '北京'); INSERT INTO person (id, name, age, address) VALUES (3, 'yy', 30, '上海'); INSERT INTO person (id, name, age, address) VALUES (4, 'zz', 29, '南京'); INSERT INTO person (id, name, age, address) VALUES (5, 'aa', 28, '武汉'); INSERT INTO person (id, name, age, address) VALUES (6, 'bb', 27, '合肥');
package com.example.spring.boot.jpa.dao; import com.example.spring.boot.jpa.domain.Person; import com.example.spring.boot.jpa.support.CustomRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.List; /** * Author: 王俊超 * Date: 2017-07-17 20:56 * All Rights Reserved !!! */ public interface PersonRepository extends CustomRepository<Person, Long> { List<Person> findByAddress(String address); Person findByNameAndAddress(String name, String address); @Query("select p from Person p where p.name= :name and p.address= :address") Person withNameAndAddressQuery(@Param("name") String name, @Param("address") String address); Person withNameAndAddressNamedQuery(String name, String address); }
package com.example.spring.boot.jpa.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.NamedQuery; /** * Author: 王俊超 * Date: 2017-07-17 20:57 * All Rights Reserved !!! */ @Entity @NamedQuery(name = "Person.withNameAndAddressNamedQuery", query = "select p from Person p where p.name=?1 and address=?2") public class Person { @Id @GeneratedValue private Long id; private String name; private Integer age; private String address; public Person() { } public Person(Long id, String name, Integer age, String address) { super(); this.id = id; this.name = name; this.age = age; this.address = address; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
package com.example.spring.boot.jpa.spec; import org.springframework.data.jpa.domain.Specification; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.SingularAttribute; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import static com.google.common.collect.Iterables.toArray; /** * Author: 王俊超 * Date: 2017-07-17 21:07 * All Rights Reserved !!! */ public class CustomerSpecs { public static <T> Specification<T> byAuto(final EntityManager entityManager, final T example) { //1 final Class<T> type = (Class<T>) example.getClass();//2 return new Specification<T>() { @Override public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) { List<Predicate> predicates = new ArrayList<>(); //3 EntityType<T> entity = entityManager.getMetamodel().entity(type);//4 for (Attribute<T, ?> attr : entity.getDeclaredAttributes()) {//5 Object attrValue = getValue(example, attr); //6 if (attrValue != null) { if (attr.getJavaType() == String.class) { //7 if (!StringUtils.isEmpty(attrValue)) { //8 predicates.add(cb.like(root.get(attribute(entity, attr.getName(), String.class)), pattern((String) attrValue))); //9 } } else { predicates.add(cb.equal(root.get(attribute(entity, attr.getName(), attrValue.getClass())), attrValue)); //10 } } } return predicates.isEmpty() ? cb.conjunction() : cb.and(toArray(predicates, Predicate.class));//11 } /** * 12 */ private <T> Object getValue(T example, Attribute<T, ?> attr) { return ReflectionUtils.getField((Field) attr.getJavaMember(), example); } /** * 13 */ private <E, T> SingularAttribute<T, E> attribute(EntityType<T> entity, String fieldName, Class<E> fieldClass) { return entity.getDeclaredSingularAttribute(fieldName, fieldClass); } }; } /** * 14 */ static private String pattern(String str) { return "%" + str + "%"; } }
package com.example.spring.boot.jpa.support; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.NoRepositoryBean; import java.io.Serializable; /** * Author: 王俊超 * Date: 2017-07-17 21:00 * All Rights Reserved !!! */ @NoRepositoryBean public interface CustomRepository<T, ID extends Serializable> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> { Page<T> findByAuto(T example, Pageable pageable); }
package com.example.spring.boot.jpa.support; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import javax.persistence.EntityManager; import java.io.Serializable; /** * Author: 王俊超 * Date: 2017-07-17 21:04 * All Rights Reserved !!! */ public class CustomRepositoryFactoryBean<T extends JpaRepository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> { // 1 public CustomRepositoryFactoryBean(Class<? extends T> repositoryInterface) { super(repositoryInterface); } @Override protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {// 2 return new CustomRepositoryFactory(entityManager); } private static class CustomRepositoryFactory extends JpaRepositoryFactory {// 3 public CustomRepositoryFactory(EntityManager entityManager) { super(entityManager); } @Override @SuppressWarnings({"unchecked"}) protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository( RepositoryInformation information, EntityManager entityManager) {// 4 return new CustomRepositoryImpl<T, ID>((Class<T>) information.getDomainType(), entityManager); } @Override protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { // 5 return CustomRepositoryImpl.class; } } }
package com.example.spring.boot.jpa.support; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import javax.persistence.EntityManager; import java.io.Serializable; import static com.example.spring.boot.jpa.spec.CustomerSpecs.*; /** * Author: 王俊超 * Date: 2017-07-17 21:02 * All Rights Reserved !!! */ public class CustomRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements CustomRepository<T, ID> { private final EntityManager entityManager; public CustomRepositoryImpl(Class<T> domainClass, EntityManager entityManager) { super(domainClass, entityManager); this.entityManager = entityManager; } @Override public Page<T> findByAuto(T example, Pageable pageable) { return findAll(byAuto(entityManager, example), pageable); } }
package com.example.spring.boot.jpa.web; import com.example.spring.boot.jpa.dao.PersonRepository; import com.example.spring.boot.jpa.domain.Person; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class DataController { //1 Spring Data JPA已自动为你注册bean,所以可自动注入 @Autowired PersonRepository personRepository; /** * 保存 * save支持批量保存:<S extends T> Iterable<S> save(Iterable<S> entities); * <p> * 删除: * 删除支持使用id,对象以,批量删除及删除全部: * void delete(ID id); * void delete(T entity); * void delete(Iterable<? extends T> entities); * void deleteAll(); */ @RequestMapping("/save") public Person save(String name, String address, Integer age) { Person p = personRepository.save(new Person(null, name, age, address)); return p; } /** * 测试findByAddress */ @RequestMapping("/q1") public List<Person> q1(String address) { List<Person> people = personRepository.findByAddress(address); return people; } /** * 测试findByNameAndAddress */ @RequestMapping("/q2") public Person q2(String name, String address) { Person people = personRepository.findByNameAndAddress(name, address); return people; } /** * 测试withNameAndAddressQuery */ @RequestMapping("/q3") public Person q3(String name, String address) { Person p = personRepository.withNameAndAddressQuery(name, address); return p; } /** * 测试withNameAndAddressNamedQuery */ @RequestMapping("/q4") public Person q4(String name, String address) { Person p = personRepository.withNameAndAddressNamedQuery(name, address); return p; } /** * 测试排序 */ @RequestMapping("/sort") public List<Person> sort() { List<Person> people = personRepository.findAll(new Sort(Direction.ASC, "age")); return people; } /** * 测试分页 */ @RequestMapping("/page") public Page<Person> page() { Page<Person> pagePeople = personRepository.findAll(new PageRequest(1, 2)); return pagePeople; } @RequestMapping("/auto") public Page<Person> auto(Person person) { Page<Person> pagePeople = personRepository.findByAuto(person, new PageRequest(0, 10)); return pagePeople; } }
package com.example.spring.boot.jpa; import com.example.spring.boot.jpa.dao.PersonRepository; import com.example.spring.boot.jpa.support.CustomRepositoryFactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; /** * Author: 王俊超 * Date: 2017-07-17 21:19 * All Rights Reserved !!! */ @SpringBootApplication @EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class) public class SampleApplication { @Autowired PersonRepository personRepository; public static void main(String[] args) { SpringApplication.run(SampleApplication.class, args); } }
相关文章推荐
- Springboot:Spring Data Jpa的使用
- SpringBoot Data JPA 关联表查询的方法
- 代码生成器CodeGenerator(目前只支持SpringBoot/SpringDataJpa)
- springboot+springdatajpa 数据库命名
- 详解基于Spring Boot与Spring Data JPA的多数据源配置
- spring boot(五):spring data jpa的使用
- Spring Cloud Spring Boot mybatis分布式微服务云架构(十八)使用Spring-data-jpa(2)
- spring boot 整合spring data jpa
- Spring Boot学习进阶笔记(四)-多数据源配置(JdbcTemplate、Spring-data-jpa)
- SpringBoot(五) :spring data jpa 的使用
- 深入学习spring-boot系列(二)--使用spring-data-jpa
- SpringBoot入门:Spring Data JPA 和 JPA(理论)
- spring boot + spring data jpa 批量插入 + POI读取 + 文件上传
- springboot(五):spring data jpa的使用
- Spring Boot结合spring-data-jpa
- SpringBoot学习笔记 - 数据访问(Spring Data JPA)
- 使用spring-boot-starter-data-jpa 怎么配置使运行时输出SQL语句
- spring boot(五):spring data jpa的使用
- Spring Boot学习进阶笔记(三)-Spring-data-jpa
- SpringBoot整合Spring Data JPA、MySQL、Druid并使用Mockito实现单元测试