zoukankan      html  css  js  c++  java
  • 10.SpringBoot学习(十)——JDBC之 Spring Boot Jpa

    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
  • 相关阅读:
    软工实践个人总结
    第03组 每周小结 (3/3)
    第03组 每周小结 (2/3)
    第03组 每周小结(1/3)
    第03组 Beta冲刺 总结
    第03组 Beta冲刺 (5/5)
    第03组 Beta冲刺 (4/5)
    第03组 Beta冲刺 (3/5)
    第03组 Beta冲刺 (2/5)
    第03组 Beta冲刺 (1/5)
  • 原文地址:https://www.cnblogs.com/col-smile/p/13382604.html
Copyright © 2011-2022 走看看