时间真快,一晃已经2021半年又过去了,年前初制定的每周一篇文章没有实现。~~~
因工作项目开发需要,最近研究了下jpa的功能。主要为了实现两个目标需求:
- 实现可以类似mybatis,可以使用配置文件编写复杂的sql管理。
- 拦截执行sql执行,可以动态添加公共字段。如插入时,可以统一插入公共时间、创建人等公共属性。
- 扩展JpaRepository,实现自定义业务公共方法。
- 原生repository,每个实体都要创建repository 对象,太过于繁琐。期望通过通过提供 commonRepository 统一对实体操作。
JPA引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
JPA启动过程–扩展
jpa加载是基于springboot stater 默认自动加载。(stater原理这边不讨论)。
可以通过@EnableJpaRepositories 开启设置jpa的一些属性配置常用的有:
- 设置扫描 repository接口包路径
- 设置repositoryFactoryBeanClass,用于自定义的Beanfactory。这个beanfactory是RepositoryFactoryBeanSupport实现类。
@EnableJpaRepositories(repositoryFactoryBeanClass=BaseDaoFactoryBean.class,basePackages= {"com.**.dao"})
@EnableTransactionManagement
启动加载类关系图
jpa在项目启动的时候会扫描相应包中的带有@repository注解接口在初始化后,调用 factoryBean afterPropertiesSet() 进行jpa相关初始化:
- 调用 createRepositoryFactory(),创建 RepositoryFactorySupport 实例,也是factory。 [这里是个切入点]。
- 调用factory中的getRepository()初始化:
2.1 通过调用getTargetRepository() 生成目标接口动态代理对象。[这里是个切入点]
2.2 添加响应执行拦截器。一个是执行默认方法的DefaultMethodInvokingMethodInterceptor,一个QueryExecutorMethodInterceptor拦截器 - 调用QueryExecutorMethodInterceptor 的构造方法,执行决策器queryLookupStrategy resolveQuery() 获取RepositoryQuery,来执行SQL。jpa默认queryLookupStrategy的实现类是CreateIfNotFoundQueryLookupStrategy。 queryLookupStrategy 。[这里是个切入点,可以自定义拓展出去其他自定义执行SQL规范]
调用链:
org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet()
org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.createRepositoryFactory()
org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(Class<T>, RepositoryFragments) // 获取代理目标对象
org.springframework.data.repository.core.support.RepositoryFactorySupport.getTargetRepository(RepositoryInformation)
org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.QueryExecutorMethodInterceptor(RepositoryInformation, ProjectionFactory, Optional<QueryLookupStrategy>, NamedQueries, List<QueryCreationListener<?>>)
org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy.DeclaredQueryLookupStrategy.resolveQuery(JpaQueryMethod, EntityManager, NamedQueries) --在这里校验,并决策出RepositoryQuery 对象
如果Repository 过多一般会启动很慢,解决方案
/**
* 减少JPA 影响启动时间
*/
@Component
public class LazyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Value("${spring.jpa.rpLazyInit:false}")
private boolean rpLazyInit;
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if(StringUtils.contains(beanName,"Repository") && rpLazyInit){
//pvs.getPropertyValue("lazyInit").getValue()
Map<String,Object> map = new HashMap<>(pvs.getPropertyValues().length);
pvs.forEach(p->{
String name = p.getName();
Object value = p.getValue();
if(StringUtils.equals("lazyInit",name)){
value = true;
}
map.put(name,value);
});
PropertyValues pvsUse = new MutablePropertyValues(map);
return pvsUse;
}
return pvs;
}
}