JPA Repository and constructor with varargs - java

It is possible to use constructor of varargs in #Query (org.springframework.data.jpa.repository). What I mean is class like this:
public class EntityDTO {
public EntityDTO(Long id, LocalDate... dates) {
// some code here where dates
//which are converted to List<Pair<LocalDate, LocalDate> datesList
}
}
public interface CustomRepository extends JpaRepository<Entity, Long> {
#Query("SELECT new package.path.EntityDTO(id, date1, date2, date2) FROM Entity")
List<EntityDTO> findEntityList();
}
Now there is an error like this:
Unsatisfied dependency expressed through constructor parameter 3
Validation failed for query for method public abstract java.util.List CustomRepository.findEntityList(java.lang.Long,java.time.LocalDate,java.time.LocalDate)
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException:
Unable to locate appropriate constructor on class [EntityDTO]. Expected arguments are: long, java.time.LocalDate, java.time.LocalDate, java.time.LocalDate, java.time.LocalDate
It is only query example because dto can have id and LocalDate parameter from 2 to 12-14 in constructor, dates can be from different tables (entities). It depends from query. I want class more generic for this purpose. Query is much more complicated than in this example but I'm interested in some kind of constructor which give me possibility to create something similar that I described in EntityDTO constructor in comment. It is possible to use varargs in #Query?
Edit:
Do you have any suggestion what could I use if JPQL don't have this functionality?

I'm afraid that's impossible.
The way Hibernate implements matching constructor resolution, only constructors with the exact same number of arguments as the number of parameters passed into the constructor expression are considered potential matches.
A workaround could be to create a constructor that accepts a list and then modify your query to something along the lines of SELECT NEW EntityDTO(id, NEW list(date1, date2, date2))... if only nested NEW expressions were supported. Unfortunately, this is a long standing feature request.

Related

Using Blaze Persistence optional parameter in subquery

In Blaze Persistence, is there a way to use an optional parameter in a subquery?
In my current use case, let's say I have topics, users, and a third table called topic_last_seen to record the date at which each user has last read each topic. I would like to create an entity view for the Topic entity which also maps that date for each topic with the current user given as an "optional parameter". This is roughly what I've been trying:
#EntityView(Topic.class)
public interface TopicView
{
#IdMapping
Long getId();
String getSummary();
#MappingParameter("currentUserId")
Long getCurrentUserId();
#MappingSubquery(LastSeenDateSubqueryProvider.class)
Date getLastSeenDate();
class LastSeenDateSubqueryProvider implements SubqueryProvider
{
#Override
public <T> T createSubquery(SubqueryInitiator<T> subqueryBuilder)
{
return subqueryBuilder.from(TopicLastSeen.class, "lastSeen")
.select("dateSeen")
.where("lastSeen.user.id").eqExpression("EMBEDDING_VIEW(currentUserId)")
.where("lastSeen.topic.id").eqExpression("EMBEDDING_VIEW(id)")
.end();
}
}
}
Unfortunately, this results in an exception:
java.lang.IllegalArgumentException: Attribute 'currentUserId' not found on type 'Topic'
Is there any way to use an optional parameter in a subquery like this at all? Or is there a different way to map this information? Changing the DB schema is not an option at this point.
Looks like I figured this out a possible suolution by trial and error. It seems that the "optional parameters" from EntityViewSetting can be used like regular parameters in JPQL:
return subqueryBuilder.from(TopicLastSeen.class, "lastSeen")
.select("dateSeen")
.where("lastSeen.user.id").eqExpression(":currentUserId")
.where("lastSeen.topic.id").eqExpression("EMBEDDING_VIEW(id)")
.end();
Unfortunately though, with this subquery mapping the currentUserId parameter is no longer "optional". When it is not set I get another exception:
org.hibernate.QueryException: Named parameter not bound : currentUserId

Using named parameters for method when I use #Query

I am using spring-data-jpa, I want update something, I have annotated my method in PaySupplierSettleBillRepository as
public interface PaySupplierSettleBillRepository extends JpaRepository<PaySupplierSettleBillEntity, Long>,
JpaSpecificationExecutor<PaySupplierSettleBillEntity> {
#Modifying
#Query("update PaySupplierSettleBillEntity p set p.payTime=:payTime,p.paymentOrder=:paymentOrder, p.transferTime=:transferTime, p.transferBank=:transferBank, p.transferOrder=:transferOrder, p.operatorName=:operatorName, p.remark=:remark where p.orderNumber=:orderNumber")
int updatePayInfo(PaySupplierSettleBillEntity entity);
}
I am getting following exception while starting
Caused by: java.lang.IllegalStateException: Using named parameters for method public abstract xxxxxx
how I fix it ? thinks.
That is not how you write a #Query with named parameters. Take a look at the example from Spring documentation here (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.named-parameters).
If you want to provide an object as param, you can do something like this.
#Query("UPDATE Entity E SET E.name = :#{#entity.name}")
public void updateEntity(#Param("entity") Entity entity);
Caused by: java.lang.IllegalStateException: Using named parameters for method can be thrown when a method has redundant parameter which is not used in the #Query.
In your case, entity should be used in the SQL query like it's mentioned in the previous answer https://stackoverflow.com/a/56053250/5962766 or you should replace it with specifying necessary parameters one by one in the method signature.
Related: IllegalStateException with naming parameters in tomcat

Using Before keyword with method parameter in spring data

I have entity Composition containing fields Date publicationDate and Boolean archival. I'm trying to get list of Compositions with publicationDate before date passed as argument and having archival flag set to false. After going through some of 'query creation from method names' tutorials and docs i came up with methods
List<Composition> findByPublicationDateBeforeDateAndArchivalFalse(Date date);
or
List<Composition> findByPublicationDateBeforeDateAndArchivalFalse(#Param("date")Date date);
but none of this works. Both gives UnsatisfiedDependencyException with
nested exception is org.springframework.data.mapping.PropertyReferenceException: No property beforeDate found for type Date! Traversed path: Composition.publicationDate.
Intellij also underlines BeforeDate as it cannot resolve property beforeDate. What is the proper way of using those keywords with parameters so the parameters could be distinguished from fields by Spring?
Refactor your method to following:
List<Composition> findByPublicationDateBeforeAndArchivalFalse(Date date);
Before keyword will compare publicationDate with date you pass as an argument, so there is no need to say BeforeDate. Just like an example in Spring Data documentation:
Before -> findByStartDateBefore produces following SQL part … where x.startDate < ?1

Spring Data Repositories - Find where field in list

I'm trying to use spring PagingAndSortingRepository with a find MyEntity where field in fieldValues query as follows:
#Repository
public interface MyEntity extends PagingAndSortingRepository<MyEntity, String> {
List<MyEntity> findByMyField(Set<String> myField);
}
But of no success.
I expected the above function to return all entities whose field matches one of the field values but it only returns empty results.
Even though it seems like a pretty straight forward ability i could not find any reference to it in the docs.
Is / How that could be achieved?
Thanks.
This should indeed be possible if you are searching on a specific field within your entity and you want to return a list of all that field matches at least one entry in some collection. The documentation here says this can be achieved using the keyword In example: findByAgeIn(Collection<Age> ages) and is equivalent to … where x.age in ?1
From your post i'm not 100% sure if this is the use case you are after but give this a try. You will need to search on a specific field, so replace 'field' with whatever field you are searching on. If you are searching on multiple fields it may be possible to concatenate the results with the Or keyword and specify multiple fields that way.
#Repository
public interface MyEntity extends PagingAndSortingRepository<MyEntity, String> {
List<MyEntity> findByFieldIn(Set<String> myField);
}
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Your method name has to be findByMyFieldIn so you got to add an In at the end to get a where ... in (..) query.

How to map class attributes with underscores "_" in their names in Spring-data JPA

Does JPA with Spring-Data have a problem with attributes with underscores "_" in their names? This is my interface that extends JpaRepository:
public interface I_My_Class extends JpaRepository<MyClass, Long> {
public MyClass findByA_my_table_id (Long headerId);
}
This line: findByA_my_table_id (Long headerId); gives this error:
Invalid derived query! No property "a" found for type MyClass !
If I name the method public MyClass findBya_my_table_id (Long headerId); it gives me the same error. If I name the attribute amytableid without the underscores I don't get the error but if I do that it's not easy to read later on. This is the class where I have the table attribute:
#Entity
#Table(name="MyTable")
public class MyClass implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column (name="MY_TABLE_ID", nullable=false)
private Long a_my_table_id; // <-- this is the attribute that I try to put in the named query
}
Yes Spring Data will have problem with underscores in Entity attribute names. Reason being JpaRepository simply expects attributes with proper Java Standard naming conventions eg property names should be in lower case. (if you can add multiple nouns to make it more meaning full then better make first letter of nouns in upper case except the first one)
String aMyTableId;
Above property would create tell JpaRepository to create a method like
List<MyClass> findByAMyTableId(String aMyTableId);
This would not give compilation error.
In case you want to write custom queries then you can use #Query API. Here you can write Object oriented query.
#Query("Select myclass from MyClass myclass where myclass.aMyTableId=?1 and myclass.activeFlag='1'")
List<MyClass> findByAMyTableIdWithActiveFlagOn(String aMyTableId);
You can find many tutorials and sites explaining how to write custom queries.

Categories

Resources