Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can I use global query comment instead @QueryHint or @Meta? #3485

Closed
minkukjo opened this issue May 22, 2024 · 3 comments
Closed

Can I use global query comment instead @QueryHint or @Meta? #3485

minkukjo opened this issue May 22, 2024 · 3 comments
Labels
for: stackoverflow A question that's better suited to stackoverflow.com

Comments

@minkukjo
Copy link

I'm using spring boot and spring data jpa 2.7 version for developing our service. I would like to add every jpa query method when jpa makes sql query without any annotation.

In my version, if I want to add query comment in jpa, I have to use like this,

@QueryHints({
            @QueryHint(name = org.hibernate.annotations.QueryHints.COMMENT, value = "userRepository.findAll")
    })

I want to add query comment as "jpa query method name".

When slow query is occurred, our DBA would let us which query is slow.

However, Our service has so many jpa and querydsl queries in our service so we don't know where query is used in our codes.

So we decide to add every query comment in our codes like"/* findByUserId */".

I couldn't find these kind of option in jpa.

I wonder spring data jpa team has a plan that is going to add global query hint in jpa.

I'm so appreciated your services.

Thank you!

@minkukjo minkukjo changed the title Can I use global query hint instead @QueryHint or @Meta? Can I use global query comment instead @QueryHint or @Meta? May 22, 2024
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 22, 2024
@mp911de
Copy link
Member

mp911de commented May 23, 2024

Right now, there's no global hook for query hints. You could however introduce functionality on your side to make it work. SimpleJpaRepository requires a slightly different approach than QuerydslJpaPredicateExecutor.

For the base repository implementation SimpleJpaRepository you need to create a subclass of SimpleJpaRepository and use that one as base repository.

In the second step, you create a wrapper for CrudMethodMetadata that holds query hints and other details that are evaluated for each query. You would augment CrudMethodMetadata.getComment() with details from CrudMethodMetadata.getMethod(). You could also augment CrudMethodMetadata.getQueryHints() with a custom wrapper.

For QuerydslJpaPredicateExecutor, you can subclass JpaRepositoryFactory and override getRepositoryFragments(…) to hook into CrudMethodMetadata.

@mp911de mp911de added the status: waiting-for-feedback We need additional information before we can continue label May 23, 2024
@minkukjo
Copy link
Author

@mp911de
Thank you for your kindness answer.
I'm going to try to add sub class of SimpleJpaRepository, JpaRepositoryFactory.
Have a good day!

@mp911de mp911de added for: stackoverflow A question that's better suited to stackoverflow.com and removed status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged labels May 24, 2024
@minkukjo
Copy link
Author

minkukjo commented Jun 26, 2024

@mp911de
Hi, I have a one more question about "wrapper for CrudMethodMetadata"
I'm using JPA 2.7 version.

I made a subclass of SimpleJpaRepository so I succeeded to change base repository SimpleJpaRepository to CustomJpaRepository that I made.

And then I override setRepositoryMethodMetadata to apply Custom CrudMethodMetadata.

But CrudMethodMetadata is always null when I debug setRepositoryMethodMetadata when spring is about start.

How Can I make wrapper for CrudMethodMetadata?

@Repository
public class CustomCrudRepository<T, ID> extends SimpleJpaRepository<T, ID> {

  public CustomCrudRepository(
      JpaEntityInformation<T, ?> entityInformation,
      EntityManager entityManager) {
    super(entityInformation, entityManager);
  }

  @Autowired
  public CustomCrudRepository(Class<T> domainClass, EntityManager em) {
    this(JpaEntityInformationSupport.getEntityInformation(domainClass, em), em);
  }

  @Override
  public void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) { // <- parameter is always null
    CrudMethodMetadata repositoryMethodMetadata = super.getRepositoryMethodMetadata();

    repositoryMethodMetadata.getMethod().getName();

    if (repositoryMethodMetadata instanceof CustomCrudMethodMetadata) {
      System.out.println("True!");
    }

    if (crudMethodMetadata instanceof CustomCrudMethodMetadata) {
      System.out.println("True!");
    }
    super.setRepositoryMethodMetadata(crudMethodMetadata);
  }
}

I copied DefaultCrudMethodMetadata to make custom CrudMethodMetadata like below

public class CustomCrudMethodMetadata implements CrudMethodMetadata {

  @Nullable
  private final LockModeType lockModeType;
  private final QueryHints queryHints;
  private final QueryHints queryHintsForCount;
  private final Optional<EntityGraph> entityGraph;
  private final Method method;

  CustomCrudMethodMetadata(Method method) {
    Assert.notNull(method, "Method must not be null!");
    this.lockModeType = findLockModeType(method);
    this.queryHints = findQueryHints(method, (it) -> true);
    this.queryHintsForCount = findQueryHints(
        method, org.springframework.data.jpa.repository.QueryHints::forCounting);
    this.entityGraph = findEntityGraph(method);
    this.method = method;
  }

  private static Optional<EntityGraph> findEntityGraph(Method method) {
    return Optional.ofNullable(
        AnnotatedElementUtils.findMergedAnnotation(method, EntityGraph.class));
  }

  @Nullable
  private static LockModeType findLockModeType(Method method) {
    Lock annotation = AnnotatedElementUtils.findMergedAnnotation(method, Lock.class);
    return annotation == null ? null : (LockModeType) AnnotationUtils.getValue(annotation);
  }

  private static QueryHints findQueryHints(Method method,
      Predicate<org.springframework.data.jpa.repository.QueryHints> annotationFilter) {
    MutableQueryHints queryHints = new MutableQueryHints();
    org.springframework.data.jpa.repository.QueryHints queryHintsAnnotation = (org.springframework.data.jpa.repository.QueryHints) AnnotatedElementUtils.findMergedAnnotation(
        method, org.springframework.data.jpa.repository.QueryHints.class);
    if (queryHintsAnnotation != null && annotationFilter.test(queryHintsAnnotation)) {
      QueryHint[] var4 = queryHintsAnnotation.value();
      int var5 = var4.length;

      for (int var6 = 0; var6 < var5; ++var6) {
        QueryHint hint = var4[var6];
        queryHints.add(hint.name(), hint.value());
      }
    }

    QueryHint queryHintAnnotation = AnnotationUtils.findAnnotation(method, QueryHint.class);
    if (queryHintAnnotation != null) {
      queryHints.add(queryHintAnnotation.name(), queryHintAnnotation.value());
    }

    return queryHints;
  }

  @Nullable
  public LockModeType getLockModeType() {
    return this.lockModeType;
  }

  public QueryHints getQueryHints() {
    return this.queryHints;
  }

  public QueryHints getQueryHintsForCount() {
    return this.queryHintsForCount;
  }

  public Optional<EntityGraph> getEntityGraph() {
    return this.entityGraph;
  }

  public Method getMethod() {
    return this.method;
  }
}

How Can I customize CrudMethodMetadata to apply global query hint in SimpleJpaRepository?

Could you give me more hint to achieve my goal that is made global query comment in simpleJpaRepository

I was so appreciated your answer. Thanks a lot!! 😄

@minkukjo minkukjo reopened this Jun 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: stackoverflow A question that's better suited to stackoverflow.com
Projects
None yet
Development

No branches or pull requests

3 participants