Using Variable in Spring #Query - java

I'm having an issue getting my variable to work in my #Query. Below is the code
#Query("SELECT new User(userId, userEmail, userForename, userSurname, userMiddleName) "
+ "FROM User "
+ "ORDER BY :orderBy DESC")
public List<User> findAllBy(#Param("orderBy") String orderBy);
I know the variable I want is being passed in properly. At the moment this is just returning all the results ordered by userId. If I hard code the value which is being passed into this function then it correctly returns the results ordered by userEmail (which is what is being passed in). Any suggestions would be great.

Unfortunately, you can pass the parameters only to "conditional" clauses (like "where") due to underlying JDBC restrictions.
Instead of it consider to use overloaded PageRequest "of" method.
public static PageRequest of(int page,
int size,
Sort.Direction direction,
String... properties)

Related

How to return 'Integer' type for ResponseEntity<> and get the result on api page?

I just started working on a spring-boot application (Java) in MVC. In the controller class,
#GetMapping("/{id}/age")
public ResponseEntity<Integer> getStudentAge(#PathVariable Long id) {
Integer age = studentService.retrieveAgeById(id);
return new ResponseEntity<Integer>(age, HttpStatus.OK);
}
With a simple SQL data, as simple as this:
INSERT INTO student (id, name, age, gender) VALUES (1, 'Rio', 5, 'Male');
When I run the application and check the webpage with path: http://localhost:8080/1/age
I get a response in which age is NOT printed:
Result
The Query used in repository package is:
#Query("select d.id, d.age from Student d where d.id=:id")
Integer findAgeById(Long id);
Also, the requests for student name, gender(Type:String) is successful. But, the request for age (Type:Integer) is not producing similar results.
Adapt your SELECT query to:
#Query("select d.age from Student d where d.id = :id")
Integer findAgeById(#Param("id") Long id);
The query in your question will map the first field from your SELECT to the type of your method, an as you can see, that is your Student ID.
You also could to provide a #Param in your method declaration, because according to this guide:
A query with named parameters is easier to read and is less error-prone in case the query needs to be refactored.
If you want to extact both the ID and the age, you can return your entire Student entity, and use that. Another option is to use a projection but I really don't believe your use-case is advanced enough to benefit from this.

Can I return custom object from UPDATE query instead of integer?

Executing UPDATE query it always returns INT number of rows affected.
Maybe there is some way to return for example list of all objects which where updated or first one updated?
I have #Entity SecurityPolicy and want this object back after update.
So here is my code:
#Modifying
#Query(value = "UPDATE security_policy s set max_fail_sign_in =:maxFailSignIn," +
" min_password_length =:minPasswordLength," +
" capital_in_password =:capitalInPassword," +
" digit_in_password =:digitInPassword," +
nativeQuery = true)
SecurityPolicy updateSecurityPolicy(#Param("maxFailSignIn") Integer maxFailSignIn,
#Param("minPasswordLength") Integer minPasswordLength,
#Param("capitalInPassword") Boolean capitalInPassword,
#Param("digitInPassword") Boolean digitInPassword);
When using #Modifying it tells me that result should be Int, and without it - could not extract ResultSet.
I know with spring data you can easily save or update some entity and get it as a result, so I want achieve something similar.
No you cannot as the integer response is not generated by framework but by RDBMS itself (SQL spec). That integer says how many rows were modified by query.
Since you don't want to use JPA to do this, you will have to SELECT that row after update, like you would do with plain SQL.

Use SELECT NEW CONSTRUCTOR with parameter not from tables

I'm using something like this now in my HQL:
"SELECT NEW com.somepackage.dto.SomeClass(myObj) "
But now I want to add a boolean parameter to constructor.
I've added it to my DAO method with HQL and to constructor of my dto object:
"SELECT NEW com.somepackage.dto.SomeClass(myObj, :param) "
...
.setParameter("param", param)
After adding parameter I got an exception:
Unable to locate appropriate constructor on class
Is there a way to add param to constructor? Or I was made something wrong?
Thx for your replies and sorry for my English.
Update
(Simple copy of my SomeClass):
public class SomeClass extends SomeClassParent {
private final String someParam;
private final List<MyObject> myObjects;
public SomeClass(MyObject myObject) {
super(myObject.getFirstField,
myObject.getSecondField, ...);
this.someParam = myObject.getSomeParamValue();
StringBuilder bodyBuilder = new StringBuilder();
...
I want it to be
public SomeClass(MyObject myObject, boolean myBoolean) {
I don't know exactly what was the problem with boolean, but now I'm using String parameter against boolean and use it like this:
"SELECT NEW com.somepackage.dto.SomeClass(myObj, '" + param + "') "...
setParameter doesn't want to work with String, because it requires quotes to be result string like this:
"SELECT NEW com.somepackage.dto.SomeClass(myObj, 'Some string') "...
against
"SELECT NEW com.somepackage.dto.SomeClass(myObj, Some string) "...
Ok, what it wasn't clear to me was if you had the SomeClass definition, you can define as many constructors as you want as long as they have different types, or quantities of parameters passed by.
It can be see as a kind of override (although it is not!)
It's up to you to define it, and do whatever you want with that Boolean, in fact you can copy and paste the original constructor (just leave that one there, don't erase it) with the Boolean's addition and it would be as valid as the previous one.
I'm pretty sure that your problem is with the boolean field. I worked with Hibernate HQL and I had a similar problem with a field. At the end I realized that I have to use nullable files for every field.
So, I think this problem could be solved turning boolean primitive field into Boolean Object class.
I'm having a similar problem with constructing a result entity using a query parameter as a constructor argument - like "select new com.example.ResultType(t.id, ?1) from Table t where ...". It seems such bare query params just get ignored when looking for constructors - in that example it would look for a constructor that takes a single argument matching t.id.
The only work-around I've found so far is to wrap the parameter with a cast (or some other non-trivial expression), e.g. "select new com.example.ResultType(t.id, cast(?1 as string)) ...".

MySQL query called from Java - configure the "WHERE" parameter

I am connecting to a mysql db from standalone java application. My application uses several filters that determine, which data will be selected from db.
In some cases, I would like to construct the select command in a way, that its "WHERE" parameter is ignored and selects all values from db instead.
This is my example:
String query = "SELECT * from messages WHERE type='" + type + "' ORDER BY id DESC";
The variable type can contain some specific type that matches the Varchar of the items in my db. However, a user can set the type to "all values" (or something like that, I hope this is clear enough), in which case, the query would select ALL values from db (it will ignore the where parameter).
I know I could do this by simply putting some if statements in my code and call a different select command in every branch, but this would be highly ineffective in case that several specifications (attributes inside WHERE parameter) are used.
For example:
String query = "SELECT * from messages WHERE type='" + type + "' AND time='" + time + "' ORDER BY id DESC";
I am not sure whether this is even possible to do. If not, sorry about dumm question... Thanks in advance!
I think you will have to do it through code, nothing in SQL to do what you want to do. Typically, people use ORM like Hibernate and construct the query in more secure way (to avoid SQL injection) instead of using String concatenation.
This is how it is done in Hibernate: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querycriteria.html#querycriteria-narrowing
You could create a Type class and a Time class and so on. This classes would contain a function called getSQL() which returns the type or in case of all types "".
The WHERE clause let you filter the dataset according to several predicates. Not specifying it means no filtering, hence all possible values for any predicates you would add.
If you consider the WHERE clause as a predicate, and predicates as either sets of predicates or atomic predicates, you can easily produce your clause by walking through the predicate nest and generate the needed string. If the string comes back empty, just elide the WHERE clause altogether.
interface Predicate {
public String toString();
}
// further derive this class for specific predicates
class AtomPredicate implements Predicate {
public AtomPredicate(){}
public toString() {}
}
// basic predicate set
class SetPredicate implements Predicate {
public SetPredicate(String connector){this.connector = connector;}
private ArrayList<Predicate> set;
private String connector;
public toString(){
String res, tmp;
int i;
if (set.size() == 0) return "";
while (res.size() == 0 && i < set.size()) {
res = set[i++].toString();
}
for (; i < set.size(); ++i) {
tmp = set[i].toString();
if (tmp.size() > 0)
res += connector + " " + tmp;
}
if (res.size() > 0)
return "(" + res + ")";
}
class WhereClause {
public WhereClause() {}
private Predicate predicate;
public toString(){
String res = predicate.toString();
if (res.size() > 0) return "WHERE " + res;
return "";
}
}
You can start from that basic outline, and expand as needed. You should however try to look for an existing solution first, like the jboss library linked in another answer, to avoid reinventing the wheel.

setMaxResults for Spring-Data-JPA annotation?

I am trying to incorporate Spring-Data-JPA into my project.
One thing that confuses me is how do I achieve setMaxResults(n) by annotation ?
for example, my code:
public interface UserRepository extends CrudRepository<User , Long>
{
#Query(value="From User u where u.otherObj = ?1 ")
public User findByOtherObj(OtherObj otherObj);
}
I only need to return one (and only one) User from otherObj, but I cannot find a way to annotate the maxResults. Can somebody give me a hint ?
(mysql complains :
com.mysql.jdbc.JDBC4PreparedStatement#5add5415: select user0_.id as id100_, user0_.created as created100_ from User user0_ where user0_.id=2 limit ** NOT SPECIFIED **
WARN util.JDBCExceptionReporter - SQL Error: 0, SQLState: 07001
ERROR util.JDBCExceptionReporter - No value specified for parameter 2
)
I found a link : https://jira.springsource.org/browse/DATAJPA-147,
I tried but failed. It seems not possible now?
Why is such an important feature not built into Spring-Data?
If I implement this feature manually:
public class UserRepositoryImpl implements UserRepository
I have to implement tons of predefined methods in CrudRepository, this would be terrible.
environments : spring-3.1 , spring-data-jpa-1.0.3.RELEASE.jar , spring-data-commons-core-1.1.0.RELEASE.jar
As of Spring Data JPA 1.7.0 (Evans release train).
You can use the newly introduced Top and First keywords that allow you to define query methods like this:
findTop10ByLastnameOrderByFirstnameAsc(String lastname);
Spring Data will automatically limit the results to the number you defined (defaulting to 1 if omitted). Note that the ordering of the results becomes relevant here (either through an OrderBy clause as seen in the example or by handing a Sort parameter into the method). Read more on that in the blog post covering new features of the Spring Data Evans release train or in the documentation.
For previous versions
To retrieve only slices of data, Spring Data uses the pagination abstraction which comes with a Pageable interface on the requesting side as well as a Page abstraction on the result side of things. So you could start with
public interface UserRepository extends Repository<User, Long> {
List<User> findByUsername(String username, Pageable pageable);
}
and use it like this:
Pageable topTen = new PageRequest(0, 10);
List<User> result = repository.findByUsername("Matthews", topTen);
If you need to know the context of the result (which page is it actually? is it the first one? how many are there in total?), use Page as return type:
public interface UserRepository extends Repository<User, Long> {
Page<User> findByUsername(String username, Pageable pageable);
}
The client code can then do something like this:
Pageable topTen = new PageRequest(0, 10);
Page<User> result = repository.findByUsername("Matthews", topTen);
Assert.assertThat(result.isFirstPage(), is(true));
Not that we will trigger a count projection of the actual query to be executed in case you use Page as return type as we need to find out how many elements there are in total to calculate the metadata. Beyond that, be sure you actually equip the PageRequest with sorting information to get stable results. Otherwise you might trigger the query twice and get different results even without the data having changed underneath.
If you are using Java 8 and Spring Data 1.7.0, you can use default methods if you want to combine a #Query annotation with setting maximum results:
public interface UserRepository extends PagingAndSortingRepository<User,Long> {
#Query("from User u where ...")
List<User> findAllUsersWhereFoo(#Param("foo") Foo foo, Pageable pageable);
default List<User> findTop10UsersWhereFoo(Foo foo) {
return findAllUsersWhereFoo(foo, new PageRequest(0,10));
}
}
There is a way you can provide the equivalent of "a setMaxResults(n) by annotation" like in the following:
public interface ISomething extends JpaRepository<XYZ, Long>
{
#Query("FROM XYZ a WHERE a.eventDateTime < :before ORDER BY a.eventDateTime DESC")
List<XYZ> findXYZRecords(#Param("before") Date before, Pageable pageable);
}
This should do the trick, when a pageable is sent as parameter.
For instance to fetch the first 10 records you need to set pageable to this value:
new PageRequest(0, 10)
Use Spring Data Evans (1.7.0 RELEASE)
the new release of Spring Data JPA with another list of modules together called Evans has the feature of using keywords Top20 and First to limit the query result,
so you could now write
List<User> findTop20ByLastname(String lastname, Sort sort);
or
List<User> findTop20ByLastnameOrderByIdDesc(String lastname);
or for a single result
List<User> findFirstByLastnameOrderByIdDesc(String lastname);
Best choice for me is native query:
#Query(value="SELECT * FROM users WHERE other_obj = ?1 LIMIT 1", nativeQuery = true)
User findByOhterObj(OtherObj otherObj);
new PageRequest(0,10) doesn't work in newer Spring versions (I am using 2.2.1.RELEASE). Basically, the constructor got an additional parameter as Sort type. Moreover, the constructor is protected so you have to either use one of its child classes or call its of static method:
PageRequest.of(0, 10, Sort.sort(User.class).by(User::getFirstName).ascending()))
You can also omit the use of Sort parameter and implicitly user the default sort (sort by pk, etc.):
PageRequest.of(0, 10)
Your function declaration should be something like this:
List<User> findByUsername(String username, Pageable pageable)
and the function will be:
userRepository.findByUsername("Abbas", PageRequest.of(0,10, Sort.sort(User.class).by(User::getLastName).ascending());
It's also posible using #QueryHints. Example bellow uses org.eclipse.persistence.config.QueryHints#JDBC_MAX_ROWS
#Query("SELECT u FROM User u WHERE .....")
#QueryHints(#QueryHint(name = JDBC_MAX_ROWS, value = "1"))
Voter findUser();
If your class #Repository extends JpaRepository you can use the example below.
int limited = 100;
Pageable pageable = new PageRequest(0,limited);
Page<Transaction> transactionsPage = transactionRepository.findAll(specification, pageable);
return transactionsPage.getContent();
getContent return a List<Transaction>.
Use
Pageable pageable = PageRequest.of(0,1);
Page<Transaction> transactionsPage = transactionRepository.findAll(specification, pageable);
return transactionsPage.getContent();

Categories

Resources