Using Before keyword with method parameter in spring data - java

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

Related

How to get only specified object class from mongo collection with spring data

I write two classes in one mongo collection, say One and Two. In these classes I have field someId. Then I want to get all One objects with specified someId. I created a method:
Page<One> findBySomeId(String id, Pageable pageable);
But get error:
org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate ...
As I see, mongo take both One and Two objects, and then trying to create One objects from them.
I tried write next:
Page<One> findOneBySomeId(String id, Pageable pageable);
and got the same error. How to write a proper method to retrieve only specified objects from mongo DB with spring data?
Your query should be like this
Page<IOne> findByOneSomeId(String id, Pageable pageable);
public interface IOne {
public One getOne();
}
You should limit the query by specifying something that distinguishes One and Two. For example, you can filter by:
the (internal) _type field that spring automatically persist, which is equal to the fully qualified class name (unless #TypeAlias is provided, when it could be whatever you set)
some other property that you know is present only in the One documents (Page<One> findBySomeIdAndPropertyExistsTrue(String id, Pageable pageable))
With Spring Data you can use following syntax :
One findFirstBySomeId(String someId);
Here is documentation about spring data(4.4.5. Limiting Query Results) where you find the explanation and examples: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#reference

ISODate fails to parse from Spring Boot JPA repo

I have a SpringBoot JPA Repository that finds a bunch of "things" in a MongoDB instance that has a field called "lastModified" which is an ISODate() as per the below.
{ "name": "a", "lastModified", "ISODate(2018-04-19T19:10:39.356574)" }
When I use a repository function like:
public List<Thing> findByName(String name);
I get a
org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value 'ISODate(2018-04-18T18:38:42.635027)'; nested exception is java.lang.IllegalArgumentException
I have tried
Creating separate setters and constructors that take each java.util.Date and
String and do a converstion with a
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS")
Using
#DateTimeFormat(pattern="yyyy-MM-dd'T'HH:mm:ss.SSSSSS") on the
lastModified parameter on the Entity
Am I missing something about how Mongo does this conversion? I would expect it turns that ISODate into a simple string and passes it into the setLastModified(String name) function but I guess that's not the case?
Turns out it was just because the date was inserted as a string of "ISODate(2018-04-19T19:10:39.356574)" instead as a Date which would be like ISODate("2018-04-17T19:43:00Z")
Once I went and found and fixed the source of this data, it started working properly.

JPA Repository and constructor with varargs

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.

Java- PLSQL- Call Table of records from java

PLSQL package-procedure declarations
TYPE custom_type IS TABLE OF single_rec_type;
TYPE single_rec_type IS RECORD(
//id, name etc
)
Problem:
But custom_type has no direct Java type representation [like OracleTypes.CLOB or OracleTypes.CURSOR]
because custom_type is a PLSQL type and not a SQL type.
When I googled, I came across these two options:
To represent it ,
(1) create a SQL TYPE from procedure(or a wrapper PLSQL function) that we can bind from java.
Reference: java - passing array in oracle stored procedure
(2) Register the output parameter with our type and use SQLData object to represent a record.
Reference: Howto get a table as a out parameter in oracle
callableStatement.registerOutParameter(8, OracleTypes.ARRAY, "custom_type");
On doing this, I get the error:
java.sql.SQLException: invalid name pattern: MYDB_OWNER.custom_type
at oracle.jdbc.oracore.OracleTypeADT.initMetadata(OracleTypeADT.java:554)
at oracle.jdbc.oracore.OracleTypeADT.init(OracleTypeADT.java:471)
One suggestion was to declare the custom_type TYPE inside the schema, instead of declaring inside the package.
or by creating public synonym and giving grants.
Question - Regarding the second approach, is it correct practice to declare any custom type in schema level?
Yes, That's the only way it works. I followed the link mentioned in second approach
Howto get a table as a out parameter in oracle
and it worked. The Package level changes included (1) Declaring the custom_type and single_rec_type in schema level [as global, not inside the package] and (2) Replacing IS RECORD with AS OBJECT.
The Java code changes apart from what was mentioned in the link, includes giving the complete name for the class in map.put("SINGLE_REC_TYPE", Class.forName("com.example.SRecord"));
Another thing to notice is that in that example, it mentioned stream.readString();. If you read the API, it says 'Reads the next attribute in the stream and returns it as a String in the Java programming language.' . So if you have three attributes inside the object, then use the method three times like this
id = stream.readString();
name = stream.readString();
designation = stream.readString();
Another point is well mentioned in that post; Regarding the datatypes of attributes inside the object. If there are type mismatch, you get internal representation errors.
eg: correct way:
SRecord.java
public String id; \\varchar in plsql procedure
public String name; \\varchar in plsql procedure

How to write Spring Data method name to retrieve all elements in a column?

Suppose I have the class:
#Entity
public class Bean {
#Id
private String beanId;
//other fields & setters and getters
}
And the corresponding Spring Data JPA repository, where I want to have in a List<String> all the beanIds.
#RepositoryDefinition(domainClass = Bean.class, idClass = String.class)
public interface BeanRepository {
#Query("select b.beanId from Bean b")
List<String> findAllBeanId();
}
As written above, everything works as expected; but this is a simple operation and I do not want to write a query explicitly. What should the name of the method be such that Spring Data can parse it and obtain the above mentioned query (or the same functionality). I have searched in both the reference documentation as two books I have on Spring Data. The above name (findAllBeanId) and others that I have tried (findBeanId, findBeanBeanId etc.) throw the following exception as root cause:
org.springframework.data.mapping.PropertyReferenceException: No property find found for type Trade
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:75)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:327)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:353)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:353)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:307)
at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:271)
at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:245)
at org.springframework.data.repository.query.parser.Part.<init>(Part.java:72)
at org.springframework.data.repository.query.parser.PartTree$OrPart.<init>(PartTree.java:180)
at org.springframework.data.repository.query.parser.PartTree$Predicate.buildTree(PartTree.java:260)
at org.springframework.data.repository.query.parser.PartTree$Predicate.<init>(PartTree.java:240)
at org.springframework.data.repository.query.parser.PartTree.<init>(PartTree.java:68)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:57)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:90)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:162)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:68)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:279)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:147)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:153)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:43)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)
... 22 more
In the Spring docs: http://static.springsource.org/spring-data/jpa/docs/1.3.0.RELEASE/reference/html/jpa.repositories.html there is nothing about getting only particular column/property from entity by query generated from method name. So I think that currently it is not possible.
The code you showed works/should work as expected. It's simply not causing the exception you see :).
The exception is referring to a Trade, which seems to indicate that you have a repository for Trade somewhere which seems to refer to a missing property. The code you've shown is definitely not the one causing the exception. This can effectively not be the case as you're defining the query manually so that the query derivation mechanism doesn't even kick in for the repo you've shown.
I've pushed a test case for you to see this in action.

Categories

Resources