'Spring not picking updated @Query value Using AOP

JPA: Method

@Repository
public interface FloorRepository extends JpaRepository<TnFloor, Integer> {
  @Query("select distinct tnFloor from TnFloor tnFloor where tnFloor.tnBuilding.buildingId in ?1")
  public List<TnFloor> findByBuildingIds(List<Integer> buildingIds);
}   
  @Before("dataRolesPointCuts()")
  public void beforeMethods(JoinPoint joinPoint) {

    log.debug(" Before Advice Called " + joinPoint.toShortString());
    String classArray[]=joinPoint.getTarget().getClass().getGenericInterfaces()[0].getTypeName().split("\\.");
    String className = classArray[classArray.length-1];
    String methodName = joinPoint.getSignature().getName();
    String securedMethodName = className + "_" + methodName;
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

    if(authentication!=null)
    {
      UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
      String loggedINUserName = userDetails.getUsername();
      Map<String, Map<String, String>> userRoleMap = usernameRoleMap.get(loggedINUserName);
      TnMethodSecurityModel methodSecurity = methodSecurityMap.get(securedMethodName); // Replace with Map

      if(methodSecurity!=null && methodSecurity.getTnMethodSecurityFilters()!=null && methodSecurity.getTnMethodSecurityFilters().size()>0)
      {
        Class<?> clazz =((Class<?>) joinPoint.getTarget().getClass().getGenericInterfaces()[0]);
        try {
          Method[] methods = clazz.getMethods();
          Method method=null;
          for(Method meth: methods )
          {
            if(meth.getName().equals(methodName))
            {
              method=meth;
              break;
            }
          }

          if(method == null)
            return;

          Query secParam = method.getAnnotation(Query.class);
          String query=secParam.value();
          String securityPredicate=Util.getSecuirtyPredicate(methodSecurity, userRoleMap);

          try {
            System.out.println("old MethodAnnotation = " + secParam.value());
            Util. changeAnnotationValue(secParam, "value", query+" "+securityPredicate);
            System.out.println("Asspect Query :: "+query);
          }   catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
          System.out.println("modified MethodAnnotation = " + secParam.value());

        } catch ( SecurityException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    }

    log.info("Executing   with argument: {}", className + " " + methodName);
  }

  @SuppressWarnings("unchecked")
  public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue){
    Object handler = Proxy.getInvocationHandler(annotation);
    Field f;
    try {
      f = handler.getClass().getDeclaredField("memberValues");
    } catch (NoSuchFieldException | SecurityException e) {
      throw new IllegalStateException(e);
    }
    f.setAccessible(true);
    Map<String, Object> memberValues;
    try {
      memberValues = (Map<String, Object>) f.get(handler);
    } catch (IllegalArgumentException | IllegalAccessException e) {
      throw new IllegalStateException(e);
    }
    Object oldValue = memberValues.get(key);
    if (oldValue == null || oldValue.getClass() != newValue.getClass()) {
      throw new IllegalArgumentException();
    }
    memberValues.put(key,newValue);
    return oldValue;
  }

Output After Before Advice execution :

old MethodAnnotation = select distinct tnFloor from TnFloor tnFloor where tnFloor.tnBuilding.buildingId in ?1 
 
modified MethodAnnotation = select distinct tnFloor from TnFloor tnFloor where tnFloor.tnBuilding.buildingId in ?1   (tnFloor.tnBuilding.buildingId IN (0,1,6)) 

But Final JPA is executing query before modification OUTPUT :.

select tnbuilding0_.building_id as building1_17_0_, tnbuilding0_.description as descript2_17_0_, tnbuilding0_.name as name3_17_0_, tnbuilding0_.site_id as site_id4_17_0_, tnsite1_.site_id as site_id1_65_1_, tnsite1_.description as descript2_65_1_, tnsite1_.email as email3_65_1_, tnsite1_.name as name4_65_1_, tnsite1_.url as url5_65_1_ from tn_building tnbuilding0_ inner join tn_site tnsite1_ on tnbuilding0_.site_id=tnsite1_.site_id where tnbuilding0_.building_id=?


Solution 1:[1]

Sorry for lecturing you, but... Annotation values are constants. You cannot change them. Your hacky way of trying to change their in-memory representations is maybe a nifty exercise, but bad design. If your application or aspect really rely on something like this, as a developer you should feel an instant urge to refactor instead.

As for why it is not working as you dreamed it up: You might expect that Spring reads annotations every time before executing an annotated method. But usually such frameworks scan annotations while wiring an application.

Simply do not use hard-coded constants (like in annotations) for things you might want to make more dynamic, be it by using AOP or by other means. If you do want to keep the query annotation but use AOP, I think you should try to hook into another component where the query is being parsed or sent to the database and modify it there.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 kriegaex