-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #313 from devgateway/feature/jpa-query-cache
#311 Cache hibernate queries from JPA repositories
- Loading branch information
Showing
16 changed files
with
291 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/target/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>checkstyle</artifactId> | ||
<groupId>org.devgateway.toolkit</groupId> | ||
<version>1.0</version> | ||
|
||
<properties> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
<maven.compiler.source>8</maven.compiler.source> | ||
<maven.compiler.target>8</maven.compiler.target> | ||
<maven-checkstyle-plugin.version>3.0.0</maven-checkstyle-plugin.version> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-checkstyle-plugin</artifactId> | ||
<version>${maven-checkstyle-plugin.version}</version> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
42 changes: 42 additions & 0 deletions
42
checkstyle/src/main/java/org/devgateway/toolkit/checks/CachableQueryAnnotationCheck.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package org.devgateway.toolkit.checks; | ||
|
||
import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | ||
import com.puppycrawl.tools.checkstyle.api.DetailAST; | ||
import com.puppycrawl.tools.checkstyle.api.TokenTypes; | ||
import com.puppycrawl.tools.checkstyle.utils.AnnotationUtility; | ||
|
||
/** | ||
* Checks that the methods of all classes annotated with @CacheableHibernateQueryResult | ||
* are explicitly annotated to cache or not the query result. | ||
* | ||
* Anytime a new method is defined this will catch a potential missing caching definition. | ||
* | ||
* @author Nadejda Mandrescu | ||
*/ | ||
public class CachableQueryAnnotationCheck extends AbstractCheck { | ||
private static final String ERROR_MESSAGE = | ||
"@CacheableHibernateQueryResult must annotate its methods explicitly either with " + | ||
"@CacheHibernateQueryResult or @NoCacheHibernateQueryResult"; | ||
|
||
@Override | ||
public int[] getDefaultTokens() { | ||
return new int[]{TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF}; | ||
} | ||
|
||
@Override | ||
public void visitToken(DetailAST ast) { | ||
if (AnnotationUtility.containsAnnotation(ast, "CacheableHibernateQueryResult")) { | ||
DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK); | ||
DetailAST methodDef = objBlock.findFirstToken(TokenTypes.METHOD_DEF); | ||
while (methodDef != null) { | ||
if (methodDef.getType() == TokenTypes.METHOD_DEF) { | ||
if (!(AnnotationUtility.containsAnnotation(methodDef, "CacheHibernateQueryResult") | ||
|| AnnotationUtility.containsAnnotation(methodDef, "NoCacheHibernateQueryResult"))) { | ||
log(methodDef.getLineNo(), ERROR_MESSAGE); | ||
} | ||
} | ||
methodDef = methodDef.getNextSibling(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
|
||
<!DOCTYPE checkstyle-packages PUBLIC | ||
"-//Checkstyle//DTD Package Names Configuration 1.0//EN" | ||
"https://checkstyle.org/dtds/packages_1_0.dtd"> | ||
|
||
<checkstyle-packages> | ||
<package name="org.devgateway.toolkit.checks" /> | ||
<package name="com.puppycrawl.tools.checkstyle" /> | ||
</checkstyle-packages> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
...java/org/devgateway/toolkit/persistence/checks/ValidateCacheableHibernateQueryResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package org.devgateway.toolkit.persistence.checks; | ||
|
||
import org.devgateway.toolkit.persistence.repository.CacheableHibernateQueryResult; | ||
import org.reflections.Reflections; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.annotation.PostConstruct; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
/** | ||
* @author Nadejda Mandrescu | ||
*/ | ||
@Component | ||
public class ValidateCacheableHibernateQueryResult { | ||
private static final String SCAN_PACKAGE = "org.devgateway"; | ||
private static final String ERROR_MESSAGE = "Classes inherited from @CacheableHibernateQueryResult base class " | ||
+ "must also be annotated with @CacheableHibernateQueryResult: "; | ||
|
||
private Set<String> validated = new HashSet<>(); | ||
private Set<String> invalidClasses = new HashSet<>(); | ||
|
||
@PostConstruct | ||
public void validate() { | ||
Reflections reflections = new Reflections(SCAN_PACKAGE); | ||
Set<Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(CacheableHibernateQueryResult.class); | ||
annotatedClasses.forEach(annotatedBaseClass -> validateClass(null, annotatedBaseClass, reflections)); | ||
if (!invalidClasses.isEmpty()) { | ||
throw new RuntimeException(ERROR_MESSAGE + String.join(", ", invalidClasses)); | ||
} | ||
} | ||
|
||
private void validateClass(final Class<?> parent, final Class<?> clazz, final Reflections reflections) { | ||
boolean notYetValidated = validated.add(clazz.getCanonicalName()); | ||
if (notYetValidated) { | ||
if (parent != null) { | ||
CacheableHibernateQueryResult annotation = clazz.getAnnotation(CacheableHibernateQueryResult.class); | ||
if (annotation == null) { | ||
invalidClasses.add(clazz.getCanonicalName()); | ||
} | ||
} | ||
reflections.getSubTypesOf(clazz).forEach(subClass -> validateClass(clazz, subClass, reflections)); | ||
} | ||
} | ||
} |
5 changes: 3 additions & 2 deletions
5
.../src/main/java/org/devgateway/toolkit/persistence/repository/AdminSettingsRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,15 @@ | ||
package org.devgateway.toolkit.persistence.repository; | ||
|
||
import org.devgateway.toolkit.persistence.dao.AdminSettings; | ||
import org.devgateway.toolkit.persistence.repository.norepository.BaseJpaRepository; | ||
import org.devgateway.toolkit.persistence.repository.norepository.CacheableQueryResultRepository; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
/** | ||
* @author idobre | ||
* @since 6/22/16 | ||
*/ | ||
@Transactional | ||
public interface AdminSettingsRepository extends BaseJpaRepository<AdminSettings, Long> { | ||
@CacheableHibernateQueryResult | ||
public interface AdminSettingsRepository extends CacheableQueryResultRepository<AdminSettings, Long> { | ||
|
||
} |
19 changes: 19 additions & 0 deletions
19
...rc/main/java/org/devgateway/toolkit/persistence/repository/CacheHibernateQueryResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package org.devgateway.toolkit.persistence.repository; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
import javax.persistence.QueryHint; | ||
|
||
import org.springframework.data.jpa.repository.QueryHints; | ||
|
||
/** | ||
* @author Octavian Ciubotaru | ||
*/ | ||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@QueryHints({@QueryHint(name = "org.hibernate.cacheable", value = "true")}) | ||
public @interface CacheHibernateQueryResult { | ||
} |
24 changes: 24 additions & 0 deletions
24
...ain/java/org/devgateway/toolkit/persistence/repository/CacheableHibernateQueryResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package org.devgateway.toolkit.persistence.repository; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* This annotation helps to guarantee that once query result caching is used, | ||
* then all methods across the repo hierarchy explicitly use the caching or not. | ||
* | ||
* Done with: | ||
* 1. Checkstyle CachableQueryAnnotationCheck: validates that such class methods | ||
* explicitly declare @CacheHibernateQueryResult or @NoCacheHibernateQueryResult. | ||
* | ||
* 2. ValidateCacheableHibernateQueryResult: checks at app startup that | ||
* all subclasses of a class annotated with @CacheableHibernateQueryResult | ||
* are also annotated with @CacheableHibernateQueryResult. | ||
* This will guarantee the checkstyle validation for the entire hierarchy. | ||
*/ | ||
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface CacheableHibernateQueryResult { | ||
} |
11 changes: 11 additions & 0 deletions
11
.../main/java/org/devgateway/toolkit/persistence/repository/NoCacheHibernateQueryResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.devgateway.toolkit.persistence.repository; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) | ||
@Retention(RetentionPolicy.CLASS) | ||
public @interface NoCacheHibernateQueryResult { | ||
} |
63 changes: 63 additions & 0 deletions
63
...evgateway/toolkit/persistence/repository/norepository/CacheableQueryResultRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package org.devgateway.toolkit.persistence.repository.norepository; | ||
|
||
import org.devgateway.toolkit.persistence.repository.CacheHibernateQueryResult; | ||
import org.devgateway.toolkit.persistence.repository.CacheableHibernateQueryResult; | ||
import org.springframework.data.domain.Page; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Sort; | ||
import org.springframework.data.jpa.domain.Specification; | ||
import org.springframework.data.repository.NoRepositoryBean; | ||
import org.springframework.lang.Nullable; | ||
|
||
import java.io.Serializable; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
/** | ||
* @author Octavian Ciubotaru | ||
*/ | ||
@NoRepositoryBean | ||
@CacheableHibernateQueryResult | ||
public interface CacheableQueryResultRepository<T, ID extends Serializable> extends BaseJpaRepository<T, ID> { | ||
|
||
@Override | ||
@CacheHibernateQueryResult | ||
List<T> findAll(); | ||
|
||
@Override | ||
@CacheHibernateQueryResult | ||
List<T> findAll(Sort sort); | ||
|
||
@Override | ||
@CacheHibernateQueryResult | ||
List<T> findAll(Specification<T> spec); | ||
|
||
@Override | ||
@CacheHibernateQueryResult | ||
Page<T> findAll(Specification<T> spec, Pageable pageable); | ||
|
||
@Override | ||
@CacheHibernateQueryResult | ||
Page<T> findAll(Pageable pageable); | ||
|
||
@Override | ||
@CacheHibernateQueryResult | ||
List<T> findAll(Specification<T> spec, Sort sort); | ||
|
||
@Override | ||
@CacheHibernateQueryResult | ||
Optional<T> findOne(@Nullable Specification<T> spec); | ||
|
||
@Override | ||
@CacheHibernateQueryResult | ||
long count(Specification<T> spec); | ||
|
||
@Override | ||
@CacheHibernateQueryResult | ||
long count(); | ||
|
||
@Override | ||
@CacheHibernateQueryResult | ||
Optional<T> findById(ID id); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
# Uncomment for Hibernate SQL statements and parameters | ||
#logging.level.org.hibernate.SQL=DEBUG | ||
#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.