1. 程式人生 > >SpringData JPA 詳解(自定義查詢、分頁、事務控制)

SpringData JPA 詳解(自定義查詢、分頁、事務控制)

簡介

SpringData JPA是 JPA的一種實現,極大的簡化了JPA的開發,原始JPA的開發,需要建立實體管理工廠,使用實體管理器定義各種查詢進行CRUD操作,而SpringData JPA只需要通過核心介面Repository和它的子類就能很方便的操作資料庫。

Repository

1. Repository:最頂層的介面,一個空的介面,統一所有的Repository型別,並且能夠讓元件掃描的時候能夠自動識別

2. CrudRepository: Repository的子介面,提供CRUD的操作

3. PagingAndSortingRepository: CrudRepository的子介面,添加了分頁和排序的功能

4. JpaRepository: 是PagingAndSortingRepository的子介面,增加一些實用的功能,比如批量操作

5. JpaSpecificationExecutor:來定義複雜查詢

使用SpringData JPA

將SpringDataJPA整合Spring,將EntityManagerFactory的建立交給Spring容器,只需要在xml中配置JPA的Repository的介面位置,就可以很方便的獲取到Repository的bean,使用時候直接在service層注入定義的repository.

配置檔案:

<?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="cn.bing"/>

    <!-- 配置資料庫資原始檔的位置-->
    <context:property-placeholder location="classpath:db.properties"/>

    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!-- jdbc.properties 中的key必須定義為 jdbc.username,格式開頭的 -->
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
    </bean>

    <!-- 2. 配置 JPA 的 EntityManagerFactory -->
    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置jpa的介面卡 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
        </property>
        <!-- 配置實體所在的包 -->
        <property name="packagesToScan" value="cn.bing.pojo"/>
        <!-- 配置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. 配置事務管理器 -->
    <bean id="transactionManager"
          class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

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

    <!-- 5. 配置 SpringData -->
    <!-- 加入  jpa 的名稱空間 -->
    <!-- base-package: 掃描 Repository Bean 所在的 package -->
    <jpa:repositories base-package="cn.bing.dao"
                      entity-manager-factory-ref="entityManagerFactory"/>

</beans>

1 . 引入Jar

2. 建立實體

@Entity表示這個類是一個實體類,參與JPA和資料庫表的對映

@Table表示具體對映的表名

@Id表示這個欄位是主鍵,@GeneratedValue表示這個主鍵的生成策略

@Column表示對映到資料庫的表的欄位名,欄位名和表字段不一致,修改註解@Column的name屬性

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    @Column
    private Integer age;
    @Column
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

3. 編寫介面繼承Repository的子類,並且需要在spring的配置檔案配置

 <!-- 5. 配置 SpringData -->
    <!-- 加入  jpa 的名稱空間 -->
    <!-- base-package: 掃描 Repository Bean 所在的 package -->
    <jpa:repositories base-package="cn.bing.dao" 
                      entity-manager-factory-ref="entityManagerFactory"/>

一般都是選擇繼承JpaRepository方便直接呼叫現有的方法進行CRUD,繼承JpaSpecificationExecutor則是方便定義一些複雜的查詢。

JpaRespository<T,D> T 表示實體,D表示實體的主鍵ID型別

public interface UserRepository extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
   
}

 自定義介面繼承父類的Repository後,自動獲取的操作資料的API,只能進行簡單的操作,需要自定義查詢。

repository.findOne(1);//根據id查詢
repository.findAll();//查詢
repository.delete(new User());//delete操作
repository.saveAndFlush(new User());//insert操作
   

關於SpringData JPA查詢的定義

1. spring data 對於定義方法的查詢策略

查詢策略是spring data 根據方法名稱取解析使用者的查詢意圖,第一種,根據方法的命名規則解析,第二種是通過Query去解析,如果兩種同時存在時,springdata按照那種解析方法名,這就是spring data的查詢策略,查詢策略可以在<jpa:repositorys/> 屬性query-lookup-strategy 配置

CREATE: 通過解析方法的名稱來建立查詢,也就是下面的規則1

USE_DECLARED_QUERY:根據定義好的語句去查詢,如果找不到,丟擲異常資訊。查詢語句定義在某個註解或者方法上。

CREATE_IF_NOT_FOUND:優先查詢方法上是否有定義好的查詢語句,如果沒有,則按照方法名稱解析,這是預設的策略。

public interface UserRepository extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
    //根據springData的關鍵字命名方法名稱,可以不用寫實現類
    List<User> findByNameAndAge(String name, Integer age);

    List<User> findByNameLikeAndAge(String name, Integer age);
    //@Query註解裡面寫JPQL語句,定義查詢
    @Query(nativeQuery = false,value = " SELECT p FROM User p WHERE id = ?1")
    User readId(Integer id);
    //Query註解也可以定義SQL語句,只要將nativeQuery屬性改為true
    @Query(nativeQuery = true, value = "select name from user where id = :id")
    String findNamebyId(@Param("id")Integer id);
    //@Modifying修飾方法,告知SpringData,這是一個UPATE或者DELETE操作
    //在service層的方法上新增事務操作
    @Modifying
    @Query(nativeQuery = true,value = "update user set name = ?1  where id = ?2 ")
    int updateUserNameById(String name,Integer id);

}

規則1:根據SpringData JPA的關鍵字定義方法名,方法的引數順序和關鍵字的順序一致,名稱可以不一致。

Spring Data JPA對方法名稱進行解析的時候,會將一些無用的字首名自動去除,比如find、findBy、read、readBy,然後根據關鍵字解析成對應JPQL語句。也要注意方法的引數順序和定義的關鍵字的順序一致。

 規則2:定義方法時候,上面加上註解@Query,預設nativeQuery是false,此時value填入的是JPQL語句,修改nativeQuery是true,就能夠寫入SQL語句

//@Query註解裡面寫JPQL語句,定義查詢
    @Query(nativeQuery = false,value = " SELECT p FROM User p WHERE id = ?1")
    User readId(Integer id);
    //Query註解也可以定義SQL語句,只要將nativeQuery屬性改為true
    @Query(nativeQuery = true, value = "select name from user where id = :id")
    String findNamebyId(@Param("id")Integer id);

注意引數注入的方式有兩種:

1. ?下標,下標從1開始

2.  :xxx ...:yyy,xxx和yyy必須是實體的屬性名,並且方法引數上加上對應的註解@Param("xxx")和@Param('yyy')

@Modify和事務

可以通過JPQL語句定義update/delete,此時在@Query註解中定義,必須加上@Modify,告訴spring data 這是一個update/delete操作。

update/delete操作需要事務支援,必須在service層,新增事務,因為spring data,預設情況下每個方法是隻讀事務,不能完成update/delete操作。

public interface UserRepository extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
    //根據springData的關鍵字命名方法名稱,可以不用寫實現類
    List<User> findByNameAndAge(String name, Integer age);
    List<User> findByNameLikeAndAge(String name, Integer age);
    //@Query註解裡面寫JPQL語句,定義查詢
    @Query(nativeQuery = false,value = " SELECT p FROM User p WHERE id = ?1")
    User readId(Integer id);
    //Query註解也可以定義SQL語句,只要將nativeQuery屬性改為true
    @Query(nativeQuery = true, value = "select name from user where id = :id")
    String findNamebyId(@Param("id")Integer id);
    //@Modifying修飾方法,告知SpringData,這是一個UPATE或者DELETE操作
    //在service層的方法上新增事務操作
    @Modifying
    @Query(nativeQuery = true,value = "update user set name = ?1  where id = ?2 ")
    int updateUserNameById(String name,Integer id);
}
@Service
public class UserServiceImpl implements  UserService {
    @Autowired
    private UserRepository repository;
    //對於SpringData jpa的update或者delete操作,必須在service層使用事務,直接使用倉庫的方法會報錯
    //另外,SpringData jpa 的 JPQL語法不支援insert
    @Transactional(propagation = Propagation.REQUIRED)
    public int updateUserNameById(String name,Integer id){
        return repository.updateUserNameById(name,id);
    }

}
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class SpringDataTest {
    @Autowired
    private UserRepository repository;
    @Autowired
    private UserService service;

    @Test
    public void test(){
        service.updateUserNameById("張三",1);
    }
}
        

分頁和模糊查詢

 模糊查詢

需要注意的是%%外面不需要再加上''

//模糊查詢
    @Query(nativeQuery = true,value = " select  * from user where name like %?1% ")
    User findUserByLikeName(String name);

分頁物件

1. 要求自定義的Repository必須繼承了PagingAndSortingRepository或者他的子類JpaRepository

2. 分頁物件是Pageable介面的實現類PageRequest

public class PageRequest implements Pageable, Serializable {
    private static final long serialVersionUID = 8280485938848398236L;
    private final int page;//頁碼,從0開始,0表示第一頁,1表示第二頁,以此類推
    private final int size;//每頁顯示的記錄數
    private final Sort sort;//排序規則

 Sort物件,定義排序規則,常用的是下面這種建構函式,支援可變引數的

public Sort(Sort.Direction direction, String... properties) {
        this(direction, (List)(properties == null ? new ArrayList() : Arrays.asList(properties)));
    }

 定義分頁查詢,只需要將查詢的引數和分頁物件作為引數。

 Page<User> findByNameLike(String str , Pageable pageable);
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class SpringDataTest {
    @Autowired
    private UserRepository repository;
    @Test
    public void test(){
       /**
        * SpringData jpa 的分頁
        * Pageable介面的實現類是PageRequest,Page介面的實現類是PageImpl。
        */
       Pageable page = new PageRequest(0,2,new Sort(Sort.Direction.DESC,"id"));
       Page<User> personList =  repository.findByNameLike("張%",page);
        System.out.println("總記錄數" + personList.getTotalElements());
        System.out.println("當前第幾頁: " + (personList.getNumber() + 1));
        System.out.println("總頁數: " + personList.getTotalPages());
        System.out.println("當前頁面的記錄數:" + personList.getContent());
        System.out.println("當前頁面的記錄數: " + personList.getNumberOfElements());
    }
}