spring-data-jpa初步學習.

這篇博客是我自己學習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中.還有很不清楚的地方.畢竟能力有限還需要慢慢的去探索..

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章