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());
}
}