spring-data-jpa 中文文檔(2)
JPA Repositories
簡介
Spring命名空間
SpringData使用了自定義的命名空間去定義repository。通常我們會使用repositories元素:<?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: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.xsd"> <jpa:repositories base-package="com.acme.repositories" /> </beans>
這個配置中啓用了持久化異常處理,所有標誌了
@Repository
的Bean將會被轉換成爲Spring的DataAccessException
。自定義命名空間屬性
除了repositories,JPA命名空間還提供了其他的屬性去控制:屬性名 屬性值 entity-manager-factory-ref 默認的話,是使用 ApplicationContext
中找到的EntityManagerFactory
,如果有多個的時候,則需要特別指明這個屬性,他將會對repositories路徑中找到的類進行處理transaction-manager-ref 默認使用系統定義的 PlatformTransactionManager
,如果有多個事務管理器的話,則需特別指定。
基於註解的配置
SpringData JPA支持JavaConfig方式的配置:@Configuration @EnableJpaRepositories @EnableTransactionManagement class ApplicationConfig { @Bean public DataSource dataSource() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); return builder.setType(EmbeddedDatabaseType.HSQL).build(); } @Bean public EntityManagerFactory entityManagerFactory() { HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setGenerateDdl(true); LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); factory.setJpaVendorAdapter(vendorAdapter); factory.setPackagesToScan("com.acme.domain"); factory.setDataSource(dataSource()); factory.afterPropertiesSet(); return factory.getObject(); } @Bean public PlatformTransactionManager transactionManager() { JpaTransactionManager txManager = new JpaTransactionManager(); txManager.setEntityManagerFactory(entityManagerFactory()); return txManager; } }
上面的配置中,我們設置了一個內嵌的HSQL數據庫,我們也配置了
EntityManagerFactory
,並且使用Hibernate作爲持久層。最後也定義了JPATransactionManager
。最上面我們還使用了@EnableJpaRepositories
註解。
持久化實體
保存實體
保存實體,我們之前使用了CrudRepository.save(…)方法。他會使用相關的JPA EntityManager來調用persist或者merge,如果數據沒存在於數據庫中,則調用entityManager.persist(..),否
則調用entityManager.merge(…)。實體狀態監測策略
SpringData JPA提供三種策略去監測實體是否存在:屬性名 屬性值 Id-Property inspection (default) 默認的會通過ID來監測是否新數據,如果ID屬性是空的,則認爲是新數據,反則認爲舊數據 Implementing Persistable 如果實體實現了Persistable接口,那麼就會通過isNew的方法來監測。 Implementing EntityInformation 這個是很少用的
查詢方法
查詢策略
你可以寫一個語句或者從方法名中查詢。- 聲明查詢方法
雖然說方法名查詢的方式很方便,可是你可能會遇到方法名查詢規則不支持你所要查詢的關鍵字或者方法名寫的很長,不方便,或者很醜陋。那麼你就需要通過命名查詢或者在方法上使用
@Query
來解決。 查詢創建
通常我們可以使用方法名來解析查詢語句,例如:public interface UserRepository extends Repository<User, Long> { List<User> findByEmailAddressAndLastname(String emailAddress, String lastname); }
其所支持的在方法名中可以使用的關鍵字:
關鍵字 例子 JPQL片段 And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2 Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2 Is,Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1 Between findByStartDateBetween … where x.startDate between ?1 and ?2 LessThan findByAgeLessThan … where x.age < ?1 LessThanEqual findByAgeLessThanEqual … where x.age ⇐ ?1 GreaterThan findByAgeGreaterThan … where x.age > ?1 GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1 After findByStartDateAfter … where x.startDate > ?1 Before findByStartDateBefore … where x.startDate < ?1 IsNull findByAgeIsNull … where x.age is null IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null Like findByFirstnameLike … where x.firstname like ?1 NotLike findByFirstnameNotLike … where x.firstname not like ?1 StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %) EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %) Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %) OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc Not findByLastnameNot … where x.lastname <> ?1 In findByAgeIn(Collection ages) … where x.age in ?1 NotIn findByAgeNotIn(Collection age) … where x.age not in ?1 True findByActiveTrue() … where x.active = true False findByActiveFalse() … where x.active = false IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1) In
和NotIn
也可以用Collection
的子類.
- 聲明查詢方法
使用JPA命名查詢
註解方式
@Entity @NamedQuery(name = "User.findByEmailAddress", query = "select u from User u where u.emailAddress = ?1") public class User { }
XML方式 略.
聲明接口
要使用上面的命名查詢,我們的接口需要這麼聲明public interface UserRepository extends JpaRepository<User, Long> { List<User> findByLastname(String lastname); User findByEmailAddress(String emailAddress); }
SpringData會先從域類中查詢配置,根據”.(原點)“區分方法名,而不會使用自動方法名解析的方式去創建查詢。
使用
@Query
命名查詢適合用於小數量的查詢,我們可以使用@Query來替代:public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.emailAddress = ?1") User findByEmailAddress(String emailAddress); }
在表達式中使用Like查詢,例子如下:
public interface UserRepository extends JpaRepository<User, Long> { + @Query("select u from User u where u.firstname like %?1") List<User> findByFirstnameEndsWith(String firstname); }
這個例子中,我們使用了%,當然,你的參數就沒必要加入這個符號了。
使用原生sql查詢
我們可以在@Query
中使用本地查詢,當然,你需要設置nativeQuery=true
,必須說明的是,這樣的話,就不再支持分頁以及排序。public interface UserRepository extends JpaRepository<User, Long> { @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true) User findByEmailAddress(String emailAddress); }
使用命名參數
使用命名查詢,我們需要用到@Param來註釋到指定的參數上,如下:public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname") User findByLastnameOrFirstname(@Param("lastname") String lastname, @Param("firstname") String firstname); }
使用SpEL表達式
在Spring Data JPA 1.4以後,我們支持在@Query
中使用SpEL表達式
(簡介)來接收變量。
SpEL支持的變量:變量名 使用方式 描述 entityName select x from #{#entityName} x 根據給定的Repository自動插入相關的entityName。有兩種方式能被解析出來:如果域類型定義了@Entity屬性名稱。或者直接使用類名稱。 以下的例子中,我們在查詢語句中插入表達式(你也可以用@Entity(name = “MyUser”)。
@Entity public class User { @Id @GeneratedValue Long id; String lastname; } public interface UserRepository extends JpaRepository<User,Long> { @Query("select u from #{#entityName} u where u.lastname = ?1") List<User> findByLastname(String lastname); }
如果你想寫一個通用的Repository接口,那麼你也可以用這個表達式來處理:
@MappedSuperclass public abstract class AbstractMappedType { … String attribute } @Entity public class ConcreteType extends AbstractMappedType { … } @NoRepositoryBean public interface MappedTypeRepository<T extends AbstractMappedType> extends Repository<T, Long> { @Query("select t from #{#entityName} t where t.attribute = ?1") List<T> findAllByAttribute(String attribute); } public interface ConcreteRepository extends MappedTypeRepository<ConcreteType> { … }
修改語句
之前我們演示瞭如何去聲明查詢語句,當然我們還有修改語句。修改語句的實現,我們只需要在加多一個註解@Modifying
:@Modifying @Query("update User u set u.firstname = ?1 where u.lastname = ?2") int setFixedFirstnameFor(String firstname, String lastname);
這樣一來,我們就使用了update操作來代替select操作。當我們發起update操作後,可能會有一些過期的數據產生,我們不需要自動去清除它們,因爲EntityManager會有效的丟掉那些未提
交的變更,如果你想EntityManager自動清除,那麼你可以在@Modify上添加clearAutomatically屬性(true);使用
QueryHints
(查詢提示 QueryHits簡介)public interface UserRepository extends Repository<User, Long> { @QueryHints(value = { @QueryHint(name = "name", value = "value")}, forCounting = false) Page<User> findByLastname(String lastname, Pageable pageable); }
配置獲取和負載圖(loadgraph)
@NamedEntityGraph
簡介
JPA2.1 支持 通過@EntityGraph
及其子類型@NamedEntityGraph
來定義獲取和負載.它們可以被直接在實體類上,用來配置查詢結果的獲取計劃.獲取的方式(獲取/負載)可以通過@EntityGraph
的type
屬性來進行配置.
在一個實體類上定義named entity graph
@Entity @NamedEntityGraph(name = "GroupInfo.detail", attributeNodes = @NamedAttributeNode("members")) public class GroupInfo { // default fetch mode is lazy. @ManyToMany List<GroupMember> members = new ArrayList<GroupMember>(); … }
在repository接口中引用在實體類上定義的
named entity graph
@Repository public interface GroupRepository extends CrudRepository<GroupInfo, String> { @EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD) GroupInfo getByGroupName(String name);
它也可以通過
@EntityGraph
註解來直接點對點的指定entity graphs
.假如依照EntityGraph
attributePaths
可以被正確的找到,就可以不用在實體類上寫@NamedEntityGraph
註解了:@Repository public interface GroupRepository extends CrudRepository<GroupInfo, String> { @EntityGraph(attributePaths = { "members" }) GroupInfo getByGroupName(String name); }
投影(Projections)
通常情況下 Spring Data Repositories 會返回整個domain 類.有時候,你需要因爲不同的原因,修改domain類的返回結果.
看下面的例子:@Entity public class Person { @Id @GeneratedValue private Long id; private String firstName, lastName; @OneToOne private Address address; … } @Entity public class Address { @Id @GeneratedValue private Long id; private String street, state, country; … }
Person
的幾個屬性:id
是主鍵fristName
和lastName
是數據屬性.address
鏈接到其他的domain類型.
現在假設我們創建了一個像下邊這樣的repository:
interface PersonRepository extends CrudRepository<Person, Long> { Person findPersonByFirstName(String firstName); }
Spring Data將會返回domain類的所有屬性.現在有兩種選擇去僅僅返回
address
屬性:- 爲
Address
定義一個repository:
interface AddressRepository extends CrudRepository<Address, Long> {}
在這種情況下 用
PersonRepository
將會返回整個Person
對象.使用AddressRepository
僅僅會返回Address
對象.
但是,如果你真的不想暴露address的信息怎麼辦?你可以提供一個像下邊這樣的repository,僅僅提供你想暴露的屬性:interface NoAddresses { String getFirstName(); String getLastName(); }
其中
interface NoAddresses
定義一個接口String getFirstName();
導出firstName
String getLastName();
導出lastName
這個
NoAddress
只有firstName
和lastName
的getter方法.它意味着它將不會提供任何的address信息.在定義查詢方法的時候,應該用NoAddress
代替Person
interface PersonRepository extends CrudRepository<Person, Long> { NoAddresses findByFirstName(String firstName); }
改變屬性的值
現在你已經知道了怎麼樣對結果進行投影,已達到不暴露不必要的部分給用戶.投影也可以調整要暴露的數據模型.你可以添加一個虛擬的屬性:interface RenamedProperty { String getFirstName(); @Value("#{target.lastName}") String getName(); }
其中:
interface RenamedProperty
定義一個接口.String getFirstName();
導出firstName
屬性.@Value("#{target.lastName}") String getName();
導出name
屬性,由於此屬性是虛擬的,因此他需要用`@Value("#{target.lastName}")
來指定數據的來源.
如果你想獲得一個人的全稱.你可能要用
String.format("%s %s", person.getFirstName(), person.getLastName())
來拼接.用虛擬的屬性可以這樣來實現:interface FullNameAndCountry { @Value("#{target.firstName} #{target.lastName}") String getFullName(); @Value("#{target.address.country}") String getCountry(); }
實際上
@Value
可以完全訪問對象及其內嵌的屬性.SpEL
表達式對於施加在投影方法上的定義來說也是非常強大的:
想想你有一個如下的domain模型:@Entity public class User { @Id @GeneratedValue private Long id; private String name; private String password; … }
在某些情況下,你想讓密碼在可能的情況下不讓其明文出現.這種情況下你可以用
@Value
和SpEL表達式
來創建一個投影:interface PasswordProjection { @Value("#{(target.password == null || target.password.empty) ? null : '******'}") String getPassword(); }
這個表達式判斷當password是null或者empty的時候返回null,否則返回’******’
存儲過程
JPA2.1 規格介紹了JPA標準查詢API對存儲過程調用的支持.下面介紹下@Procedure
註解的使用.DROP procedure IF EXISTS plus1inout CREATE procedure plus1inout (IN arg int, OUT res int) BEGIN ATOMIC set res = arg + 1; END
存儲過程的元數據可以在實體類上通過@NamedStoredProcedureQuery進行配置.
@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 {}
在repository 方法上可以通過多種方式引用存儲過程.存儲過程可以直接通過
@Procedure
註解的value
或者procedureName
屬性調用或者通過name
屬性.如果repository方法沒有名字,則將其作爲後備.@Procedure("plus1inout") Integer explicitlyNamedPlus1inout(Integer arg); @Procedure(procedureName = "plus1inout") Integer plus1inout(Integer arg); @Procedure(name = "User.plus1IO") Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg); @Procedure Integer plus1(@Param("arg") Integer arg);
JPA2 引入了criteria API 去建立查詢,Spring Data JPA使用Specifications來實現這個API。在Repository中,你需要繼承JpaSpecificationExecutor:
public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor { … }
下面先給個例子,演示如何利用findAll方法返回所有符合條件的對象:
List<T> findAll(Specification<T> spec);
Specification 接口定義如下:
public interface Specification<T> { Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder); }
好了,那麼我們如何去實現這個接口呢?代碼如下:
public class CustomerSpecs { public static Specification<Customer> isLongTermCustomer() { return new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder builder) { LocalDate date = new LocalDate().minusYears(2); return builder.lessThan(root.get(_Customer.createdAt), date); } }; } public static Specification<Customer> hasSalesOfMoreThan(MontaryAmount value) { return new Specification<Customer>() { public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) { // build query here } }; } }
好了,那如果有多個需要結合的話,我們可以這麼做:
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR); List<Customer> customers = customerRepository.findAll( where(isLongTermCustomer()).or(hasSalesOfMoreThan(amount)));
Query by Example
- 介紹
這一部分介紹Query by Example和解釋怎麼去使用Examples. 使用
Query by Example由三部分組成.Probe
這是填充字段的domain對象的實際範例.ExampleMatcher
ExampleMatcher
描述怎麼去匹配特定字段的細節.他可以通過多個Examples進行重複利用.Example
它由ExampleMatcher
和Probe
組成.用來創建一個查詢.
Query by Example使用與多種情況,但是它也有一些限制.
適用於:- 用靜態或者靜態的約束來查詢數據
- domain對象的頻繁重構,而不用擔心破壞現有的查詢
- 根據底層數據存儲API進行獨立工作
限制: - 用
AND
關鍵字進行條件查詢的時候. - 不支持內嵌的/組合的屬性約束,像
firstname = ?0 or (firstname = ?1 and lastname = ?2)
僅僅支持starts/contains/ends/regex匹配strings或者精確匹配其他屬性類型.
在使用Query by Example之前,你需要有一個domain對象:public class Person { @Id private String id; private String firstname; private String lastname; private Address address; // … getters and setters omitted }
這是一個簡單的domain對象.你可以使用它去創建一個
Example
默認情況下,字段爲null
值時會被忽略.
Examples
通過使用of
工廠方法或者ExampleMatcher
來創建.Example
是不可改變的.Person person = new Person(); person.setFirstname("Dave"); Example<Person> example = Example.of(person);
其中:
Person person = new Person();
創建一個domain對象實例person.setFirstname("Dave");
設置firstName屬性去查詢Example<Person> example = Example.of(person);
創建Example
Examples用repositories來執行是非常理想的.要使用它需要讓你的repository接口繼承QueryByExampleExecutor<T>
.
public interface QueryByExampleExecutor<T> { <S extends T> S findOne(Example<S> example); <S extends T> Iterable<S> findAll(Example<S> example); // … more functionality omitted. }
Example matchers
Example不僅僅侷限於默認的設置.你可以給strings定義自己的默認值然後去匹配.使用ExampleMatcher
綁定null和特定屬性的設置.Person person = new Person(); person.setFirstname("Dave"); ExampleMatcher matcher = ExampleMatcher.matching() .withIgnorePaths("lastname") .withIncludeNullValues() .withStringMatcherEnding(); Example<Person> example = Example.of(person, matcher);
其中:
Person person = new Person();
創建一個domain對象實例.- 設置屬性值去查詢.
ExampleMatcher matcher = ExampleMatcher.matching()
創建一個ExampleMatcher
讓其可以使用,但沒有多餘的配置項..withIgnorePaths("lastname")
Construct a new ExampleMatcher to ignore the property path lastname..withIncludeNullValues()
Construct a new ExampleMatcher to ignore the property path lastname and to include null values..withStringMatcherEnding();
Construct a new ExampleMatcher to ignore the property path lastname, to include null values, and use perform suffix string matching.Example<Person> example = Example.of(person, matcher);
根據domain對象和配置的ExampleMatcher
對象來創建一個Example
你可以給個別的屬性指定行爲.(比如.
firstname
和lastname
以及domain對象的嵌套屬性address.city
)
你可以調整他讓他匹配大小寫敏感的選項.ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("firstname", endsWith()) .withMatcher("lastname", startsWith().ignoreCase()); }
另一種配置matcher選項的方式是通過使用Java 8 lambdas表達式.這種方式是一種可以通過詢問實現者修改matcher的回調方法.你不需要去返回matcher,因爲他已經保留了matcher的實例.(引用)
ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("firstname", match -> match.endsWith()) .withMatcher("firstname", match -> match.startsWith()); }
ExampleMatcher
可以設置的範圍設置 範圍 Null-handling ExampleMatcher String matching ExampleMatcher and property path Ignoring properties Property path Case sensitivity ExampleMatcher and property path Value transformation Property path Property path指 這個設置項要跟在需要設置的屬性後邊,而不是在ExampleMatcher對象上進行設置.
對比ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("firstname", endsWith()).withMatcher("lastname", startsWith().ignoreCase());
和ExampleMatcher matcher = ExampleMatcher.matching().withIgnorePaths("lastname").withIncludeNullValues().withStringMatcherEnding();
執行一個Example
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
(單數屬性)可以被屬性匹配正確使用.
StringMatcher
選項:匹配 邏輯結果 DEFAULT (case-sensitive) firstname = ?0 DEFAULT (case-insensitive) LOWER(firstname) = LOWER(?0) EXACT (case-sensitive) firstname = ?0 STARTING (case-sensitive) firstname like ?0 + ‘%’ STARTING (case-insensitive) LOWER(firstname) like LOWER(?0) + ‘%’ ENDING (case-sensitive) firstname like ‘%’ + ?0 ENDING (case-insensitive) LOWER(firstname) like ‘%’ + LOWER(?0) CONTAINING (case-sensitive) firstname like ‘%’ + ?0 + ‘%’ CONTAINING (case-insensitive) LOWER(firstname) like ‘%’ + LOWER(?0) + ‘%’
- 介紹
事務
默認的CRUD操作在Repository裏面都是事務性的。如果是隻讀操作,只需要設置事務readOnly爲true,其他的操作則配置爲@Transaction。如果你想修改一個Repository的事務性,你只需要在子接口中重寫並且修改他的事務:public interface UserRepository extends CrudRepository<User, Long> { @Override @Transactional(timeout = 10) public List\<\User\>\ findAll(); // Further query method declarations }
這樣會讓findAll方法在10秒內執行否則會超時的非只讀事務中。
另一種修改事務行爲的方式在於使用門面或者服務層中,他們包含了多個repository。@Service class UserManagementImpl implements UserManagement { private final UserRepository userRepository; private final RoleRepository roleRepository; @Autowired 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方法的時候,創建一個或者加入一個事務中去。實際在Repository裏面定義的事務將會被忽略,而外部定義的事務將會被應用。當然,要使用事務,你需要聲明(這個例子中,假設你已經使用了component-scan)
要讓方法在事務中,最簡單的方式就是使用@Transactional註解:@Transactional(readOnly = true) public interface UserRepository extends JpaRepository<User, Long> { List <User> findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void deleteInactiveUsers(); }
一般的查詢操作,你需要設置readOnly=true。在deleteInactiveUsers方法中,我們添加了Modifying註解以及覆蓋了Transactional,這樣這個方法執行的時候readOnly=false了.
鎖
想要爲方法指定鎖的類型,你只需要使用@Lock
:interface UserRepository extends Repository<User, Long> { // Plain query method @Lock(LockModeType.READ) List<User> findByLastname(String lastname); }
當然你也可以覆蓋原有的方法:
interface UserRepository extends Repository<User, Long> { // Redeclaration of a CRUD method @Lock(LockModeType.READ); List<User> findAll(); }
審計
基礎知識
SpringData爲您跟蹤誰創建或者修改數據,以及相應的時間提供了複雜的支持。你現在想要這些支持的話,僅僅需要使用幾個註解或者實現接口即可。註解方式: 我們提供了
@CreatedBy
,@LastModifiedBy
去捕獲誰操作的實體,當然還有相應的時間@CreatedDate
和@LastModifiedDate
。class Customer { @CreatedBy private User user; @CreatedDate private DateTime createdDate; // … further properties omitted }
正如你看到的,你可以選擇性的使用這些註解。操作時間方面,你可以使用org.joda.time.DateTime 或者java.util.Date或者long/Long表示。
基於接口的審計:
如果你不想用註解來做審計的話,那麼你可以實現Auditable
接口。他暴露了審計屬性的get/set方法。
如果你不想實現接口,那麼你可以繼承AbstractAuditable,通常來說,註解方式時更加方便的。審計織入:
如果你在用@CreatedBy或者@LastModifiedBy的時候,想織入當前的業務操作者,那你可以使用我們提供的AuditorAware接口。T表示你想織入在這兩個字段上的類型。class SpringSecurityAuditorAware implements AuditorAware<User> { public User getCurrentAuditor() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || !authentication.isAuthenticated()) { return null; } return ((MyUserDetails) authentication.getPrincipal()).getUser(); } }
在這個實現類中,我們使用SpringSecurity內置的Authentication來查找用戶的UserDetails。
通用審計配置:
SpringData JPA有一個實體監聽器,他可以用於觸發捕獲審計信息。要用之前,你需要在orm.xml裏面註冊AuditingEntityListener.當然你得引入spring-aspects.jar
審計配置:<persistence-unit-metadata> <persistence-unit-defaults> <entity-listeners> <entity-listener class="….data.jpa.domain.support.AuditingEntityListener" /> </entity-listeners> </persistence-unit-defaults> </persistence-unit-metadata>
你也可以再每個實體類上使用
@EntityListeners
註解來激活AuditingEntityListener
監聽.@Entity @EntityListeners(AuditingEntityListener.class) public class MyEntity { }
要啓用這個審計,我們還需要在配置文件裏面配置多一條:
<jpa:auditing auditor-aware-ref="yourAuditorAwareBean" />
Spring Data JPA 1.5之後,你也可以使用
@EnableJpaAuditing
註解來激活.@Configuration @EnableJpaAuditing class Config { @Bean public AuditorAware<AuditableUser> auditorProvider() { return new AuditorAwareImpl(); } }
其他: 略. 點擊查看
附錄
附錄A: 命名空間引用
<repositories />
的元素:屬性名稱 描述 base-package 定義去掃描哪些繼承了*Repository接口的用戶自定義Repository接口. repository-impl-postfix 定義用戶自定義實現sql語句的實現類以什麼結尾,以用來自動發現.默認是 Impl
query-lookup-strategy 定義查詢的策略.默認的是 create-if-not-found
named-queries-location 定義去哪裏尋找已經寫好了named-query查詢的配置文件 consider-nested-repositories 考慮是否要控制內嵌的Repository接口.默認是 false
附錄B:Populators 命名空間引用
<populator />
的元素:屬性名稱 描述 locations 尋找要填入Repository接口的對象的值的文件. 附錄C:Repository 查詢關鍵詞
支持的查詢關鍵字:Logical keyword Keyword expressions AND
And
OR
Or
AFTER
After
,IsAfter
BEFORE
Before
,IsBefore
CONTAINING
Containing
,IsContaining
,Contains
BETWEEN
Between
,IsBetween
ENDING_WITH
EndingWith
,IsEndingWith
,EndsWith
EXISTS
Exists
FALSE
False
,IsFalse
GREATER_THAN
GreaterThan
,IsGreaterThan
GREATER_THAN_EQUALS
GreaterThanEqual
,IsGreaterThanEqual
IN
In
,IsIn
IS
Is
,Equals
, (or no keyword)IS_NOT_NULL
NotNull
,IsNotNull
IS_NULL
Null
,IsNull
LESS_THAN
LessThan
,IsLessThan
LESS_THAN_EQUAL
LessThanEqual
,IsLessThanEqual
LIKE
Like
,IsLike
NEAR
Near
,IsNear
NOT
Not
,IsNot
NOT_IN
NotIn
,IsNotIn
NOT_LIKE
NotLike
,IsNotLike
REGEX
Regex
,MatchesRegex
,Matches
STARTING_WITH
StartingWith
,IsStartingWith
,StartsWith
TRUE
True
,IsTrue
WITHIN
Within
,IsWithin
附錄D:Repository查詢的返回結果類型
支持的查詢返回結果類型:Return type Description void
Denotes no return value.
Primitives
Java primitives.
Wrapper types
Java wrapper types.
T
An unique entity. Expects the query method to return one result at most. In case no result is found
null
is returned. More than one result will trigger anIncorrectResultSizeDataAccessException
.Iterator<T>
An
Iterator
.Collection<T>
A
Collection
.List<T>
A
List
.Optional<T>
A Java 8 or Guava
Optional
. Expects the query method to return one result at most. In case no result is foundOptional.empty()
/Optional.absent()
is returned. More than one result will trigger anIncorrectResultSizeDataAccessException
.Stream<T>
A Java 8
Stream
.Future<T>
A
Future
. Expects method to be annotated with@Async
and requires Spring’s asynchronous method execution capability enabled.CompletableFuture<T>
A Java 8
CompletableFuture
. Expects method to be annotated with@Async
and requires Spring’s asynchronous method execution capability enabled.ListenableFuture
A
org.springframework.util.concurrent.ListenableFuture
. Expects method to be annotated with@Async
and requires Spring’s asynchronous method execution capability enabled.Slice
A sized chunk of data with information whether there is more data available. Requires a
Pageable
method parameter.Page<T>
A
Slice
with additional information, e.g. the total number of results. Requires aPageable
method parameter.GeoResult<T>
A result entry with additional information, e.g. distance to a reference location.
GeoResults<T>
A list of
GeoResult<T>
with additional information, e.g. average distance to a reference location.GeoPage<T>
A
Page
withGeoResult<T>
, e.g. average distance to a reference location.- 附錄E : faq. 略 點擊查看
- 附錄F: 詞彙表: 略 點擊查看