jpql constructor expression query org.postgresql.util.PSQLException - spring

i am using spring data jpa , i want to retrieve specific columns from postgres table, i followed couple of links to retrieve specific columns Spring JPA selecting specific columns
i found regarding jpql constructor expression query to retrieve specific columns,
i used the jpql expression on the respository class
public interface DeviceCrudRepository extends CrudRepository<Device,String>{
public Device findByBarcode(String barcode);
#Query(value = "select new com.hello.world.model.Device (d.deviceName,d.deviceType,d.deviceDescription ,d.deviceRentalStatus) from devicedetails d where d.scancode=:scancode", nativeQuery = true)
public Device findDeviceDetailsByBarcode(#Param("d.scancode") String d.scancode);
Model class is
i am using constructor too
public Device(String deviceName, String deviceType, String deviceDescription, String deviceRentalStatus) {
super();
this.deviceName = deviceName;
this.deviceType = deviceType;
this.deviceDescription = deviceDescription;
this.deviceRentalStatus = deviceRentalStatus;
}
I keep on getting exception
org.postgresql.util.PSQLException: ERROR: syntax error at or near "."
Position: 15
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2477) ~[postgresql-42.1.4.jar:42.1.4]
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2190) ~[postgresql-42.1.4.jar:42.1.4]
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:300) ~[postgresql-42.1.4.jar:42.1.4]
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:428) ~[postgresql-42.1.4.jar:42.1.4]
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:354) ~[postgresql-42.1.4.jar:42.1.4]
at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:169) ~[postgresql-42.1.4.jar:42.1.4]
at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:117) ~[postgresql-42.1.4.jar:42.1.4]
when i checked in logs
the query which was generated was
select new com.hello.world.model.Device (d.deviceName,d.deviceType,d.deviceDescription ,d.deviceRentalStatus) from devicedetails d where d.scancode=?
it's not able to translate the jpql expression into query,it's showing exception for package name, when i use the class name it's showing exception near the brackets, so i guess the jpql expression is not being translated
am i missing any dependency any help would be appreciated?

Because PGSQL is not JPQL it's normal that it do not understand your query :) Remove the nativeQuery = true.

Related

Using projection with a interface in `#Query` annotation with a native query gives "Target type is not an interface and no matching Converter found"

I have a table with a clob in an oracle 19 database which I try to fetch with a native query using a #Query annotation with a projection from a Spring boot 2.7.4 application. I get the following error message:
java.lang.UnsupportedOperationException: Cannot project jdk.proxy2.$Proxy281 implementing java.sql.Clob,org.hibernate.engine.jdbc.WrappedClob,java.io.Serializable to java.lang.String; Target type is not an interface and no matching Converter found
The query from my repository class:
#Query(
value = """
select submission_id as "submissionId", text as "textAnswer"
from answer
where answer_id = :answerId
""",
nativeQuery = true)
public MyDTO findDTO(Long answerId);
My interface which I use for the projection:
public interface MyDTO {
String getTextAnswer();
}
From my domain object annotated with #Entity:
private String textAnswer;
My testcase which reproduce which reproduce this error. If I comment out the line with a.getTextAnswer() it runs ok.
#Test
public void findFormPublishedAnswersInterfaceDTOById() {
FormPublishedAnswersInterfaceDTO a = answerRepository.findFormPublishedAnswersInterfaceDTOById(21540241L);
assertEquals("test", a.getTextAnswer());
}
I have tried different hints I found by the help of google :) like annotating private String textAnswer with #Lob, #Type(type = "text") and #Column(name = "text", columnDefinition = "CLOB") without any luck.
If you read the exception carefully, you should understand that the JDBC driver reports that the type is a Clob, so your DTO must look like this instead:
public interface MyDTO {
Clob getTextAnswer();
}

How to resolve No converter found capable of converting from type TupleBackedMap to type [com.example.dto.ExampleDto]

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.example.dto.ExampleDto]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:321) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:194) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:174) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.data.repository.query.ResultProcessor$ProjectingConverter.convert(ResultProcessor.java:293) ~[spring-data-commons-2.1.5.RELEASE.jar:2.1.5.RELEASE]
The above error is being thrown when I have a query that returns 2 values in a native JPA query is being used. I'm capturing the query response in the DTO below:
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class ExampleDto {
#Id
private String name1;
private int nameFlag;
}
And in a DAO class, I'm calling the native query as below. The query works in SQL Developer and returns 2 records. But when called as below , it throws the above error.
List<ExampleDto> getExampleDto = myJPARepository.
.findNameObject(uuid);
There is something wrong in the DTO class, which i need to change. Annotations? I'm not sure what is missing here, and try as I might , putting in #Entity annotation, #Data annotation , I'm not able to resolve this error when the query is called.
UPDATE: The native query associated with this is
#Query(value = "select name1, nameFlag from NameTable",
nativeQuery = true, name = "findNameObject where namekey = ?")
List<ExampleDto> findNameObject(
#Param("nameKey") UUID nameKey);
This is a bug: https://jira.spring.io/browse/DATAJPA-1714
You can either use JPQL with a constructor expression, an interface projection or a custom method implementation as a workaround.
Make sure, your Repository class is using same class, as you are using for extracting the records from native query. Example shown below for XYZDTO
#Repository
public interface XYZRepository extends JpaRepository <XYZDTO, Long> {
}

How return column from my DB using Java Spring JPQL

I have a very basic task. I want to return just one column from my table in my DB. Literally, I want the text from my category_name. This is my JPQL cod:|
#Transactional
#Modifying
#Query(value = "SELECT category_name FROM Category WHERE id=:id", nativeQuery = true)
String findName(#Param("id") long id);
And I have this error:
Modifying queries can only use void or int/Integer as return type!
As the error apparently states, you should use #Modifying annotation when you are actually updating/deleting the row. Since you are fetching data from already stored database, you can simply remove this annotation.
You should also remove #Transactional annotation.
https://dzone.com/articles/how-does-spring-transactional is an interesting article to know about how transactional annotation works and when it should be used.

Spring JPA Repository Sub-Query In Native Query

I am trying to run a native query on a repository method so that it returns the results with some counts. It was too complicated to do with JPQL, so I opted for a native query instead.
Repository
#RepositoryRestResource(collectionResourceRel="projects", path="projects")
interface ProjectRepository extends BaseRepository<Project, Long>, ProjectRepositoryCustom {
#Query(
value="SELECT p.id, p.user_id, p.title, p.description, p.created_on, p.version,(SELECT COUNT(0) FROM projectparts WHERE project_id = p.id) AS parts,(SELECT COUNT(0) FROM requests WHERE project_id = p.id) AS requests FROM projects AS p ORDER BY ?#{#pageable}",
countQuery="SELECT COUNT(0) FROM projects",
nativeQuery=true
)
Page<Project> findAll(Pageable pageable)
}
The entity has 2 properties annotated with #Transient so that the info is not persisted to the database. All the data comes back fine except the 2 transient properties which return null for the values. When I copy the query from the console and paste it in MySQL Workbench, the results are as expected and I see the counts that I need. Anyhow, not sure if there is anything else that needs to be done in order to get this native query to work as an annotation. I hard coded a value in the sub-query SELECT 55 FROM... just to see if it was a problem with the count and it still returned as null. I ran the query in Workbench and it works fine.
I've tried changing the transient property type from Integer, Long, BigInteger, long, int... and none of that made a difference. Since I'm using Groovy, I also tried def to let Groovy infer the type and that didn't work either.
I also tried running the project from the terminal instead and it still didn't work. I've tried it on a Mac and Linux and had no luck with displaying the results of the counts.
This will not work. You could use an SQLConstructorExpression however the returned instances would be unmanaged which is a major drawback.
An better option is to create a simple DB view which holds the pieces of summary info for the Project. You can them map the Project entity to both it's table and the associated summary view using the #SecondaryTable functionality of JPA.
https://en.wikibooks.org/wiki/Java_Persistence/Tables#Example_mapping_annotations_for_an_entity_with_multiple_tables
An added benefit is that you can sort and query on the summary values as for any other property.
Updated mapping:
#Entity
#Table(name = "projects")
#SecondaryTable(name = "projects_summary_vw")
public class Project{
//use Integer rather than int to avoid issue outlined here:
//http://stackoverflow.com/a/37160701/1356423
#Column(name = "parts", table = "projects_summary_vw",
insertable="false", updateable="false")
private Integer partsCount;
#Column(name = "requests", table = "requestsCount"
insertable="false", updateable="false")
private Integer requestsCount;
//other mappings as required
}
No Custom query required:
#RepositoryRestResource(collectionResourceRel="projects",
path="projects")
interface ProjectRepository extends BaseRepository<Project, Long>,
ProjectRepositoryCustom {
}
An alternative non-JPA compliant solution may be to use some vendor specific extension rather than a view. Hibernate for example has an #Formula annotation which could be used:
https://docs.jboss.org/hibernate/orm/5.1/javadocs/org/hibernate/annotations/Formula.html
#Entity
#Table(name = "projects")
public class Project{
#Formula("my count query as native sql")
private Integer partsCount;
#Formula("my count query as native sql")
private Integer requestsCount;
//other mappings as required
}

Spring Data: "delete by" is supported?

I am using Spring JPA for database access. I am able to find examples such as findByName and countByName, for which I dont have to write any method implementation. I am hoping to find examples for delete a group of records based on some condition.
Does Spring JPA support deleteByName-like delete? Any pointer is appreciated.
Regards and thanks.
Deprecated answer (Spring Data JPA <=1.6.x):
#Modifying annotation to the rescue. You will need to provide your custom SQL behaviour though.
public interface UserRepository extends JpaRepository<User, Long> {
#Modifying
#Query("delete from User u where u.firstName = ?1")
void deleteUsersByFirstName(String firstName);
}
Update:
In modern versions of Spring Data JPA (>=1.7.x) query derivation for delete, remove and count operations is accessible.
public interface UserRepository extends CrudRepository<User, Long> {
Long countByFirstName(String firstName);
Long deleteByFirstName(String firstName);
List<User> removeByFirstName(String firstName);
}
Derivation of delete queries using given method name is supported starting with version 1.6.0.RC1 of Spring Data JPA. The keywords remove and delete are supported. As return value one can choose between the number or a list of removed entities.
Long removeByLastname(String lastname);
List<User> deleteByLastname(String lastname);
2 ways:-
1st one Custom Query
#Modifying
#Query("delete from User where firstName = :firstName")
void deleteUsersByFirstName(#Param("firstName") String firstName);
2nd one JPA Query by method
List<User> deleteByLastname(String lastname);
When you go with query by method (2nd way) it will first do a get call
select * from user where last_name = :firstName
Then it will load it in a List
Then it will call delete id one by one
delete from user where id = 18
delete from user where id = 19
First fetch the list of object, then for loop to delete id one by one
But, the 1st option (custom query),
It's just a single query
It will delete wherever the value exists.
Since in 2nd option it is making multiple DB query, try to use the first option.
Go through this link too https://www.baeldung.com/spring-data-jpa-deleteby
If you take a look at the source code of Spring Data JPA, and particularly the PartTreeJpaQuery class, you will see that is tries to instantiate PartTree.
Inside that class the following regular expression
private static final Pattern PREFIX_TEMPLATE = Pattern.compile("^(find|read|get|count|query)(\\p{Lu}.*?)??By")
should indicate what is allowed and what's not.
Of course if you try to add such a method you will actually see that is does not work and you get the full stacktrace.
I should note that I was using looking at version 1.5.0.RELEASE of Spring Data JPA
If you will use pre defined delete methods as directly provided by spring JPA then below two queries will be execute by the framework.
First collect data(like id and other column) using by execute select query with delete query where clause.
then after getting resultSet of first query, second delete queries will be execute for all id(one by one)
Note : This is not optimized way for your application because many queries will be execute for single MYSQL delete query.
This is another optimized way for delete query code because only one delete query will execute by using below customized methods.
#NamedNativeQueries({
#NamedNativeQuery(name = "Abc.deleteByCreatedTimeBetween",
query = "DELETE FROM abc WHERE create_time BETWEEN ?1 AND ?2")
,
#NamedNativeQuery(name = "Abc.getByMaxId",
query = "SELECT max(id) from abc")
})
#Entity
public class Abc implements Serializable {
}
#Repository
public interface AbcRepository extends CrudRepository {
int getByMaxId();
#Transactional
#Modifying
void deleteByCreatedTimeBetween(String startDate, String endDate);
}
It works just
import org.springframework.transaction.annotation.Transactional;
#Transactional
Long removeAddressByCity(String city);
Yes , deleteBy method is supported
To use it you need to annotate method with #Transactional
here follows my 2 cents. You can also use native queries, like:
#Modifying
#Query(value="delete from rreo r where r.cod_ibge = ?1 and r.exercicio= ?2", nativeQuery = true)
void deleteByParameters(Integer codIbge, Integer exercicio);
#Query(value = "delete from addresses u where u.ADDRESS_ID LIKE %:addressId%", nativeQuery = true)
void deleteAddressByAddressId(#Param("addressId") String addressId);

Resources