25-SpringBoot——核心-Spring Data JPA

Spring Boot & Spring Cloud 同时被 2 个专栏收录
43 篇文章 0 订阅
32 篇文章 16 订阅

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.properties

spring.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);

    }
}
  • 2
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页

打赏

Wang-Junchao

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值