10.SpringBoot学习(十)——JDBC之 Spring Boot Jpa

本文详细介绍了Spring Boot结合JPA进行数据库操作,涵盖JPA概述、特点、演示环境、代码结构及实现。文章还深入探讨了JPA的源码分析,包括@Repository的加载机制、UserRepository的动态代理以及JPA查询流程,帮助读者理解Spring Boot与JPA的集成应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.简介

1.1 概述

The Java Persistence API is a standard technology that lets you “map” objects to relational databases. The spring-boot-starter-data-jpa POM provides a quick way to get started. It provides the following key dependencies:

  • Hibernate: One of the most popular JPA implementations.
  • Spring Data JPA: Makes it easy to implement JPA-based repositories.
  • Spring ORMs: Core ORM support from the Spring Framework.

Java Persistence API 是一种标准技术,可让您将对象“映射”到关系数据库。 spring-boot-starter-data-jpa POM提供了一种快速入门的方法。它提供以下关键依赖性:

  • Hibernate:最流行的JPA实现之一。
  • Spring Data JPA:使基于JPA的存储库的实现变得容易。
  • Spring ORMs:Spring 框架对Core ORM的支持。

1.2 特点

image-20200721204052309

  • 基于Spring和JPA构建存储库的先进支持
  • 支持 Querydsl 谓词,从而支持类型安全的JPA查询
  • 实体类的透明审核
  • 分页支持,动态查询执行,集成自定义数据访问代码的能力
  • 在启动时验证 @Query 带注释的查询
  • 支持基于XML的实体映射
  • 通过引入 @EnableJpaRepositories,支持基于 JavaConfig 的存储库配置

2.演示环境

  1. JDK 1.8.0_201
  2. Spring Boot 2.2.0.RELEASE
  3. 构建工具(apache maven 3.6.3)
  4. 开发工具(IntelliJ IDEA )

3.演示代码

3.1 代码说明

演示基于 spring-boot-starter-data-jpa 来操作数据库的简单 web mvc 项目。包括以下常用场景:

  • 单表的增、删、改、查
  • 多表关联查询(这里使用2张表)
  • 复杂条件混合查询
  • 分页查询

3.2 代码结构

image-20200721204828632

3.3 maven 依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
</dependencies>

3.4 配置文件

application.properties

spring.datasource.url=jdbc:mysql://172.16.11.125:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# 打印sql
spring.jpa.show-sql=true
# 自动建表
spring.jpa.hibernate.ddl-auto=create
# 方言;innodb存储引擎
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
# 格式化sql
spring.jpa.properties.hibernate.format_sql=true
# 打印sql中参数
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace

spring.data.web.pageable.default-page-size=3
spring.data.web.pageable.page-parameter=pageNum
spring.data.web.pageable.size-parameter=pageSize
spring.data.web.sort.sort-parameter=orderBy

3.5 java代码

Order.java

@Entity
@Table(name = "t_order")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;
    @Column(nullable = false)
    private Long userId;
    @Column(nullable = false, unique = true)
    private String orderCode;
    @Column(nullable = false)
    private BigDecimal totalMoney;
    @Column(nullable = false)
    private String orderDate;

    public Order() {}

    public Order(Long userId, String orderCode, BigDecimal totalMoney, String orderDate) {
        this.userId = userId;
        this.orderCode = orderCode;
        this.totalMoney = totalMoney;
        this.orderDate = orderDate;
    }

   	// get&set&toString
}

User.java

@Entity
@Table(name = "t_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false, unique = true, length = 32)
    private String name;
    @Column(nullable = false)
    private Integer age;
    private String birthday;
    private String address;
    @Column(nullable = false, length = 16)
    private String phone;

    public User() {}

    public User(String name, Integer age, String birthday, String address, String phone) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
        this.address = address;
        this.phone = phone;
    }

   	// get&set&toString
}

OrderRepository.java

@Repository
public interface OrderRepository extends JpaRepository<Order, Long>, JpaSpecificationExecutor<Order> {

    @Query(value = "select "
        + "o.id as orderId, o.orderCode as orderCode, o.orderDate as orderDate, o.userId as userId, "
        + "u.address as address, u.phone as phone, u.age as age from Order o inner join User u on o.userId = u.id where o.orderCode = ?1")
    OrderInfo selectOrderByCode(String orderCode);
}

UserRepository.java

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    @Query("select u from User u where u.name = ?1")
    User findUserByName(String name);

    @Query("select u from User u")
    Page<User> findByPage(Pageable pageable);

    @Query("select u from User u where u.phone = :phone")
    List<User> findUserByPhone(@Param("phone") String phone);

    @Modifying
    @Transactional
    @Query("update User set phone = ?1 where name = ?2")
    int updateByName(String phone, String name);

    @Modifying
    @Transactional
    @Query("delete from User where name = :name")
    int deleteByName(@Param("name") String name);
}

OrderService.java

public interface OrderService {

    /**
     * 查询所有user
     * @return order
     */
    List<Order> selectList();

    /**
     * 根据订单号关联查询
     * @param orderCode 订单号
     * @return OrderInfo
     */
    OrderInfo selectOrderByCode(String orderCode);

    /**
     * 使用example查询
     * @param order 查询参数
     * @return Order
     */
    List<Order> selectByExample(Order order);

    /**
     * 多条件组合查询
     * @param orderParam 查询参数
     * @return Order
     */
    Page<Order> selectByCondition(OrderParam orderParam, Pageable pageable);
}

UserService.java

public interface UserService {

    /**
     * 查询所有数据
     * @return user
     */
    List<User> selectList();

    /**
     * 根据名称查询
     * @param name name
     * @return user
     */
    User findUserByName(String name);

    /**
     * 根据电话查询
     * @param phone 电话
     * @return user
     */
    List<User> findUserByPhone(String phone);

    /**
     * 分页查询
     * @param pageable 分页参数
     * @return user
     */
    Page<User> findByPage(Pageable pageable);

    /**
     * 根据名称更新电话
     * @param phone 电话
     * @param name 名称
     * @return 影响行数
     */
    User updateByName(String phone, String name);

    /**
     * 根据名称删除
     * @param name 名称
     * @return 影响行数
     */
    User deleteByName(String name);

    /**
     * 新增
     * @param user user
     * @return user
     */
    User add(User user);
}

UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public List<User> selectList() {
        return userRepository.findAll();
    }

    @Override
    public User findUserByName(String name) {
        return userRepository.findUserByName(name);
    }

    @Override
    public List<User> findUserByPhone(String phone) {
        return userRepository.findUserByPhone(phone);
    }

    @Override
    public Page<User> findByPage(Pageable pageable) {
        return userRepository.findByPage(pageable);
    }

    @Override
    public User updateByName(String phone, String name) {
        userRepository.updateByName(phone, name);
        return findUserByName(name);
    }

    @Override
    public User deleteByName(String name) {
        User user = findUserByName(name);
        userRepository.deleteByName(name);
        return user;
    }

    @Override
    public User add(User user) {
        return userRepository.save(user);
    }
}

OrderServiceImpl.java

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Override
    public List<Order> selectList() {
        return orderRepository.findAll();
    }

    @Override
    public OrderInfo selectOrderByCode(String orderCode) {
        return orderRepository.selectOrderByCode(orderCode);
    }

    @Override
    public List<Order> selectByExample(Order order) {
        // exact:精确比配 contains: 模糊匹配 startsWith:从头匹配
        // 同 matcher -> matcher.exact();
        ExampleMatcher exampleMatcher = matching().withMatcher("userId", GenericPropertyMatcher::exact)
            .withMatcher("orderCode", GenericPropertyMatcher::contains)
            .withMatcher("orderDate", GenericPropertyMatcher::startsWith);
        Example<Order> example = Example.of(order, exampleMatcher);
        return orderRepository.findAll(example);
    }

    @Override
    public Page<Order> selectByCondition(OrderParam orderParam, Pageable pageable) {
        return orderRepository.findAll((root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            // equal userId
            if (Objects.nonNull(orderParam.getUserId())) {
                predicates.add(cb.equal(root.get("userId"), orderParam.getUserId()));
            }
            // like orderCode
            if (StringUtils.isNotBlank(orderParam.getOrderCode())) {
                predicates.add(cb.like(root.get("orderCode"), "%" + orderParam.getOrderCode() + "%"));
            }
            // between
            if (StringUtils.isNotBlank(orderParam.getOrderStartDate()) && StringUtils.isNotBlank(orderParam.getOrderEndDate())) {
                predicates.add(cb.between(root.get("orderDate"), orderParam.getOrderStartDate(), orderParam.getOrderEndDate()));
            }
            // greater than
            if (Objects.nonNull(orderParam.getTotalMoney())) {
                predicates.add(cb.greaterThan(root.get("totalMoney"), orderParam.getTotalMoney()));
            }
            return query.where(predicates.toArray(new Predicate[0])).getRestriction();
        }, pageable);
    }
}

OrderInfo.java

public interface OrderInfo {

    Long getUserId();
    Long getOrderId();
    Integer getAge();
    String getOrderCode();
    String getAddress();
    String getPhone();
    String getOrderDate();
}

OrderParam.java

public class OrderParam {

    private Long id;
    private Long userId;
    private String orderCode;
    private BigDecimal totalMoney;
    private String orderStartDate;
    private String orderEndDate;
    // get&set
}

OrderController.java

@RestController
@RequestMapping(value = "/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @GetMapping(value = "/list")
    public List<Order> list() {
        return orderService.selectList();
    }

    @GetMapping(value = "/queryByCode/{orderCode}")
    public OrderInfo queryByCode(@PathVariable String orderCode) {
        return orderService.selectOrderByCode(orderCode);
    }

    @GetMapping(value = "/queryByExample")
    public List<Order> selectByExample(@RequestBody Order order) {
        return orderService.selectByExample(order);
    }

    @GetMapping(value = "/queryByCondition")
    public Page<Order> queryByCondition(@RequestBody OrderParam orderParam, Pageable pageable) {
        return orderService.selectByCondition(orderParam, pageable);
    }
}

UserController.java

@RestController
@RequestMapping(value = "/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping(value = "/list")
    public List<User> list() {
        return userService.selectList();
    }

    @GetMapping(value = "/findByName/{name}")
    public User findByName(@PathVariable String name) {
        return userService.findUserByName(name);
    }

    @GetMapping(value = "/findByPhone/{phone}")
    public List<User> findByPhone(@PathVariable String phone) {
        return userService.findUserByPhone(phone);
    }

    @GetMapping(value = "/page")
    public Page<User> page(Pageable pageable) {
        return userService.findByPage(pageable);
    }

    @PostMapping(value = "/add")
    public User add(User user) {
        return userService.add(user);
    }

    @PutMapping(value = "/updateByName")
    public User updateByName(@RequestBody User user) {
        return userService.updateByName(user.getPhone(), user.getName());
    }

    @DeleteMapping(value = "/deleteByName/{name}")
    public User deleteByName(@PathVariable String name) {
        return userService.deleteByName(name);
    }
}

InitializeDataCommand.java

@Component
public class InitializeDataCommand implements CommandLineRunner {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private OrderRepository orderRepository;

    @Override
    public void run(String... args) throws Exception {

        User user1 = new User("zhangsan", 20, "2000-01-01", "shenzhen", "13888888888");
        User user2 = new User("lisi", 21, "1999-01-01", "shanghai", "13777777777");
        User user3 = new User("wangwu", 22, "1998-01-01", "beijing", "13666666666");
        User user4 = new User("zhaoliu", 23, "1997-01-01", "guangzhou", "13555555555");
        User user5 = new User("sunqi", 24, "1996-01-01", "wuhan", "13444444444");

        SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime now = LocalDateTime.now();
        List<User> users = userRepository.saveAll(Arrays.asList(user1, user2, user3, user4, user5));

        List<Order> orders = users.stream().map(user -> {
            Order order = new Order();
            order.setUserId(user.getId());
            order.setOrderCode("OC202005231205000" + (users.indexOf(user) + 1));
            order.setOrderDate(dateTimeFormatter.format(now.minusDays(random.nextInt(100))));
            order.setTotalMoney(BigDecimal.valueOf(random.nextDouble() * random.nextInt(10000)));
            return order;
        }).collect(Collectors.toList());

        orderRepository.saveAll(orders);
    }
}

3.6 git 地址

spring-boot/spring-boot-06-jdbc/spring-boot-data-jpa

4.效果展示

启动 SpringBootJpaDemoApplication.main 方法,在 spring-boot-data-jpa.http 访问下列地址,观察输出信息是否符合预期。

4.1 t_user

查询用户列表(所有)

### GET /user/list
GET http://localhost:8080/user/list
Accept: application/json

image-20200721210408036

根据用户名查询

### GET /user/findByName/{name}
GET http://localhost:8080/user/findByName/lisi
Accept: application/json

image-20200721210526269

根据手机号查询

### GET /user/findByPhone/{phone}
GET http://localhost:8080/user/findByPhone/13666666666
Accept: application/json

image-20200721210624524

查询用户列表(分页)

### GET /user/page
GET http://localhost:8080/user/page
Accept: application/json
Content-Type: application/json

{
  "pageable":{
     "pageNumber":1,
     "pageSize":3,
     "orderBy":"age desc"
  }
}

image-20200721210744826

更新用户信息

### PUT /user/updateByName
PUT http://localhost:8080/user/updateByName
Content-Type: application/json

{
  "name": "zhangsan",
  "phone": "13456789012"
}

image-20200721210849107

删除用户

### DELETE /user/deleteByName/{name}
DELETE http://localhost:8080/user/deleteByName/zhangsan
Content-Type: application/json

image-20200721210950916

4.2 t_order

查询订单列表(所有)

### GET  /order/list
GET http://localhost:8080/order/list
Accept: application/json

image-20200721211249281

根据订单编号关联查询

### GET /order/queryByCode/{orderCode}
GET http://localhost:8080/order/queryByCode/OC2020052312050002
Accept: application/json

image-20200721211331472

多条件查询订单

### GET /order/queryByExample
GET http://localhost:8080/order/queryByExample
Accept: application/json
Content-Type: application/json

{
"userId":2,
"orderCode":"OC202005231",
"orderDate": "2020-05-17"
}

image-20200721211616720

多条件混合查询

### GET /order/queryByCondition
GET http://localhost:8080/order/queryByCondition
Accept: application/json
Content-Type: application/json

{
  "userId": 2,
  "orderCode": "OC20200523",
  "totalMoney": 20,
  "orderStartDate": "2020-02-10 16:17:12",
  "orderEndDate": "2020-05-30 16:17:12"
}

image-20200721211716482

5.源码分析

5.1 @Repository 如何加载的?

SpringBooApplication 应用启动时,会调用 createApplicationContext 方法,这里指定了默认 web 应用的类型是 AnnotationConfigServletWebServerApplicationContext。在 AnnotationConfigServletWebServerApplicationContext 的构造函数中,调用了 AnnotatedBeanDefinitionReader 的构造方法,最终通过 registerAnnotationConfigProcessors 方法将一些和注解扫描相关的 Processor 注册到 context 中,其中有一个类是 ConfigurationClassPostProcessor,这个比较关键。

在调用 refreshContext 方法时,最终会调用到 AbstractApplicationContext 的 refresh 方法,在这个流程中,invokeBeanFactoryPostProcessors 方法触发了 ConfigurationClassPostProcessor,将注解进行扫描,从而注册到 registry 中。

image-20200722214637396

5.2 UserRepository 的动态代理

image-20200726121906729

UserRepository 继承自 JpaRepository,JpaRepository 有一个 FactoryBean 叫 JpaRepositoryFactoryBean,它实现了InitializingBean 接口,在 afterPropertiesSet 中进行了代理操作。同时它也实现了 FactoryBean 接口,提供一个 getObject 方法来获取 bean 的实例。

在 factory.getRepository 方法中,有一个 getRepositoryInformation 方法,它的实现如下

private RepositoryInformation getRepositoryInformation(RepositoryMetadata metadata,
                                                       RepositoryComposition composition) {

    RepositoryInformationCacheKey cacheKey = new RepositoryInformationCacheKey(metadata, composition);

    return repositoryInformationCache.computeIfAbsent(cacheKey, key -> {

        // 这里的 baseClass 为 SimpleJpaRepository
        Class<?> baseClass = repositoryBaseClass.orElse(getRepositoryBaseClass(metadata));

        return new DefaultRepositoryInformation(metadata, baseClass, composition);
    });
}

这里的 getRepositoryBaseClass 获取一个 baseClass,实际返回一个 SimpleJpaRepository.class,这个 baseClass 在后面作为被代理对象使用。

在 getTargetRepositoryViaReflection 方法中,根据这个 baseClass,通过反射创建对象

protected final <R> R getTargetRepositoryViaReflection(RepositoryInformation information,
      Object... constructorArguments) {
	// 获取到 baseClass,即为 SimpleJpaRepository
   Class<?> baseClass = information.getRepositoryBaseClass();
   return getTargetRepositoryViaReflection(baseClass, constructorArguments);
}

	protected final <R> R getTargetRepositoryViaReflection(Class<?> baseClass, Object... constructorArguments) {
		Optional<Constructor<?>> constructor = ReflectionUtils.findConstructor(baseClass, constructorArguments);
		// 通过反射创建对象对象
		return constructor.map(it -> (R) BeanUtils.instantiateClass(it, constructorArguments)).orElseThrow(() -> new IllegalStateException(String.format(
						"No suitable constructor found on %s to match the given arguments: %s. Make sure you implement a constructor taking these",
						baseClass, Arrays.stream(constructorArguments).map(Object::getClass).collect(Collectors.toList()))));
	}

然后将这个对象作为 target 放到 result 中,result 又添加了一些 advisor 和 advice,这些在查询时被构建成链接器链

// 获取到一个 SimpleJpaRepository 实例
Object target = getTargetRepository(information);

// Create proxy
ProxyFactory result = new ProxyFactory();
// 作为目标对象
result.setTarget(target);
result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);

if (MethodInvocationValidator.supports(repositoryInterface)) {
   result.addAdvice(new MethodInvocationValidator());
}
// 添加 advisor
result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);

postProcessors.forEach(processor -> processor.postProcess(result, information));

if (DefaultMethodInvokingMethodInterceptor.hasDefaultMethods(repositoryInterface)) {
   result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
}

// 添加 advice
ProjectionFactory projectionFactory = getProjectionFactory(classLoader, beanFactory);
result.addAdvice(new QueryExecutorMethodInterceptor(information, projectionFactory));

composition = composition.append(RepositoryFragment.implemented(target));
result.addAdvice(new ImplementationMethodExecutionInterceptor(composition));
// 获取代理对象
T repository = (T) result.getProxy(classLoader);

image-20200726123554363

最终生成的代理对象即为如下所示

image-20200726123414685

5.3 Jpa 查询流程是怎样的?

这里以 UserServiceImpl#findUserByName 说一下 jpa 的查询流程

image-20200726135828933

在 UserServiceImpl 调用了 UserRepository,UserRepository 是一个代理对象,它被 JdkDynamicAopProxy 所代理,所以执行 UserRepository 中方法时,会调用 JdkDynamicAopProxy 中 invoke 方法。

@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;

   TargetSource targetSource = this.advised.targetSource;
   Object target = null;

   try {
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
         // The target does not implement the equals(Object) method itself.
         return equals(args[0]);
      }
      else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
         // The target does not implement the hashCode() method itself.
         return hashCode();
      }
      else if (method.getDeclaringClass() == DecoratingProxy.class) {
         // There is only getDecoratedClass() declared -> dispatch to proxy config.
         return AopProxyUtils.ultimateTargetClass(this.advised);
      }
      else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
         // Service invocations on ProxyConfig with the proxy config...
         return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
      }

      Object retVal;

      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }

      // Get as late as possible to minimize the time we "own" the target,
      // in case it comes from a pool.
       // 获取目标对象
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);

      // Get the interception chain for this method.
       // 构建拦截链
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      // Check whether we have any advice. If we don't, we can fallback on direct
      // reflective invocation of the target, and avoid creating a MethodInvocation.
      if (chain.isEmpty()) {
         // We can skip creating a MethodInvocation: just invoke the target directly
         // Note that the final invoker must be an InvokerInterceptor so we know it does
         // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         // We need to create a method invocation...
         MethodInvocation invocation =
               new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         // Proceed to the joinpoint through the interceptor chain.
         retVal = invocation.proceed();
      }

      // Massage return value if necessary.
      Class<?> returnType = method.getReturnType();
      if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
         // Special case: it returned "this" and the return type of the method
         // is type-compatible. Note that we can't help if the target sets
         // a reference to itself in another returned object.
         retVal = proxy;
      }
      else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
         throw new AopInvocationException(
               "Null return value from advice does not match primitive return type for: " + method);
      }
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         // Must have come from TargetSource.
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

在 JdkDynamicAopProxy 中会通过 getInterceptorsAndDynamicInterceptionAdvice 获取到一条链,实际上它是一个拦截器链,它由一下几个部分组成:

  • ExposeInvocationInterceptor: 将当前的invocation设置到上下文中
  • CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor: 判断是自定义方法还是jpa中方法,如果是自定义方法直接执行下一个拦截器;否则绑定资源再执行下一个拦截器
  • PersistenceExceptionTranslationInterceptor: 捕获RuntimeException,出现异常之后拦截器才生效
  • TransactionInterceptor: 给后面要执行的拦截器添加后置事务处理
  • DefaultMethodInvokingMethodInterceptor: 判断是否 defaultMethod,如果不是走下一个拦截器;否则使用MethodHandle执行
  • RepositoryFactorySupport$QueryExecutorMethodInterceptor: 执行自定义查询
  • RepositoryFactorySupport$ImplementationMethodExecutionInterceptor:拦截 RepositoryComposition
  • PersistenceExceptionTranslationInterceptor:异常处理拦截器

最终在 QueryExecutorMethodInterceptor 中调用 doInvoke 方法执行自定义查询

@Nullable
private Object doInvoke(MethodInvocation invocation) throws Throwable {

    Method method = invocation.getMethod();
	
    if (hasQueryFor(method)) {
        // 执行查询
        return queries.get(method).execute(invocation.getArguments());
    }
	// 继续执行下一个拦截器
    return invocation.proceed();
}

在 execute 中,通过调用 AbstractJpaQuery#execute -> AbstractJpaQuery#doExecute -> JpaQueryExecution#execute -> JpaQueryExecution.SingleEntityExecution#doExecute -> AbstractProducedQuery#getSingleResult -> AbstractProducedQuery#list -> AbstractProducedQuery#doList -> org.hibernate.internal.SessionImpl#list,使用 hibernate 完成查询。

6.参考

  1. 官方 spring-data-jpa
  2. 官方文档-Spring Boot Features/JPA
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值