@EntityGraph
我使用存儲庫 '@EntityGraph' 註釋爲findAll
方法:要使用1個+ N問題我用下面兩種方法戰鬥。只需覆蓋它:
@Override
@EntityGraph(attributePaths = {"author", "publisher"})
Page<Book> findAll(Pageable pageable);
此方法適用於Repository的所有「讀取」方法。
緩存
我用緩存減少1個+ N問題的複雜預測的影響。
假設我們有書實體存儲圖書數據和閱讀實體存儲有關特定圖書和讀者評價的讀數數量的信息。爲了得到這個數據,我們可以做出這樣的預測:
@Projection(name = "bookRating", types = Book.class)
public interface WithRatings {
String getTitle();
String getIsbn();
@Value("#{@readingRepo.getBookRatings(target)}")
Ratings getRatings();
}
哪裏readingRepo.getBookRatings
是ReadingRepository的方法:
@RestResource(exported = false)
@Query("select avg(r.rating) as rating, count(r) as readings from Reading r where r.book = ?1")
Ratings getBookRatings(Book book);
它還返回存儲「等級」信息的投影:
@JsonSerialize(as = Ratings.class)
public interface Ratings {
@JsonProperty("rating")
Float getRating();
@JsonProperty("readings")
Integer getReadings();
}
/books?projection=bookRating
的請求將導致調用readingRepo.getBookRatings
爲每個書將導致多餘的N查詢。
爲了減少這方面的影響,我們可以使用緩存:
在SpringBootApplication類準備緩存:
@SpringBootApplication
@EnableCaching
public class Application {
//...
@Bean
public CacheManager cacheManager() {
Cache bookRatings = new ConcurrentMapCache("bookRatings");
SimpleCacheManager manager = new SimpleCacheManager();
manager.setCaches(Collections.singletonList(bookRatings));
return manager;
}
}
然後加入相應的註釋readingRepo.getBookRatings
方法:
@Cacheable(value = "bookRatings", key = "#a0.id")
@RestResource(exported = false)
@Query("select avg(r.rating) as rating, count(r) as readings from Reading r where r.book = ?1")
Ratings getBookRatings(Book book);
並在更新書籍數據時實施緩存逐出:
@RepositoryEventHandler(Reading.class)
public class ReadingEventHandler {
private final @NonNull CacheManager cacheManager;
@HandleAfterCreate
@HandleAfterSave
@HandleAfterDelete
public void evictCaches(Reading reading) {
Book book = reading.getBook();
cacheManager.getCache("bookRatings").evict(book.getId());
}
}
現在的/books?projection=bookRating
所有後續請求將從我們的緩存得到的評定數據,不會造成多餘的數據庫請求。
更多信息和工作示例是here。