这篇博客是我自己学习spring-data-jpa时的一些理解,可能有错误.还希望看到的大牛能指正.
本博客深度不够,只是简单的一些自我的理解,还望轻喷.
为什么突然对spring-data-jpa(下面简称JPA)感兴趣,要说到一次我自己写的一个针对hbase数据库的增删查改的工具类.里面使用的是泛型的方式(博客链接),因为对hbase中操作表,很多方法都是可以复用的,当时第一时间想到的就是泛型,但现在回想一下,其他(hibernate,JPA等)是如何来实现的呢?因为在使用的工程中,自定义的持久层都是继承的HibernateDaoSupport或JpaRepository就能实现数据的持久层了(JPA还会有一些通用的方法),所以当时就萌生了一个念头,想去看看他们是如何实现的.
先看了hibernate的实现,其实我自己写的工具类也借鉴了一下hibernate,先定义一个实现类,继承HibernateDaoSupport,实现自定义方法.查看上面的博客链接里面就可以看出来,
自定义daoImpl层, 实现自定义接口dao
class daoImpl<T> implements dao<T> {
private Class<T> type;
@SuppressWarnings("unchecked")
public Test2() {
//getClass().getGenericSuperclass() 得到 当前对象的直接超类的 Type
ParameterizedType parameterizedType = (ParameterizedType) getClass()
.getGenericSuperclass();
this.type = (Class<T>) parameterizedType.getActualTypeArguments()[0];
System.out.println(getClass().getGenericSuperclass());
}
}
在实现类中保存泛型的实际类型(type),以便在方法中能使用.然后接口中添加通用的方法.以后需要用到时,就在根据具体的实体对象来实现各自的dao层
class myDao extends daoImpl<Entity> {
}
想这样,myDao中就会有daoImpl中的具体方法了(通用方法)
hibernate的研究没有太过深入,比如如何从数据库中获取数据后组装成具体对象这块就没有去看,毕竟当时只关注了如何让一个实现类通用这个问题上了.
再来看看JPA,JPA非常强大,现在才使用了它的一点点功能就能看出来里面的强大.可以使用@Query来写JPQL语句查询,也可以写原生的sql查询,还能通过方法名来查询(findByName()这样直接写一个方法,就可以通过实体对象中name属性来查找)是不是很牛逼.而我这次兴趣点在于为什么我们一个实体对象的Respository继承JpaRepository,JpaSpecificationExecutor这两个类后,就能使用很多方法了,如findOne(),save(Entity entity)等,甚至我们都没有对自己的Respository接口写实现类就能做到基本的增删改查等操作,这些东西是谁帮我们做了呢?接下来,就是探索JPA的旅程了.
在使用JPA时,我们只需要这样既可
@Repository("myRepository")
public interface myRepository
extends
JpaRepository<myEntity, String>,
JpaSpecificationExecutor<myEntity> {
@Query
public void function();
}
然后就可以使用myRepository来进行一系列数据操作了,为什么我们都没有实现myRepository接口,但我们还是可以调用find,save等方法呢?这个就要看JPA源码了.
我们发现JpaRepository类有一个实现类SimpleJpaRepository,而SimpleJpaRepository就是我们上面能用到的find,save,delete等方法的实现.那我们的自定义myRepository,是如何调用到SimpleJpaRepository里面的实现方法的呢?
其实查看JPA源码能发现org.springframework.data.jpa.repository.support.JpaRepositoryFactory是JPARepository的创建工厂类,spring在加载时,其实都是通过JpaRepositoryFactory下的
@Override
protected Object getTargetRepository(RepositoryInformation information) {
SimpleJpaRepository<?, ?> repository = getTargetRepository(information, entityManager);
repository.setRepositoryMethodMetadata(crudMethodMetadataPostProcessor.getCrudMethodMetadata());
return repository;
}
来创建Repository的.我们虽然自己的myRepository继承了JpaRepository,但在spring加载时,并不是加载的myRepository,这里是动态加载了一个SimpleJpaRepository在spring容器中,而我们使用@Autowired注解注入myRepository使用时,其实就是在使用SimpleJpaRepository来操作数据.而这里我有一个疑问就是,SimpleJpaRepository只提供了通用的方法的实现,那其他我们自定义在myRepository中的Query方法是如何操作的呢?接着往下看.
我在跟踪项目启动的时候发现,org.springframework.data.repository.core.support.RepositoryFactorySupport下面有一个QueryExecutorMethodInterceptor类,其中一个方法是
public QueryExecutorMethodInterceptor(RepositoryInformation repositoryInformation, Object customImplementation,
Object target) {
Assert.notNull(repositoryInformation, "RepositoryInformation must not be null!");
Assert.notNull(target, "Target must not be null!");
this.resultHandler = new QueryExecutionResultHandler();
this.repositoryInformation = repositoryInformation;
this.customImplementation = customImplementation;
this.target = target;
QueryLookupStrategy lookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey,
RepositoryFactorySupport.this.evaluationContextProvider);
lookupStrategy = lookupStrategy == null ? getQueryLookupStrategy(queryLookupStrategyKey) : lookupStrategy;
Iterable<Method> queryMethods = repositoryInformation.getQueryMethods();
if (lookupStrategy == null) {
if (queryMethods.iterator().hasNext()) {
throw new IllegalStateException("You have defined query method in the repository but "
+ "you don't have any query lookup strategy defined. The "
+ "infrastructure apparently does not support query methods!");
}
return;
}
SpelAwareProxyProjectionFactory factory = new SpelAwareProxyProjectionFactory();
factory.setBeanClassLoader(classLoader);
factory.setBeanFactory(beanFactory);
for (Method method : queryMethods) {
RepositoryQuery query = lookupStrategy.resolveQuery(method, repositoryInformation, factory, namedQueries);
invokeListeners(query);
queries.put(method, query);
}
}
这里的Iterable<Method> queryMethods = repositoryInformation.getQueryMethods();就是获取的我们自定义在myRepository中的方法,然后将方法存入一个库里面.在执行时,拦截自定义方法,从这里执行自定义方法.
注释是:
/**
* Creates a new {@link QueryExecutorMethodInterceptor}. Builds a model of {@link QueryMethod}s to be invoked on
* execution of repository interface methods.
*/
翻译: 创建一个新的{@link QueryExecutorMethodInterceptor}。 构建要调用的{@link QueryMethod}模型
执行存储库接口方法。
到此就是我初步的一些理解了...还有很多不明白的地方..比如RepositoryFactoryBeanSupport,RepositoryFactorySupport,JpaRepositoryFactory的关系.方法时如何在执行时被调用的.既然是生成的动态代理类,那自定义方法是否和通用方法一起封装返回的SimpleJpaRepository中.还有很不清楚的地方.毕竟能力有限还需要慢慢的去探索..