原标題:Spring認證|Spring Data JPA 參考文檔六(内容來源:Spring中國教育管理中心)
5.1.4. 存儲過程
JPA 2.1 規範引入了對使用 JPA 條件查詢 API 調用存儲過程的支援。我們引入了@Procedure用于在存儲庫方法上聲明存儲過程中繼資料的注釋。
以下示例使用以下存儲過程:
Example 91. plus1inoutHSQL DB 中過程的定義。
/;
DROP procedure IF EXISTS plus1inout
CREATE procedure plus1inout (IN arg int, OUT res int)
BEGIN ATOMIC
set res = arg + 1;
END
可以通過使用NamedStoredProcedureQuery實體類型上的注釋來配置存儲過程的中繼資料。
示例 92.實體上的 StoredProcedure 中繼資料定義。
@Entity
@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class),
@StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) })
public class User {}
請注意,@NamedStoredProcedureQuery存儲過程有兩個不同的名稱。 name是 JPA 使用的名稱。procedureName是存儲過程在資料庫中的名稱。
您可以通過多種方式從存儲庫方法中引用存儲過程。要調用的存儲過程可以直接使用注解的value或procedureName屬性定義@Procedure。這直接引用資料庫中的存儲過程,并忽略通過@
NamedStoredProcedureQuery.
或者,您可以将@
NamedStoredProcedureQuery.name屬性指定為@Procedure.name屬性。如果沒有value,procedureName也沒有name被配置,存儲庫方法的名稱被用作name屬性。
以下示例顯示了如何引用顯式映射的過程:
示例 93. 引用資料庫中名稱為“plus1inout”的顯式映射過程。
@Procedure("plus1inout")
Integer explicitlyNamedPlus1inout(Integer arg);
以下示例與前一個示例等效,但使用procedureName别名:
示例 94. 通過procedureName别名在資料庫中引用名為“plus1inout”的隐式映射過程。
@Procedure(procedureName = "plus1inout")
Integer callPlus1InOut(Integer arg);
以下再次等效于前兩個,但使用方法名稱而不是顯式注釋屬性。
示例 95.EntityManager使用方法名稱引用隐式映射的命名存儲過程“User.plus1” 。
@Procedure
Integer plus1inout(@Param("arg") Integer arg);
以下示例顯示如何通過引用@
NamedStoredProcedureQuery.name屬性來引用存儲過程。
示例 96.在EntityManager.
@Procedure(name = "User.plus1IO")
Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);
如果被調用的存儲過程有一個單出參數,則該參數可以作為方法的傳回值傳回。如果在@NamedStoredProcedureQuery注釋中指定了多個輸出參數,則這些參數可以作為 a 傳回,Map鍵是@NamedStoredProcedureQuery注釋中給出的參數名稱。
5.1.5. 規格
JPA 2 引入了一個标準 API,您可以使用它以程式設計方式建構查詢。通過編寫criteria,您可以定義域類查詢的 where 子句。再退一步,這些标準可以被視為對 JPA 标準 API 限制所描述的實體的謂詞。
Spring Data JPA 從 Eric Evans 的書“Domain Driven Design”中采用了規範的概念,遵循相同的語義并提供 API 以使用 JPA 标準 API 定義此類規範。為了支援規範,您可以使用該JpaSpecificationExecutor接口擴充您的存儲庫接口,如下所示:
public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
…
}
附加接口的方法可以讓您以多種方式運作規範。例如,該findAll方法傳回與規範比對的所有實體,如以下示例所示:
List<T> findAll(Specification<T> spec);
的Specification接口被定義為如下:
public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder builder);
規範可以很容易地用于在實體之上建構一組可擴充的謂詞,然後可以組合和使用這些謂詞,JpaRepository而無需為每個需要的組合聲明查詢(方法),如以下示例所示:
示例 97. 客戶規格
public class CustomerSpecs {
public static Specification<Customer> isLongTermCustomer() {
return (root, query, builder) -> {
LocalDate date = LocalDate.now().minusYears(2);
return builder.lessThan(root.get(Customer_.createdAt), date);
};
public static Specification<Customer> hasSalesOfMoreThan(MonetaryAmount value) {
// build query here
該Customer_類型是使用 JPA 元模型生成器生成的元模型類型(有關示例,請參閱Hibernate 實作的文檔)。是以表達式 ,Customer_.createdAt假定Customer具有createdAt類型的屬性Date。除此之外,我們在業務需求抽象級别上表達了一些标準并建立了可執行檔案Specifications。是以用戶端可能會使用 aSpecification如下:
示例 98. 使用簡單的規範
List<Customer> customers = customerRepository.findAll(isLongTermCustomer());
為什麼不為這種資料通路建立查詢?Specification與普通的查詢聲明相比,使用單個并沒有太大的好處。當您将規範組合起來建立新Specification對象時,規範的力量會真正發揮作用。您可以通過Specification我們提供的預設方法來實作這一點,以建構類似于以下内容的表達式:
示例 99. 組合規格
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
isLongTermCustomer().or(hasSalesOfMoreThan(amount)));
Specification提供了一些“膠水代碼”預設方法來連結群組合Specification執行個體。這些方法讓您可以通過建立新的Specification實作并将它們與現有的實作相結合來擴充資料通路層。
5.1.6. 按示例查詢
介紹
本章介紹了 Query by Example 并解釋了如何使用它。
示例查詢 (QBE) 是一種使用者友好的查詢技術,具有簡單的界面。它允許動态建立查詢,并且不需要您編寫包含字段名稱的查詢。事實上,Query by Example 根本不需要您使用特定于商店的查詢語言編寫查詢。
用法
Query by Example API 由三部分組成:
探針:具有填充字段的域對象的實際示例。
ExampleMatcher:ExampleMatcher包含有關如何比對特定字段的詳細資訊。它可以在多個示例中重複使用。
Example: AnExample由探針和ExampleMatcher. 它用于建立查詢。
Query by Example 非常适合以下幾個用例:
使用一組靜态或動态限制查詢您的資料存儲。
頻繁重構域對象而不必擔心破壞現有查詢。
獨立于底層資料存儲 API 工作。
Query by Example 也有幾個限制:
不支援嵌套或分組的屬性限制,例如firstname = ?0 or (firstname = ?1 and lastname = ?2).
僅支援字元串的開始/包含/結束/正規表達式比對以及其他屬性類型的精确比對。
在開始使用 Query by Example 之前,您需要有一個域對象。首先,為您的存儲庫建立一個接口,如以下示例所示:
示例 100. 示例 Person 對象
public class Person {
@Id
private String id;
private String firstname;
private String lastname;
private Address address;
// … getters and setters omitted
前面的示例顯示了一個簡單的域對象。您可以使用它來建立Example. 預設情況下,null忽略具有值的字段,并使用商店特定的預設值比對字元串。
将屬性包含在 Query by Example 标準中是基于可空性。除非忽略屬性路徑,否則始終包含使用原始類型 ( int, double, ...)的屬性。
可以使用of工廠方法或使用ExampleMatcher. Example是不可變的。以下清單顯示了一個簡單的示例:
示例 101. 簡單示例
Person person = new Person();
person.setFirstname("Dave");
Example<Person> example = Example.of(person);
建立域對象的新執行個體。
設定要查詢的屬性。
建立Example.
您可以使用存儲庫運作示例查詢。為此,讓您的存儲庫接口擴充QueryByExampleExecutor<T>. 以下清單顯示了QueryByExampleExecutor界面的摘錄:
例 102. QueryByExampleExecutor
public interface QueryByExampleExecutor<T> {
<S extends T> S findOne(Example<S> example);
<S extends T> Iterable<S> findAll(Example<S> example);
// … more functionality omitted.
示例比對器
示例不限于預設設定。您可以使用 為字元串比對、空值處理和特定于屬性的設定指定自己的預設值ExampleMatcher,如以下示例所示:
示例 103. 具有自定義比對的示例比對器
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("lastname")
.withIncludeNullValues()
.withStringMatcher(StringMatcher.ENDING);
Example<Person> example = Example.of(person, matcher);
設定屬性。
建立一個ExampleMatcher以期望所有值比對。即使沒有進一步的配置,它也可以在這個階段使用。
構造一個新ExampleMatcher的忽略lastname屬性路徑。
構造一個 newExampleMatcher以忽略lastname屬性路徑并包含空值。
構造一個 newExampleMatcher來忽略lastname屬性路徑,包含空值,并執行字尾字元串比對。
建立一個新的Example基于域對象和配置上ExampleMatcher。
預設情況下,ExampleMatcher期望在探測器上設定的所有值都比對。如果要獲得與任何隐式定義的謂詞比對的結果,請使用
ExampleMatcher.matchingAny().
您可以為單個屬性指定行為(例如“名字”和“姓氏”,或者對于嵌套屬性,“address.city”)。您可以使用比對選項和區分大小寫來調整它,如以下示例所示:
示例 104. 配置比對器選項
.withMatcher("firstname", endsWith())
.withMatcher("lastname", startsWith().ignoreCase());
另一種配置比對器選項的方法是使用 lambdas(在 Java 8 中引入)。這種方法建立了一個回調,要求實作者修改比對器。您不需要傳回比對器,因為配置選項儲存在比對器執行個體中。以下示例顯示了使用 lambda 的比對器:
示例 105. 使用 lambda 配置比對器選項
.withMatcher("firstname", match -> match.endsWith())
.withMatcher("firstname", match -> match.startsWith());
Example使用配置的合并視圖建立的查詢。預設比對設定可以在ExampleMatcher級别設定,而單獨的設定可以應用于特定的屬性路徑。已設定上的設定ExampleMatcher由屬性路徑設定繼承,除非它們被明确定義。屬性更新檔上的設定比預設設定具有更高的優先級。下表描述了各種ExampleMatcher設定的範圍:
運作示例
在 Spring Data JPA 中,您可以将 Query by Example 與 Repositories 一起使用,如下例所示:
示例 106. 使用存儲庫按示例查詢
public interface PersonRepository extends JpaRepository<Person, String> { … }
public class PersonService {
@Autowired PersonRepository personRepository;
public List<Person> findPeople(Person probe) {
return personRepository.findAll(Example.of(probe));
目前,隻有SingularAttribute屬性可以用于屬性比對。
屬性說明符接受屬性名稱(例如firstname和lastname)。您可以通過将屬性與點 ( address.city)連結在一起進行導航。您還可以使用比對選項和區分大小寫來調整它。
下表顯示了StringMatcher您可以使用的各種選項以及在名為 的字段上使用它們的結果firstname:
5.1.7. 交易性
預設情況下,從存儲庫執行個體繼承的 CRUD 方法SimpleJpaRepository是事務性的。對于讀取操作,事務配置readOnly标志設定為true。所有其他人都使用普通配置,@Transactional以便應用預設事務配置。由事務存儲庫片段支援的存儲庫方法從實際片段方法繼承事務屬性。
如果您需要為存儲庫中聲明的方法之一調整事務配置,請在存儲庫接口中重新聲明該方法,如下所示:
示例 107. CRUD 的自定義事務配置
public interface UserRepository extends CrudRepository<User, Long> {
@Override
@Transactional(timeout = 10)
public List<User> findAll();
// Further query method declarations
這樣做會導緻findAll()方法以 10 秒的逾時時間運作并且沒有readOnly标志。
改變事務行為的另一種方法是使用(通常)覆寫多個存儲庫的外觀或服務實作。其目的是為非 CRUD 操作定義事務邊界。以下示例展示了如何将這樣的外觀用于多個存儲庫:
示例 108. 使用 Facade 為多個存儲庫調用定義事務
@Service
public class UserManagementImpl implements UserManagement {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
public UserManagementImpl(UserRepository userRepository,
RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
@Transactional
public void addRoleToAllUsers(String roleName) {
Role role = roleRepository.findByName(roleName);
for (User user : userRepository.findAll()) {
user.addRole(role);
userRepository.save(user);
此示例導緻調用addRoleToAllUsers(…)在事務内運作(參與現有事務或建立新事務,如果尚未運作)。然後忽略存儲庫中的事務配置,因為外部事務配置決定了實際使用的事務配置。請注意,您必須顯式激活<tx:annotation-driven />或使用@
EnableTransactionManagement才能使外觀的基于注釋的配置工作。此示例假定您使用元件掃描。
請注意,save從 JPA 的角度來看,調用 to并不是絕對必要的,但仍應存在以與 Spring Data 提供的存儲庫抽象保持一緻。
事務查詢方法
要讓您的查詢方法具有事務性,請@Transactional在您定義的存儲庫接口處使用,如以下示例所示:
示例 109.在查詢方法中使用 @Transactional
@Transactional(readOnly = true)
interface UserRepository extends JpaRepository<User, Long> {
List<User> findByLastname(String lastname);
@Modifying
@Query("delete from User u where u.active = false")
void deleteInactiveUsers();
通常,您希望将該readOnly标志設定為true,因為大多數查詢方法隻讀取資料。與此相反,deleteInactiveUsers()使用@Modifying注釋并覆寫事務配置。是以,該方法在readOnly标志設定為 的情況下運作false。
您可以将事務用于隻讀查詢,并通過設定readOnly标志來标記它們。但是,這樣做并不能檢查您是否不會觸發操縱查詢(盡管某些資料庫拒絕INSERT和UPDATE隻讀事務中的語句)。該readOnly标志會作為對底層 JDBC 驅動程式的提示進行傳播,以進行性能優化。此外,Spring 對底層 JPA 提供程式執行了一些優化。例如,當與 Hibernate 一起使用時,重新整理模式NEVER在您将事務配置為時設定為readOnly,這會導緻 Hibernate 跳過髒檢查(對大對象樹的顯着改進)。
内容來源:Spring中國教育管理中心(Spring認證)
2021年2月,VMware公司正式與北京中科卓望網絡科技有限公司(以下簡稱:中科卓望)達成戰略合作,授予其 Spring 中國教育管理中心,攜手 VMware 全球最新 Spring技術和認證體系,幫助中國院校建構專業教學内容,全面賦能未來開發人。