Spring boot not able to read custom #Query annotation - spring

I have requirement when we apply #CustomQUery annotation, then I need to intercept this method and append the query predicate which I already know.
Created
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
#Documented
public #interface CustomQuery {
Query query();
}
#Repository
public interface FloorRepository extends JpaRepository<TnFloor, Integer> {
public String query="select distinct tnFloor from TnFloor tnFloor where tnFloor.tnBuilding.buildingId in ?1 ";
#CustomQUery(query=#Query(query))
public List<TnFloor> findByBuildingIds(List<Integer> buildingIds);
}
Here, Spring is unable to read this #CustomQUery because I have not mentioned anywhere to read this annotation.
Is this the correct way to create custom query annotation ?
I am getting below exception on application startup.
Could not create query for public abstract java.util.List
FloorRepository.findByBuildingIds(java.util.List)!
Reason: Failed to create query for method public abstract java.util.List FloorRepository.findByBuildingIds(java.util.List)!
No property buildingIds found for type TnFloor!
Did you mean 'buildingId'?;
nested exception is java.lang.IllegalArgumentException: Failed to c

As other people have already said in comments, I think your way of extending the Query annotation is usefull only if you need to do some things more than just extending it.
If you need some paths to enhance the behavior of the #Query annotation, maybe using #Modifying annotation could get the point, or using #NamedQuery and #NamedNativeQuery annotations too.
If it is a requirement you could not resolve with these other annotations, maybe make some click on them to see how they are declared and raised in the Spring IoC ecosystem using Aspect programming.
The problem here, to my point of view, seems not to be related to your annotation, but a missing property, as the error message has told to you :
No property buildingIds found for type TnFloor! Did you mean 'buildingId'?;
Maybe because of a typo error in your annotation when you are using it, which is not found :
declared as public #interface CustomQuery and used as #CustomQUery(query=#Query(query)). Write the things as they are declared will work better I think.
Did you try using native query feature like that too ?
Query q = em.createNativeQuery("SELECT a.firstname, a.lastname FROM Author a");
// of course, update with your own code.
You can look at what JPA is capable of and switch to native query as I have just added if it is not supported.

Related

#EntityGraph annotation doesn't work properly?

I need to implement two different implementations for the same findAll() method by following different EntityGraphs annotations. By referencing through another StackOverflow post, I found a way to implement the same findAll() method with different EntityGrpahs. But when I use default methods as mentioned in that post, I am not getting the expected behavior. It neglects the #EntityGraph annotation and returns lazy Collections by following the default behavior.
Please provide a fix for this issue or state any other better solution that I can implement to solve this problem.
public interface BspCategoryRepository extends JpaRepository<DbpMetaBspCategory, String> {
#EntityGraph(attributePaths = {"dbpBspMetaCollection","dbpBspMetaCollection.dbpBspMetaCustomFieldCollection","dbpBspMetaCollection.bspType","dbpBspMetaCollection.bankCode","dbpBspMetaCollection.dbpBspMetaCustomFieldCollection.fieldType"}, type = EntityGraph.EntityGraphType.FETCH)
default List<DbpMetaBspCategory> findAllCategories(){
return findAll();
}
}
Please refer to the second answer in the post which was answered by Femi.
References
Spring Data simply can not know about this annotation, as the method is not abstract. You should be able to declare the method just like this:
#EntityGraph(attributePaths = {"dbpBspMetaCollection","dbpBspMetaCollection.dbpBspMetaCustomFieldCollection","dbpBspMetaCollection.bspType","dbpBspMetaCollection.bankCode","dbpBspMetaCollection.dbpBspMetaCustomFieldCollection.fieldType"}, type = EntityGraph.EntityGraphType.FETCH)
List<DbpMetaBspCategory> findAllCategories();

Jackson deserializer priority?

I have a Spring Boot app that is modeling ActityStreams objects and for the most part Jackson's Polymorphic Deserialization works well.
There are 'objects' in the JSON which are references (links) and not JSON objects with type information. For instance
"actor":"https://some.actors.href/ rather than
"actor":{
"type":"Actor",
"name":"SomeActor"
}
I've written custom deserializers and and placed them on the fields to deal with this
#JsonDeserialize (using = ActorOrLinkDeserializer.class)
private Actor actor;
However my ActorOrLinkDeserializer is instantiated but never called and Jackson complains with Missing type id when trying to resolve subtype of [simple type, class org.w3.activity.streams.Actor]: missing type id property 'type' (for POJO property 'actor') which is from the polymorphic deserializer.
It appears that the polymorphic deserialization code takes precedence over my local #JsonDeserialize annotation and I need a way to force my code to run first.
I've tried using my own ObjectMapper rather than Boot's and there's no difference.
I'd appreciate pointers and suggestions.
It turns-out there's a fairly simple solution to this problem using a DeserializationProblemHandler.
What I've implemented that works for all test cases so far is
1.
objectMapper.addHandler(new DeserProblemHandler());
or register with Spring Boot.
2.
public class DeserProblemHandler extends DeserializationProblemHandler {
public JavaType handleMissingTypeId(DeserializationContext ctxt, JavaType baseType, TypeIdResolver idResolver, String failureMsg) {
return TypeFactory.defaultInstance().constructType(baseType.getRawClass());
}
}
Add a constructor to each of the polymorphic classes that takes a string argument which is the href.

Hibernate interceptors with spring does not intercept

I'm trying to use interceptors with hibernate in order to create a log table.
The application works fine but my interceptors are never triggered when they were supposed to be triggered.
I tried the solutions of this post on stack overflow and I did not succed in making it work.
What I did is:
create an entity class that will be the table where the log are stored (UserLog)
create another class called by the interceptor that will fill my table (dao), in this example it is called UserLogUtil
I tried the first solution, making my interceptor class not dependant on spring #Autowire by making the UserLogUtil a singleton manually and adding the following line to my application.properties : spring.jpa.properties.hibernate.ejb.interceptor=LogInterceptor.
I also tried the second solution by autowiring UserLogUtil however my spring configuration is in xml and although I tried to convert the beans from the above example it did not work.
This is what my interceptor looks like:
public class LogInterceptor extends EmptyInterceptor {
private UserLogUtil userLogUtil = UserLogUtil.getInstance();
public boolean onSave(Object entity,Serializable id, Object[] state,String[] propertyNames,Type[] types)
throws CallbackException {
if (entity instanceof Owner){
userLogUtil.log(........));
}
return false;
}
I think I missed something in the process of building my interceptor and my design could be flawed. Do you have any idea that could make my interceptor function as it should ?

How to create custom abstract repository like PagingAndSortingRepository, etc?

For example in my persistence:
public interface SomePersistence extends JpaRepository<SomeClass, String> {};
I can write method like:
#Query("some query")
List<SomeClass> getAllWithSomeParam();
and spring knows to use SimpleJpaRepository class - implementation of JpaRepository.
When i write:
#Query("some query")
Page<SomeClass> getAllWithSomeParam(Pageable page);
spring knows to use implementation of PagingAndSortingRepository.
But now i want to add my own returned type - Cursor<T>
It`s mean i want write:
#Query("some query")
Cursor<SomeClass> anyMethodName();
Then i want to give spring my own repository CursorRepository with its personal CursorRepositoryImpl when i have only one method Cursor<T> findAll()
Can i realize it?

Why is there a duplicate variable use in Spring Data JPA and QueryDSL?

There are a lot of examples online of using QueryDSL like this:
public class CustomerRepositoryImpl
extends QueryDslRepositorySupport
implements CustomerRepositoryCustom {
public Iterable<Customer> findAllLongtermCustomersWithBirthday() {
QCustomer customer = QCustomer.customer;
return from(customer)
.where(hasBirthday().and(isLongTermCustomer()))
.list(customer);
}
}
This code makes sense, but I am wondering why customer is "duplicated" in the method call to list().
Shouldn't the type be obvious from the reference in from(customer)?
from defines the source and list the projection. Querydsl query classes don't have any generic type argument for the return type, the projection (select part) is defined in the last part of the query construction chain.
Examples for cases where a different projection than the source is wanted
specific columns only: query.list(customer.firstName, customer.lastName)
constructor invocation : Projections.constructor(...)
Bean population: Projections.bean(...)
multiple from calls are used

Resources