#Query annotation with custom Class Spring Boot Java [closed] - java

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
question for JPA-Users, is the following somehow possible?
#Query(value = "SELECT * FROM Users u WHERE u.status = :sampleClass.status",
nativeQuery = true)
List<SampleClass> findBySampleClass(SampleClass sampleClass);
Notice the way I am trying to access SampleClass in the #Query annotation. Wasn't able to get this going, instead went for Criteria and constructed my query old-school.

There should not be any space in between = and : so change the query to below one.
SELECT * FROM Users u WHERE u.status =: sampleClass.status
Also as your mathod name findBySampleClass, you are trying to find based on SampleClass then why only passing one parameter of SampleClass instead of object??
see this for much clarity.
You can use like below also by indexing parameters like ?1.
#Query(value = "select id,name,roll_no from USER_INFO_TEST where rollNo = ?1", nativeQuery = true)
ArrayList<IUserProjection> findUserUsingRollNo(String rollNo);
Refer : Quote from Spring Data JPA reference docs.
Also, see this section on how to do it with a named native query.

What you are trying to do is not possible because in native queries you have support just for binding direct variables, you can not do fancy access on objects. You could do something similar to your attempt by using either JPQL or HQL (in case you are using hibernate as an ORM provider).
But there is a problem on higher level. You are calling your method findBySampleClass, you are passing SampleClass as an input parameter and expect SampleClass as an output.This means that you would be returning the object itself from semantic point of view. This is suspicious. But going further on, you are doing something different inside the actual query specfication; you are using the status property of the object you passed in. This deviates from the convention that the method name should say what the query will be. In this case the correct(and most natural) way to go is to pass the status as a parameter, rename the method to findBySampleClassStatus or findByStatus.

You could use the approach as mentioned in the JPA docs:
#Query(value = "SELECT * FROM Users u WHERE u.status = ?1",
nativeQuery = true)
List<SampleClass> findBySampleClass(String status);
Please refer to the official docs for further options : https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#_native_queries
Not sure why you are trying to pass the whole object though.Would you care to explain?

Related

Use of Optional return types for custom Spring Repositories

I have been writing a custom JPA Query to handle a complicated query that may or may not return a result.
Note: I am using a simple query as an example for this question.
Example 1:
#Query(value = "SELECT TOP(1) e FROM Employee e WHERE e.NAME = :name", nativeQuery = true)
Employee getEmployeeByName(#Param("name") String employeeName);
Example 2:
#Query(value = "SELECT s FROM Student s WHERE s.CLASS = :class", nativeQuery = true)
List<Student> getStudentsByClass(#Param("class") String className);
In both these examples is it recommended to use Optional as a return type (as shown below), since there could be cases where either we don't get an Employee or Student?
#Query(value = "SELECT TOP(1) e FROM Employee e WHERE e.NAME = :name", nativeQuery = true)
Optional<Employee> getEmployeeByName(#Param("name") String employeeName);
#Query(value = "SELECT s FROM Student s WHERE s.CLASS = :class", nativeQuery = true)
Optional<List<Student>> getStudentsByClass(#Param("class") String className);
I have tried both the ways but just want to understand if this is one of the original use-case for introducing Optional.
Optional is a core Java class, so it was not introduced for anything to do with Spring or JPA, but the practical answer to your question is "yes." Spring/Hibernate/JPA support it for cases like your first example. It not only provides some convenient methods to act based on the presence or absence of a value (such as orElse()), but also as a form of contract that clearly indicates the return can be null. Returning Optinal makes it clear to callers that it's possible for the result to be empty/missing/absent - in other words, null.
Having said that, returning Optional<List<>>, as in your Example 2, doesn't make sense; a query method that returns a collection of objects would return an empty collection, not null. So there's no need to use Optional in that case.
It is not required to use Optional with List<T> since Spring Data will return an empty list (zero size) if nothing found.
However, if you return a single object, it is possible that there will be no match and then you will get null instead of object. And further you will possibly get NullPointerException trying to invoke it's methods.
It's up to you to use Optional with single object or don't. Because there may be situations depending on your business, if for example you're totally sure that desired object will be found (for example, some constants), then you may not use Optional. But in cases when there is any chance that object may be not found, surely use Optional and check if your actual object is present.
This is the typical approach.

fetching single column values in JPA

I want to get all the values from a particular column in JPA and store all values into a list. currently, I am using the below approach but I am getting records in something else format.can someone please help me out
Query q1 = factory.createNativeQuery("select * from booking_attendee where booking_id="+id);
List<String> em1=q1.getResultList();
return em1;
query otput
em=[[Ljava.lang.Object;#68606667, [Ljava.lang.Object;#2cd7f99a, [Ljava.lang.Object;#137a5a5, [Ljava.lang.Object;#a45cc1c, [Ljava.lang.Object;#61fdc06d, [Ljava.lang.Object;#72f5eee1, [Ljava.lang.Object;#4e536797]
If you want to create a native query for this, it is more about how to solve this in SQL. You do not say SELECT * which means all columns. You would have to say SELECT your_column_name to select only a specific column.
Query q1 = factory.createNativeQuery("SELECT your_column FROM booking_attendee");
List<String> em1 = q1.getResultList();
The WHERE clause could and should be defined with the parameter binding of JPA. There are several advantages concerning performance and SQL injection.
Named parameter binding is special to the persistence provider (e.g. Hibernate). The common way for JPA is using ? to let your code be portable to other providers.
Query q1 = factory.createNativeQuery("SELECT your_column FROM booking_attendee b WHERE b.booking_id = ?");
q1.setParameter(1, id);
List<String> em1 = q1.getResultList();
Native queries offer the possibilities to use original SQL. Like this, some features which are specific for your database could be used with this. Nevertheless, if you do not have very specific SQL code, you should also have a look in JPQL, the specific query language of JPA, and the JPA Criteria API which offers advantages when you want to refactor your code, shows errors during compile time and makes the dynamic creation of queries easier.

Is there any way to use table name from #params in #query annotation in Hibernate? [duplicate]

This question already has answers here:
How to replace table name with parameter value while using Spring Data JPA nativeQuery
(5 answers)
Closed 3 years ago.
I am using Hibernate and Spring data JPA to build a web project.
In Eclipse-Link we can use native query like
String tableName = "sometablename";
String query = "SELECT * FROM " +tableName +"WHERE id > 10";
In Hibernate I am using #Query annotation
#Query(value = "SELECT COUNT(r.id) as resultsCount FROM #{#resultTable} r WHERE r.customerId= :customerId AND r.is_deleted=0 AND r.is_ignored=0 ", nativeQuery = true)
Integer getResultsCount(#Param("customerId") int customerId,
#Param("resultTable") String resultTable);
I tried #{#resultTable} but this is getting replaced as a string with quote and i am getting an exception that
You have a error in your SQL Syntax
I want to use table name dynamically from params. Is it possible? And if yes, Please tell me how?
It's not possible, #org.springframework.data.jpa.repository.Query takes only jpql, you cannot pass the name of the table since it's not recognized as any entity.
It states the javadoc of Query:
/**
* Defines the JPA query to be executed when the annotated method is called.
*/
String value() default "";
The best solution would be not to pass tablename as string, but resolve it using for example inheritance (link) or rebuild your datamodel somehow. As a quick and dirty solution I would suggest creating a custom repository and using EntityManager.createNativeQuery and pass the sql there. But remember to validate the query you're composing (validate user input, use enums for table names), because it can lead to sql injection.
Your own observations actually answer your question:
I tried #{#resultTable} but this is getting replaced as a string with quote and i am getting an exception
The placeholders which are used inside the #Query query string are intended to filled with literal values. Hence, the table name ended up appearing as literal string, inside single quotes. This means that behind the scenes #Query and Spring are probably using a JDBC prepared statement. It is not possible to bind the name of the table. Allowing this would be a major security hole.
The only possible workaround here would be to concatenate your query string together, and then trying to use that string with #Query. But note that this would not be a safe option.
What you are doing is wrong, you are mixing business logic into DAO layer, i suggest you create two DAO's, each one with its own table and query, then into the business/service layer call the desired one.

Cannot get update query to work with Hibernate

I have a simple update query just to check if update query works:
this.openDBTransaction();
Query updateQuery = session.createQuery(
"UPDATE User AS usr SET usr.failedLogins=666 WHERE usr.id='USER_3000'"
);
int result = updateQuery.executeUpdate();
this.closeDBTransaction();
but somehow DB is not update with desired value. result came as 1 so something took place but for sure not update query.
Any clue what is going on?
You should use #Transactional annotation so that the compiler knows that the transaction is manipulating the database, thus permits to perform Data Manipulation queries or it will simply execute it as a Data Definition Language query.
Look at the code snippet below, for example,
#Transactional
public Employee editEmployee(Employee employee) { //employee is the data you got through post
return entityManager.merge(e1);
}
Also, the best practice is to always implement Data Access Object Interface and its implementation and define your queries in the implementation.
I hope this helps.

ObjectDB Select query with too few parameters

I'm trying to populate a tsp page based on what parameter was passed to it. Currently I'm using the title of the blog post, I don't like this but I wanted proof of concept and had difficulties obtaining the object id. So, I'm temporarily passing in the title string with a view to change it later.
My query is throwing a too few arguments exception, none of the examples I've come across have lead me to believe that there is something missing so I'm slightly confused. The call is:
#Transactional
public objectName getObjectByTitle(String title){
TypedQuery< objectName > query = em.createQuery(
"SELECT b FROM tableName AS b WHERE b.title = :title",
objectName.class);
return query.getSingleResult();
}
with the error being:
com.objectdb.o._PersistenceException: Attempt to execute a query with too few arguments
It might be one of those cases where I'm looking too hard but I've scoured the objected site and cannot find a solution. Any help is appreciated.
You defined a named parameter in the query, and didn't bother setting its value (using setParameter), even though you went to the trouble of passing "title" in to the method ...

Categories