SpringData入門

SpringData概述

  • Spring Data : Spring 的一個子項目。用於簡化數據庫訪問,支持NoSQL 和 關係數據存儲。其主要目標是使數據庫的訪問變得方便快捷。
  • SpringData 項目所支持 NoSQL 存儲:
    • MongoDB (文檔數據庫)
    • Neo4j(圖形數據庫)
    • Redis(鍵/值存儲)
    • Hbase(列族數據庫)
  • SpringData 項目所支持的關係數據存儲技術:
    • JDBC
    • JPA

JPA Spring Data : 致力於減少數據訪問層 (DAO) 的開發量. ==開發者唯一要做的,就只是聲明持久層的接口,其他都交給 Spring Data JPA 來幫你完成!==

框架怎麼可能代替開發者實現業務邏輯呢?

Spring Data JPA 做的便是==規範方法==的名字,根據符合規範的名字來確定方法需要實現什麼樣的邏輯。

比如:當有一個 UserDao.findUserById() 這樣一個方法聲明,大致應該能判斷出這是根據給定條件的 ID 查詢出滿足條件的 User 對象

SpringData JPA進行持久化開發的4個步驟

  • Spring整合JPA
  • Spring配置SpringData

    • 讓Spring爲聲明的接口創建代理對象
  • 聲明持久層的接口(繼承Repository)

    • Repository 是一個標記型接口,它不包含任何方法,如必要,Spring Data 可實現 Repository 其他子接口,其中定義了一些常用的增刪改查,以及分頁相關的方法。
  • 在接口中聲明方法

    • Spring Data 將根據給定的策略(具體策略稍後講解)來爲其生成實現代碼。

框架搭建

導入jar包

image
image

配置配置文件

Spring整合JPA(完整版)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!-- 配置自動掃描的包 -->
    <context:component-scan base-package="com.xiaoming.springdata"></context:component-scan>

    <!-- 1. 配置數據源 -->
    <context:property-placeholder location="classpath:db.properties"/>

    <bean id="dataSource"
        class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property> 
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>

        <!-- 配置其他屬性 -->
    </bean>

    <!-- 2. 配置 JPA 的 EntityManagerFactory -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!--配置數據源-->
        <property name="dataSource" ref="dataSource"></property>
        <!-- 配置 JPA 提供商的適配器. 可以通過內部 bean 的方式來配置 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
        </property>
        <!-- 配置實體類所在的包 -->
        <property name="packagesToScan" value="com.xiaoming.springdata"></property>
        <!-- 配置 JPA 的基本屬性. 例如 JPA 實現產品的屬性 -->
        <property name="jpaProperties">
            <props>
                <!-- 二級緩存相關 -->
                <!--  
                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
                <prop key="net.sf.ehcache.configurationResourceName">ehcache-hibernate.xml</prop>
                -->
                <!-- 生成的數據表的列的映射策略 -->
                <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
                <!-- hibernate 基本屬性 -->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <!-- 3.配置JPA使用的事務管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>    
    </bean>

    <!-- 4. 配置支持註解的事務 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!--上面的是Spring整合JPA-->

    <!-- 5. 配置 SpringData -->
    <!-- 加入  jpa 的命名空間 -->
    <!--
        base-package: 掃描 Repository Bean 所在的 package
            爲掃描的包中繼承Repository或其子接口的接口創建代理對象,並將代理對象註冊爲SpringBean
    -->
    <jpa:repositories base-package="com.xiaoming.springdata" entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>

</beans>

在Spring配置文件中配置SpringData

    <!-- 5. 配置 SpringData -->
    <!-- 加入  jpa 的命名空間 -->
    <!--
        base-package: 掃描 Repository Bean 所在的 package
            爲掃描的包中繼承Repository或其子接口的接口創建代理對象,並將代理對象註冊爲SpringBean
    -->
    <jpa:repositories base-package="com.xiaoming.springdata" entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>

SpringData的約束

xmlns:jpa="http://www.springframework.org/schema/data/jpa"


http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd

Person類

@Table(name = "person")
@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;
    private String username;
    private String password;
    private Integer age;

    .......
}    

PersonRepository類

public interface PersonRepository extends Repository<Person,Long> {

    //根據用戶名獲取person對象
    Person getByUsername(String username);

}

代碼測試(每完成一步測試一下)

package com.xiaoming.springdata.test;

import com.xiaoming.springdata.domain.Person;
import com.xiaoming.springdata.repsotory.PersonRepsotory;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.sql.DataSource;

/**
 * 測試SpringData項目是否搭建成功
 */
public class InitTest {

    private ApplicationContext applicationContext = null;


    {
        applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

    }


    @Test
    public void testHelloWorldSpringData() throws Exception{

        PersonRepsotory personRepsotory = applicationContext.getBean(PersonRepsotory.class);
        System.out.println(personRepsotory.getClass().getName());

        Person person = personRepsotory.getByUsername("張三");
        System.out.println(person);
    }


    @Test
    public void testJpa(){

    }

    /**
     * 測試數據庫連接池是否整合成功
     */
    @Test
    public void testDataSource() throws Exception{
        DataSource dataSource = applicationContext.getBean(DataSource.class);
        System.out.println(dataSource.getConnection());
    }
}

Repository接口詳解

Repository接口的概述

  • Repository 接口是 Spring Data 的一個核心接口,它不提供任何方法,開發者需要在自己定義的接口中聲明需要的方法
 public interface Repository<T, ID extends Serializable> {}
  • Spring Data可以讓我們只定義接口,只要遵循 Spring Data的規範,就無需寫實現類。

  • 與繼承 Repository 等價的一種方式,就是在持久層接口上使用 @RepositoryDefinition 註解,併爲其指定 domainClass 和 idClass 屬性。如下兩種方式是完全等價的

//@RepositoryDefinition(domainClass=Person.class,idClass=Integer.class)
public interface PersonRepository extends Repository<Person,Long> {
    ......
}

Repository的子接口

基礎的 Repository 提供了最基本的數據訪問功能,其幾個子接口則擴展了一些功能。它們的繼承關係如下

  • Repository: 僅僅是一個標識,表明任何繼承它的均爲倉庫接口類
  • CrudRepository: 繼承 Repository,實現了一組 CRUD 相關的方法
  • PagingAndSortingRepository: 繼承 - CrudRepository,實現了一組分頁排序相關的方法
  • JpaRepository: 繼承 PagingAndSortingRepository,實現一組 JPA 規範相關的方法
  • 自定義的 XxxxRepository 需要繼承 JpaRepository,這樣的 XxxxRepository 接口就具備了通用的數據訪問控制層的能力。
  • JpaSpecificationExecutor: 不屬於Repository體系,實現一組 JPA Criteria 查詢相關的方法

SpringData方法定義規範

簡單條件查詢(查詢某一個實體類或者集合)

  • 簡單條件查詢: 查詢某一個實體類或者集合
  • ==按照 Spring Data 的規範,查詢方法以 find | read | get 開頭,涉及條件查詢時,條件的屬性用條件關鍵字連接,要注意的是:條件屬性以首字母大寫==
  • 例如:定義一個 Entity 實體類
class User{ 
    private String firstName;   
    private String lastName;
    ......

使用And條件連接時,應這樣寫:==findByLastNameAndFirstName(String lastName,String firstName)==;

==條件的屬性名稱與個數要與參數的位置與個數一一對應==

SpringData支持的關鍵字

==直接在接口中定義查詢方法,如果是符合規範的,可以不用寫實現==

image

image

測試代碼

PersonRepository

public interface PersonRepository extends Repository<Person,Long> {

    //根據用戶名獲取person對象
    Person getByUsername(String username);

    //WHERE username LIKE ?% AND id < ?
    //這個方法like後面沒有 自動添加 % 傳的參數中需要手動添加
    List<Person> getByUsernameLikeAndAgeGreaterThan(String username,Integer age);

    //是否包含該字符串(jpql語句不會寫emmm)
    List<Person> getByUsernameContaining(String username);

    //WHERE  username IN (?, ?, ?) OR age < ?
    List<Person> getByUsernameInOrAgeLessThanEqual(List<String> name,Integer age);

}

RepositoryTest(測試一部分)

public class SpringDataRepositoryTest {

    private ApplicationContext applicationContext = null;
    private PersonRepository personRepsotory = null;

    {
        applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        personRepsotory= applicationContext.getBean(PersonRepository.class);

    }

    @Test
    public void testKeyWords(){
        //Like關鍵字後面只添加 ?  沒有添加 % 手動添加
        List<Person> personList = personRepsotory.getByUsernameLikeAndAgeGreaterThan("%a%", 45);
        System.out.println(personList);

    }

    @Test
    public void testKeyWords2(){
        List<Person> personList = personRepsotory.getByUsernameContaining("a");
        System.out.println(personList.size());
        System.out.println(personList);
    }


    @Test
    public void testKeyWords3(){
        List<Person> personList = personRepsotory.getByUsernameInOrAgeLessThanEqual(Arrays.asList("aa","bb","cc"),47);
        System.out.println(personList.size());
        System.out.println(personList);
    }

}

簡單條件查詢的弊端

  • 需要滿足規範方法名太長
  • 靈活性較差

簡單條件查詢進行級聯查詢時出現的問題
- 簡單條件查詢是是支持級聯查詢的,默認使用左外連接
- 但是如果要查詢的級聯的屬性的名稱跟本類中的屬性名稱重複,那麼優先使用本類的屬性
- 如果想要使用級聯屬性那麼屬性之間應該用_進行連接

List<Person> getByAddress_IdGreaterThan(Integer id);

使用Query註解

這種查詢可以聲明在 Repository 方法中,==擺脫像命名查詢那樣的約束,將查詢直接在相應的接口方法中聲明==,結構更爲清晰,這是 Spring data 的特有實現

//查詢 id 值最大的那個 Person
//使用 @Query 註解可以自定義 JPQL 語句以實現更靈活的查詢
@Query("SELECT p FROM Person p WHERE p.id = (SELECT max(p2.id) FROM Person p2)")
Person getMaxIdPerson();

動態參數賦值問題

索引參數

@Query("SELECT p FROM Person p WHERE p.lastName = ?1 AND p.email = ?2")
List<Person> testQueryAnnotationParams1(String lastName, String email);
//SpringData 允許在佔位符上添加 %%. 
@Query("SELECT p FROM Person p WHERE p.lastName LIKE %?1% OR p.email LIKE %?2%")
List<Person> testQueryAnnotationLikeParam(String lastName, String email);

命名參數(推薦)

@Query("SELECT p FROM Person p WHERE p.lastName = :lastName AND p.email = :email")
List<Person> testQueryAnnotationParams2(@Param("email") String email, @Param("lastName") String lastName);

測試代碼

  • PersonRepository
    //查詢 id 值最大的那個 Person
    //使用 @Query 註解可以自定義 JPQL 語句以實現更靈活的查詢
    @Query("SELECT p FROM Person p WHERE p.id = (SELECT max(p2.id) FROM Person p2)")
    Person getMaxIdPerson();

    //爲 @Query 註解傳遞參數的方式1: 使用佔位符. 
    @Query("SELECT p FROM Person p WHERE p.lastName = ?1 AND p.email = ?2")
    List<Person> testQueryAnnotationParams1(String lastName, String email);

    //爲 @Query 註解傳遞參數的方式1: 命名參數的方式. 
    @Query("SELECT p FROM Person p WHERE p.lastName = :lastName AND p.email = :email")
    List<Person> testQueryAnnotationParams2(@Param("email") String email, @Param("lastName") String lastName);

    //SpringData 允許在佔位符上添加 %%. 
    @Query("SELECT p FROM Person p WHERE p.lastName LIKE %?1% OR p.email LIKE %?2%")
    List<Person> testQueryAnnotationLikeParam(String lastName, String email);

    //SpringData 允許在佔位符上添加 %%. 
    @Query("SELECT p FROM Person p WHERE p.lastName LIKE %:lastName% OR p.email LIKE %:email%")
    List<Person> testQueryAnnotationLikeParam2(@Param("email") String email, @Param("lastName") String lastName);

    //設置 nativeQuery=true 即可以使用原生的 SQL 查詢
    @Query(value="SELECT count(id) FROM jpa_persons", nativeQuery=true)
    long getTotalCount();
  • SpringDataRepositoryTest
    @Test
    public void testQueryAnnotationParams1(){
        List<Person> persons = personRepsotory.testQueryAnnotationParams1("AA", "[email protected]");
        System.out.println(persons);
    }

        @Test
    public void testQueryAnnotationParams2(){
        List<Person> persons = personRepsotory.testQueryAnnotationParams2("[email protected]", "AA");
        System.out.println(persons);
    }


        @Test
    public void testQueryAnnotationLikeParam(){
//      List<Person> persons = personRepsotory.testQueryAnnotationLikeParam("%A%", "%bb%");
//      System.out.println(persons.size());

//      List<Person> persons = personRepsotory.testQueryAnnotationLikeParam("A", "bb");
//      System.out.println(persons.size());

        List<Person> persons = personRepsotory.testQueryAnnotationLikeParam2("bb", "A");
        System.out.println(persons.size());
    }

        @Test
    public void testNativeQuery(){
        long count = personRepsotory.getTotalCount();
        System.out.println(count);
    }

@Query 與 @Modifying 執行更新操作

因爲Repository接口是頂層的接口是一個空的接口,作爲標記,我們前面講到的全是查詢的方法,因爲hql不支持insert所以我們只能用hql進行更新操作

想要執行更新操作我們有兩種方法,第一種是繼承Repository的子接口,裏面聲明瞭增刪改的方法,還有一種就是在Repository接口的基礎上使用@Query跟@Modifying註解來執行操作

//可以通過自定義的 JPQL 完成 UPDATE 和 DELETE 操作. 注意: JPQL 不支持使用 INSERT
//在 @Query 註解中編寫 JPQL 語句, 但必須使用 @Modifying 進行修飾. 以通知 SpringData, 這是一個 UPDATE 或 DELETE 操作
//UPDATE 或 DELETE 操作需要使用事務, 此時需要定義 Service 層. 在 Service 層的方法上添加事務操作. 
//默認情況下, SpringData 的每個方法上有事務, 但都是一個只讀事務. 他們不能完成修改操作!
@Modifying
@Query("UPDATE Person p SET p.email = :email WHERE id = :id")
void updatePersonEmail(@Param("id") Integer id, @Param("email") String email);

事物

Spring Data 提供了默認的事務處理方式,即所有的查詢均聲明爲只讀事務。

對於自定義的方法,如需改變 Spring Data 提供的事務默認方式,可以在方法上註解 @Transactional 聲明

進行多個 Repository 操作時,也應該使它們在同一個事務中處理,按照分層架構的思想,這部分屬於業務邏輯層,因此,需要在 Service 層實現對多個 Repository 的調用,並在相應的方法上聲明事務。

CrudRepository接口

CrudRepository 接口提供了最基本的對實體類的添刪改查操作
- T save(T entity);//保存單個實體
- Iterable save(Iterable

    @Test
    public void testCrudRepository(){
        List<Person> persons = new ArrayList<>();

        Person p1 = new Person();
        p1.setUsername("王五");
        p1.setPassword("123");
        p1.setAge(18);

        Person p2 = new Person();
        p1.setUsername("趙六");
        p1.setPassword("123");
        p1.setAge(18);
        persons.add(p1 );
        persons.add(p2 );
//        personService.savePersons(persons);

        personRepsotory.save(persons);

PagingAndSortingRepository接口

該接口提供了分頁與排序功能
- Iterable findAll(Sort sort); //排序
- Page findAll(Pageable pageable); //分頁查詢(含排序功能)

  • PagingAndSortingRepository接口中的方法
    @Test
    public void testPagingAndSortingRespository(){
        //pageNo 從 0 開始. 
        int pageNo = 6 - 1;
        int pageSize = 5;
        //Pageable 接口通常使用的其 PageRequest 實現類. 其中封裝了需要分頁的信息
        //排序相關的. Sort 封裝了排序的信息
        //Order 是具體針對於某一個屬性進行升序還是降序. 
        Order order1 = new Order(Direction.DESC, "id");
        Order order2 = new Order(Direction.ASC, "email");
        Sort sort = new Sort(order1, order2);

        PageRequest pageable = new PageRequest(pageNo, pageSize, sort);
        Page<Person> page = personRepsotory.findAll(pageable);

        System.out.println("總記錄數: " + page.getTotalElements());
        System.out.println("當前第幾頁: " + (page.getNumber() + 1));
        System.out.println("總頁數: " + page.getTotalPages());
        System.out.println("當前頁面的 List: " + page.getContent());
        System.out.println("當前頁面的記錄數: " + page.getNumberOfElements());
    }
  • Repository中簡單查詢
//根據用戶名獲取person對象
List<Person> getByUsernameContaining(String username, Pageable pageable);
    @Test
    public void testPagingAndSortingRepository(){

        //SpringData實現分頁要傳入Pageable接口類型的參數-->實現類PageRequest
        //PageRequest 需要參數 pageNumber,pageSize Sort(排序的看需求)
        int pageNumber = 0; //當前頁 第3頁 從0開始
        int pageSize = 9; //每頁記錄數5


        //不排序的
//        PageRequest pageRequest = new PageRequest(pageNumber,pageSize);

        //排序
        Sort.Order order = new Sort.Order(Sort.Direction.DESC,"age");
        Sort sort = new Sort(order);
        PageRequest pageRequest = new PageRequest(pageNumber,pageSize,sort);

        List<Person> persons = personRepsotory.getByUsernameContaining("a", pageRequest);
        System.out.println(persons.size());
        System.out.println(persons);

    }

JpaRepository接口

該接口提供了JPA的相關功能
- List findAll(); //查找所有實體
- List findAll(Sort sort); //排序、查找所有實體
- List save(Iterable

    @Test
    public void testJpaRepository(){
        Person person = new Person();
        person.setBirth(new Date());
        person.setEmail("[email protected]");
        person.setLastName("xyz");
        person.setId(28);

        Person person2 = personRepsotory.saveAndFlush(person);

        System.out.println(person == person2);
    }

JpaSpecificationExecutor接口

不屬於Repository體系,實現一組 JPA Criteria 查詢相關的方法

image

Specification:封裝 JPA Criteria 查詢條件。通常使用匿名內部類的方式來創建該接口的對象
前面講到的Repository體系中分頁查詢,添加條件不方便,這個外來的接口極大地方便了我們帶條件的分頁查詢

我們可以寫一個通用的帶條件的分頁查詢

    /**
     * 目標: 實現帶查詢條件的分頁. age <45  並按照年齡排序 的條件
     * <p>
     * 調用 JpaSpecificationExecutor 的 Page<T> findAll(Specification<T> spec, Pageable pageable);
     * Specification: 封裝了 JPA Criteria 查詢的查詢條件
     * Pageable: 封裝了請求分頁的信息: 例如 pageNo, pageSize, Sort
     */

    @Test
    public void testJpaSpecificationExecutor() {
        //findAll(Specification<T> spec, Pageable pageable);
        //1.先解決分頁排序問題
        Sort.Order order = new Sort.Order(Sort.Direction.DESC, "age");
        Sort sort = new Sort(order);
        PageRequest pageRequest = new PageRequest(0, 20, sort);
        //2.Specification<T>

        //通常使用 Specification 的匿名內部類創建對象
        Specification<Person> specification = new Specification<Person>() {
            /**
             *
             * @param root(掌握):代表查詢的實體類(這裏指的是Person)
             * @param criteriaQuery(瞭解):可以從中能得到實體類;可以添加查詢條件;
             *                              還可以結合 EntityManager 對象得到最終查詢的 TypedQuery 對象.
             * @param criteriaBuilder(掌握):創建Criteria相關對象的工廠,可以從中獲得目標對象Predicate
             * @return Predicate(掌握): 代表一個查詢條件_
             */

            @Override
            public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //先通過root導航,導航到相關屬性(條件)
                Path<Integer> age = root.get("age");
                //將導航到的屬性添加到條件中
                Predicate predicate = criteriaBuilder.lt(age, 45);
                return predicate;
            }
        };

        Page<Person> page = personRepsotory.findAll(specification, pageRequest);

        System.out.println("總記錄數: " + page.getTotalElements());
        System.out.println("當前第幾頁: " + (page.getNumber() + 1));
        System.out.println("總頁數: " + page.getTotalPages());
        System.out.println("當前頁面的 List: " + page.getContent());
        System.out.println("當前頁面的記錄數: " + page.getNumberOfElements());

    }

root導航圖
image

自定義 Repository 方法

我們通過繼承PagingAndSortingRepository接口和繼承JpaSpecificationExecutor能夠實現大部分的增刪改查以及分頁查詢(帶條件)

當不滿足我們的時候我們還可以通過@Modifying跟@Query註解來手寫JPQL語句(insert不行)

當還不滿足我們的時候我們可以自定義Repository方法

  • ==爲某一個 Repository 上添加自定義方法(重點)==
  • 爲所有的 Repository 都添加自實現的方法

步驟(某個Repository)
- 定義一個接口: 聲明要添加的, 並自實現的方法
- 提供該接口的實現類: 類名需在要聲明的 Repository 後添加 Impl, 並實現方法
- 聲明 Repository 接口, 並繼承 1) 聲明的接口
- 使用.
- 注意: 默認情況下, Spring Data 會在 base-package 中查找 “接口名Impl” 作爲實現類. 也可以通過 repository-impl-postfix 聲明後綴.

image

爲某一個 Repository 上添加自定義方法

自己寫一個接口PersonDao

public interface PersonDao {

    void test();

}

自己寫PersonDao的實現PersonRepsotoryImpl(固定的)

public class PersonRepsotoryImpl implements PersonDao {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public void test() {
        Person person = entityManager.find(Person.class, 11);
        System.out.println("-->" + person);
    }

}

PersonReposity繼承我們寫的PersonDao

public interface PersonRepsotory extends 
    JpaRepository<Person, Integer>,
    JpaSpecificationExecutor<Person>, PersonDao{
        ......
    }

測試


//調用的是我們自己實現的test方法
@Test
public void testCustomRepositoryMethod(){
    personRepsotory.test();
}
發佈了25 篇原創文章 · 獲贊 5 · 訪問量 6654
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章