setMaxResults for Spring-Data-JPA annotation? - java

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();

Related

Using registerFunction in JPA workaround

I am trying to implement a HQL query in Jpa which uses an MySQL DATE_ADD method to increase data, as apparently Jpa doesn't seem to support native add operator for timestamp.
I read some guides which suggest a class that extends the SQLDialect, and use registerFunction to register an SQL function which can be used in a query specified by that class. However, I want to instead use this SQL function inside my Jpa class instead of separate class. Does anyone know the solution to my problem? This is my code attempt, feel me to ask me for more information.
repository class
public interface CustomerRepository extends JpaRepository<Customer, Long> {
List<Conference> findByCreatorIdentifier(String creatorIdentifier);
#Query("SELECT c.id, FROM Customer c WHERE c.date > current_timestamp() AND c.creatorIdentifier = ?1 ORDER BY c.date")
List<Conference> findByRecentDate(String email);
//unsuccessful SQL query
// #Query("SELECT c.id FROM Customer c WHERE DATE_ADD(c.date, INTERVAL +8 HOUR) > current_timestamp() AND c.creatorIdentifier = ?1 ORDER BY c.date")
// List<Customer> findByRecentDate(String email);
}
Update
The Customer entity class stores a Date attribute as date, and is stored in UTC-time - 8h. I'm trying to get the mostRecentCustomer with reference to the current system time, which is intuitively LocalDateTime.now(), but the query doesn't seem to work too. I'm not sure if I am allowed to change the code's database schema.
registerFunction class
public class MySQLServerDialect extends MySQL5InnoDBDialect {
public MySQLServerDialect() {
registerFunction("conference.DATE_ADD", new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, "DATE_ADD"));
// registerFunction("conference.DATE_ADD", new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, "DATE_ADD"));
// registerFunction("addhours", new VarArgsSQLFunction(TimestampType.INSTANCE, "dateadd(minute,", ",", ")"));
}
}
You are not writing what the problem is you are facing, but I don't think that using dots in the function name is allowed. Use the following:
registerFunction("DATE_ADD", new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, "DATE_ADD"));
Now the problem with MySQL is, that it requires an interval literal which requires a special keyword. There is no way to passthrough keywords from HQL to SQL directly, so you will have to use a special function e.g. DATE_ADD_HOURS:
registerFunction("DATE_ADD_HOURS", new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, "DATE_ADD(?1, INTERVAL ?2 HOUR"));
and then use
#Query("SELECT c.id, FROM Customer c WHERE DATE_ADD_HOURS(c.date, 8) > current_timestamp() AND c.creatorIdentifier = ?1 ORDER BY c.date")
List<Conference> findByRecentDate(String email);

Spring Data JPA and Exists query

I'm using Spring Data JPA (with Hibernate as my JPA provider) and want to define an exists method with a HQL query attached:
public interface MyEntityRepository extends CrudRepository<MyEntity, String> {
#Query("select count(e) from MyEntity e where ...")
public boolean existsIfBlaBla(#Param("id") String id);
}
When I run this query, I get a java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Boolean.
How does the HQL query have to look like to make this work? I know I could simply return a Long value and afterwards check in my Java code if count > 0, but that workaround shouldn't be necessary, right?
Spring Data JPA 1.11 now supports the exists projection in repository query derivation.
See documentation here.
In your case the following will work:
public interface MyEntityRepository extends CrudRepository<MyEntity, String> {
boolean existsByFoo(String foo);
}
I think you can simply change the query to return boolean as
#Query("select count(e)>0 from MyEntity e where ...")
PS:
If you are checking exists based on Primary key value CrudRepository already have exists(id) method.
in my case it didn't work like following
#Query("select count(e)>0 from MyEntity e where ...")
You can return it as boolean value with following
#Query(value = "SELECT CASE WHEN count(pl)> 0 THEN true ELSE false END FROM PostboxLabel pl ...")
It's gotten a lot easier these days!
#Repository
public interface PageRepository extends JpaRepository<Page, UUID> {
Boolean existsByName(String name); //Checks if there are any records by name
Boolean existsBy(); // Checks if there are any records whatsoever
}
Since Spring data 1.12 you can use the query by Example functionnality by extending the QueryByExampleExecutor interface (The JpaRepositoryalready extends it).
Then you can use this query (among others) :
<S extends T> boolean exists(Example<S> example);
Consider an entity MyEntity which as a property name, you want to know if an entity with that name exists, ignoring case, then the call to this method can look like this :
//The ExampleMatcher is immutable and can be static I think
ExampleMatcher NAME_MATCHER = ExampleMatcher.matching()
.withMatcher("name", GenericPropertyMatchers.ignoreCase());
Example<MyEntity> example = Example.<MyEntity>of(new MyEntity("example name"), NAME_MATCHER);
boolean exists = myEntityRepository.exists(example);
Apart from the accepted answer, I'm suggesting another alternative.
Use QueryDSL, create a predicate and use the exists() method that accepts a predicate and returns Boolean.
One advantage with QueryDSL is you can use the predicate for complicated where clauses.
You can use Case expression for returning a boolean in your select query like below.
#Query("SELECT CASE WHEN count(e) > 0 THEN true ELSE false END FROM MyEntity e where e.my_column = ?1")
Spring data provides method for checking the existence of a row using field:
example: boolean existsByEmployeeIdAndEmployeeName(String employeeId, String employeeName);
You can use .exists (return boolean) in jpaRepository.
if(commercialRuleMsisdnRepo.exists(commercialRuleMsisdn.getRuleId())!=true){
jsRespon.setStatusDescription("SUCCESS ADD TO DB");
}else{
jsRespon.setStatusCode("ID already exists is database");
}

Spring data JPA query behaves unexpectedly

I have these entities:
public class Order_status_sas {
private Order_sas order;
private Date lastModified;
...
}
public class Order_sas {
private long id;
...
}
My CrudRepository:
public interface StatusesWareHouseRepository extends CrudRepository<Order_status_sas, Long> {
Order_status_sas findFirstByOrderIdOrderByLastModifiedDesc(long id);
}
I expect that method findFirstByOrderIdOrderByLastModifiedDesc would return first row from table Order_status_sas, where order.id = <some_id> sorted by field lastModified, but in log I see this query:
Hibernate: select ...
from order_status_sas a
left outer join orders_sas b
on a.order_id=b.id
where b.id=?
order by a.last_modified desc
This query does not return me one row, but returns a list of rows. It seems that Spring Data do not look at word First in my method name. Also, I get an Exception:
org.springframework.dao.IncorrectResultSizeDataAccessException:
result returns more than one elements; nested exception is javax.persistence.NonUniqueResultException: result returns more than one elements
Please, tell me what I am doing wrong and how can I achieve my purpose?
EDITED:
I edited my StatusesWareHouseRepository with custom query:
#Query("select s from Order_status_sas s where s.order.id = ?1 order by s.lastModified desc limit 1")
Order_status_sas findFirstByOrderIdOrderByLastModifiedDesc(long id);
but the query, executed by Hibernate, haven't changed. It looks like this:
select ...
from order_status_sas s
where s.order_id=?
order by s.last_modified desc
OK, I understood #PriduNeemre point. Lets leave the DB model and come back to the JPA question. Here is another example:
#Entity
public class Client {
....
}
public interface ClientRepository extends CrudRepository<Client, Integer> {
Client findFirstByOrderByNameDesc();
}
Hibernate query still looks like this:
select ...
from clients c
order by c.name desc
Have you tried adding a #Query annotation (see here) on top of your findFirstByOrderIdOrderByLastModifiedDesc(..) method to specify the expected behaviour by hand? A (non-related) example on how this could work:
public interface InvoiceRepository extends JpaRepository<Invoice, Long> {
#Query("SELECT I FROM Invoice I JOIN I.customer C JOIN C.user U WHERE
U.username = :username")
public List<Invoice> findInvoicesByUsername(#Param("username")
String username);
}
Note that the query language used in the annotation body is in fact JPQL, not SQL. For more examples on the #Query annotation, see the Spring Data docs here.
PS: I'm also having conflicted feelings about your domain object structure, i.e. whether an instance of Order_sas should really be stored in an instance of Order_status_sas - shouldn't it be the other way around? Normally you would want to store the reference objects in your main domain object, not vice versa. (There's a slight possibility that I'm just not getting it right, though.)
EDIT: I would even go as far as to say that considering your current domain model, Hibernate is doing everything right except missing a LIMIT 1 clause to limit the expected resultset to one single row. The query itself is extremely inefficient, though, and could be improved by fixing your skewed domain model.

Querying Data with a predicate and paging using Spring Data JPA

I have had the misfortune of working in Java for some time, coming from the .net world. Ranting aside, I am simply looking to implement a Repository that can handle use of predicates and must have pagination. I am unable to find a good way to do this.
// IContactRepository.java
public interface IContactRepository extends Repository<Contact,Long> {
}
// Application.java
contactRepo.findAll(predicate, new PageRequest(0,10));
I want to to be able to find contacts with contact name containing search term or contact phone number containing search term and then get first 10 matches.
In the .net world, if I was not using an orm I would use sql server's awesome TSQL to get what I want but stuck with Oracle here. I would otherwise use some ORM and pass a lambda to the query function as predicate.
In my configuration I am also using JPA and spring. (FOR STATIC PREDICATES. If you want to add predicates(search terms) dynamically please let me know.)
// IContactRepository.java
public interface IContactRepository extends CrudRepository<Contact,Long>, PagingAndSortingRepository<Contact, Long> {
List<Contact> findByContactNameLikeAndContactPhoneLike(String name, String phone, Pageable pageable)
}
I tried Pageable with CrudRepo and it works fine.
And for the lambda you are right :)
In my configuration your implementation looks like this :
IContactRepository contactRepo = context.getBean(IContactRepository.class);
List<Contacts> results = contactRepo.findByContactNameLikeAndContactPhoneLike("%CA%","%090%" , new PageRequest(1, 20));
http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html
Please have a look Query creation under 1.2.2 Defining query methods
I am guessing you are looking at Predicate because you want to be able to execute any arbitrarily complex query.
However there is no findAll(Predicate, Pageable) method.
I suggest that you check out Specification and JpaSpecificationExecutor. Your code would look like this:
public interface IContactRepository extends JpaRepository<Contact,Long>, JpaSpecificationExecutor<Contact> {
}
Then you would have access to the method findAll(Specification, Pageable). And as per your requirement, Specification is a Functional Interface, so you can use a lambda to easily pass in an implementation.
Check out section 2.5 from the documentation for more details.
Here is the Javadoc of Specification and here is the Javadoc of JpaSpecificationExecutor
Also if you have to endure the pain of Java, you should probably drop the I in IContactRepository :). Java code usually forgoes that .NET practice
Thats my implementation maybe help to you:
#Override
public Page<Advert> findActiveAdverts(String searchValue, String availability, Long branchId, Long categoryId, Pageable pageable) {
Page<Advert> adverts = advertRepository.findAll(new Specification<Advert>() {
#Override
public javax.persistence.criteria.Predicate toPredicate(Root<Advert> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.get(Advert_.ACTIVE), true));
if (!StringUtils.isEmpty(searchValue)) {
predicates.add(cb.or(
cb.like(root.get(Advert_.TITLE), "%" + searchValue + "%"),
cb.like(root.get(Advert_.EXPLANATION), "%" + searchValue + "%"))
);
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
}, pageable);
return adverts;
}

Selecting fields on Spring Data

I'm trying to find information about how to select only certain fields of an entity using Spring Data (I'm using JPA). I want to select only specific information of an entity, the repository interfaces gives you the ways to return the information of the WHOLE entity!. Some times I only need 2 or 3 fields of an entity and returning 20,30, ...100.. fields may be a little overkill.
This kind of functionality is something that I would do using Hibernate Criteria Projections, or even JPA "SELECT NEW ...." queries. Don't know if it is possible with Spring Data.
Thanks.
What you can do is return a List<Object[]> from repository. Then in your service class iterate over this list and manually create the object you need. Sample repository method
#Query("select el.moduleId, el.threadId from ExceptionLog el")
public List<Object[]> tempQuery();
I think you can also do it in this way
SomeDataPOJO{
required col1
required col2
}
and then write query like this
#Query("select new SomeDataPOJO from requiredTable where xyz="abc")
public List<SomeDataPoJO> tempQuery()
Its not plain Spring Data but did you consider using Springs JdbcTemplate? Its also in the Context if you use Spring Boots Autoconfiguration and has several handlers for transforming results of the Query.
For Example for the Query SELECT a, b FROM EMPLOYEE WHERE ID = ? you could use
String query = "SELECT a, b FROM EMPLOYEE WHERE ID = ?";
List<Pair<String,Integer>> employees = jdbcTemplate.queryForObject(
query, new Object[] { id }, new ExampleRowMapper());
Where the ExampleRowMapper transforms each row from the result into your given Return type (Pair<String, Integer> in this case) and could look like
public class ExampleRowMapper implements RowMapper<Pair<String, Integer>> {
#Override
public Pair<String, Integer> mapRow(ResultSet rs, int rowNum) throws SQLException {
return Pair.of(rs.getString(1), rs.getString(2));
}
}
Example adapted from https://www.baeldung.com/spring-jdbc-jdbctemplate where you find more information.
Of course its not as typesafe as a JPQL as the Query is "raw" or "native" SQL but at least the response is again typesafe and I like it more than to return Object[] or something.

Categories

Resources