Spring: Derived Queries using 2 clauses? - java

I am currently using spring derived queries in my application. E.g:
Dog findById(String id);
Is there a way to add 2 or more clauses to the derived queries? e.g.:
Dog findByIdAndOwnerOrderByOwner();

You should be able to do something like this,
Dog findByIdAndOwnerOrderByOwnerDesc(String id, String owner);
As long as you use AND or OR to concatenate your query, it should handle multiple clauses. I also believe you need to specify the order by order (ASC/DESC).
Check out Spring Data JPA for more information regarding this topic.

Related

How to exclude or ignore field when building a generic Lucene Query to run against more than 1 Entity

We're in the process of converting our java application from using SOLR/Lucene to Elasticsearch 5.6.6.
In 1 particular area, we would previously build 1 Lucene Search BooleanQuery and run it against 3 different entities, looking for matches. We didn't need to specify the entity it would be used against until you ran the actual query.
BooleanQuery luceneQuery = myQueryBuilder.buildMyQuery();
session.createFullTextQuery(luceneQuery, entityOne);
session.createFullTextQuery(luceneQuery, entityTwo);
session.createFullTextQuery(luceneQuery, entityThree);
One sub-query within [luceneQuery] above searched on taxId, which entityOne doesn't have (no taxId indexed field) but the other 2 entities do have. However it all worked fine, no exceptions were given, I believe it just ignored the unknown/un-indexed field, not exactly sure how it worked, but it did.
Now we're converting over to Elasticsearch DSL, we need to give the entity up front so I (for better or worse) build the query 3 different times, against each entity, like so:
QueryBuilder entityOneQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityOne.class).get();
QueryBuilder entityTwoQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityTwo.class).get();
QueryBuilder entityThreeQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityThree.class).get();
// Create 3 exact (except for which entity they point at) queries
Query entityOneQuery = myQueryBuilder.buildMyQuery(entityOne);
Query entityTwoQuery = myQueryBuilder.buildMyQuery(entityTwo);
Query entityThreeQuery = myQueryBuilder.buildMyQuery(entityThree);
Where buildMyQuery() has a number of sub-queries but the one dealing with taxId looks something like:
qb.bool().should(
qb.keyword()
.onField("taxId")
.matching(taxId)
.createQuery()
);
However, now, since, entityOne doesn't have taxId as an indexed column/field, createQuery() throws an exception:
SearchException: Unable to find field taxId in EntityOne
My questions are:
Is there some way to tell Lucene to ignore the field if the entity doesn't have it?
If not, is there some way, using the passed in QueryBuilder to determine what the entity is, so that, within the taxId subquery code, I can basically say if (entityType == EntityOne) {return null;} so that this particular sub-query won't be included in the overall query?
Is there some way to tell Lucene to ignore the field if the entity doesn't have it?
Just a clarification: it's Hibernate Search that implements the DSL and throws exceptions, not Lucene. Lucene is the underlying technology, and doesn't perform much validation.
If your goal is to retrieve all three entities in a single result list, and if fields with the same name in different entity types are configured similarly (e.g. field "name" appears in entity 1 and 2, but has the same analyzer), you could simply build a single query and retrieve all three types in that single query. You will have to:
Make sure, when building the single Lucene query, to always use the query builder of an entity type that actually defines the field your targeting: if targeting taxId for instance, you can use the query builder for EntityTwo or for EntityThree, but not the one for EntityOne. Yes, that's right: you can mix multiple query builders in a single query, as long as fields with the same name are configured similarly in all targeted entities.
build the FullTextQuery that way: session.createFullTextQuery(luceneQuery, EntityOne.class, EntityTwo.class, EntityThree.class);.
If not, is there some way, using the passed in QueryBuilder to determine what the entity is, so that, within the taxId subquery code, I can basically say if (entityType == EntityOne) {return null;} so that this particular sub-query won't be included in the overall query?
No, there is not. You could pass add a parameter to your method to pass the entity type, though: buildMyQuery(Class<?> type, QueryBuilder queryBuilder) instead of buildMyQuery(QueryBuilder queryBuilder).

Difference between findBy and findOneBy in Spring data JPA

All i know so far is that FindBy can return multiple results while FindOneBy will return a single result or null when we use it the following way.
List<Department> findByDepartmentName(String name);
Department findOneByDepartmentId(Long Id);
now, my question is, can i use findBy this way?
Department findByDepartmentId(Long Id);
If yes,
Lets assume there are multiple records for given Id.
On what basis does findBydepartmentId return a single record?
Finally, When or Why should i not use findBy in place of findOneBy?
Can I use findBy this way? Department findByDepartmentId(Long Id);
Yes, this syntax is technically correct from Spring JPA point of view. Although Spring JPA infers what you're trying to achieve with your query looking at the return type as well.
Basically these are the cases for return types:
with your query you want to return a single value - you can specify basic type, Entity T, Optional<T>, CompletableFuture<T> etc.
with your query you want to return a collection of T - you can specify List<T>, Stream<T>, Page<T>, Slice<T> etc.
That being said, your query definition:
Department findByDepartmentId(Long Id);
means that you expect a single result (because you've specified Entity T as a return type). This will reflect on how Spring JPA executes the query - it will call getSingleResult() on the javax.persistence.Query interface, which will throw an exception if more than one objects satisfy the criteria.
On what basis does findBydepartmentId return a single record?
On the basis that there's a single object with that Id, otherwise it will throw an exception.
When or Why should i not use findBy in place of findOneBy?
Those two have different meanings and are not interchangeable.
findOneBy always results in getSingleResult() being invoked.
findBy has different behavior depending on the return type - as per the definitions given above.
findOneByXX will ensure that there is only one or no value, if there are 2 values an exception will be thrown.
However findByXX doesn't make this check of uniqueness.
I have done some tests and Spring Data ignore all characters between the method (find, delete,...) and By.
In https://github.com/spring-projects/spring-data-commons/blob/14d5747f68737bb44441dc511cf16393d9d85dc8/src/main/java/org/springframework/data/repository/query/parser/PartTree.java#L65 it is the \p{Lu}.*? part.
Spring Data only use return type to decide how handle responses.
So it is possible to define these following methods even if it is not correct semantically.
Department findAllByDepartmentId(Long Id);
List<Department> findOneByDepartmentName(String name);

How to specify default sort in Spring Data #Repository find method?

Is below the only way (not so "clean" IMHO)? I'd expect something like #SortDefault and #PageableDefault, but it's only for (REST) controllers and not applicable to Spring Data REST.
#Query("SELECT e FROM FacebookPost e WHERE e.commentsPaging.next IS NOT NULL ORDER BY e.creationTime ASC")
Page<FacebookPost> findByCommentsHasNext(Pageable pageable);
Additionally:
How to specify multiple columns for default sorting?
How to specify calculated column(s), i.e. involving CASE WHEN or functions (which can be indexed in PostgreSQL)?
I'm not sure if #SortDefault is what you're looking for but you can directly specify the type of ordering you'd like with the repository method name. For example, from reading your query, I'd name the method something as follows:
Page<FaceBookPost> findByCommentHasNextOrderByCreationTimeAsc(Pageable pageable);
If you dictate elsewhere that the field commentsPaging cannot be null, I don't think there would even be a need for a #Query above your method name since Spring will automatically go and perform the query for you.

DTOs with different granularity

I'm on a project that uses the latest Spring+Hibernate for persistence and for implementing a REST API.
The different tables in the database contain lots of records which are in turn pretty big as well. So, I've created a lot of DAOs to retrieve different levels of detail and their accompanying DTOs.
For example, if I have some Employee table in the database that contains tons of information about each employee. And if I know that any client using my application would benefit greatly from retrieving different levels of detail of an Employee entity (instead of being bombarded by the entire entity every time), what I've been doing so far is something like this:
class EmployeeL1DetailsDto
{
String id;
String firstName;
String lastName;
}
class EmployeeL2DetailsDto extends EmployeeL1DetailsDto
{
Position position;
Department department;
PhoneNumber workPhoneNumber;
Address workAddress;
}
class EmployeeL3DetailsDto extends EmployeeL2DetailsDto
{
int yearsOfService;
PhoneNumber homePhoneNumber;
Address homeAddress;
BidDecimal salary;
}
And So on...
Here you see that I've divided the Employee information into different levels of detail.
The accompanying DAO would look something like this:
class EmployeeDao
{
...
public List<EmployeeL1DetailsDto> getEmployeeL1Detail()
{
...
// uses a criteria-select query to retrieve only L1 columns
return list;
}
public List<EmployeeL2DetailsDto> getEmployeeL2Detail()
{
...
// uses a criteria-select query to retrieve only L1+L2 columns
return list;
}
public List<EmployeeL3DetailsDto> getEmployeeL3Detail()
{
...
// uses a criteria-select query to retrieve only L1+L2+L3 columns
return list;
}
.
.
.
// And so on
}
I've been using hibernate's aliasToBean() to auto-map the retrieved Entities into the DTOs. Still, I feel the amount of boiler-plate in the process as a whole (all the DTOs, DAO methods, URL parameters for the level of detail wanted, etc.) are a bit worrying and make me think there might be a cleaner approach to this.
So, my question is: Is there a better pattern to follow to retrieve different levels of detail from a persisted entity?
I'm pretty new to Spring and Hibernate, so feel free to point anything that is considered basic knowledge that you think I'm not aware of.
Thanks!
I would go with as little different queries as possible. I would rather make associations lazy in my mappings, and then let them be initialized on demand with appropriate Hibernate fetch strategies.
I think that there is nothing wrong in having multiple different DTO classes per one business model entity, and that they often make the code more readable and maintainable.
However, if the number of DTO classes tends to explode, then I would make a balance between readability (maintainability) and performance.
For example, if a DTO field is not used in a context, I would leave it as null or fill it in anyway if that is really not expensive. Then if it is null, you could instruct your object marshaller to exclude null fields when producing REST service response (JSON, XML, etc) if it really bothers the service consumer. Or, if you are filling it in, then it's always welcome later when you add new features in the application and it starts being used in a context.
You will have to define in one way or another the different granularity versions. You can try to have subobjects that are not loaded/set to null (as recommended in other answers), but it can easily get quite awkward, since you will start to structure your data by security concerns and not by domain model.
So doing it with individual classes is after all not such a bad approach.
You might want to have it more dynamic (maybe because you want to extend even your data model on db side with more data).
If that's the case you might want to move the definition out from code to some configurations (could even be dynamic at runtime). This will of course require a dynamic data model also on Java side, like using a hashmap (see here on how to do that). You gain thereby a dynamic data model, but loose the type safety (at least to a certain extend). In other languages that probably would feel natural but in Java it's less common.
It would now be up to your HQL to define on how you want to populate your object.
The path you want to take depends now a lot on the context, how your object will get used
Another approach is to use only domain objects at Dao level, and define the needed subsets of information as DTO for each usage. Then convert the Employee entity to each DTO's using the Generic DTO converter, as I have used lately in my professional Spring activities. MIT-licenced module is available at Maven repository artifact dtoconverter .
and further info and user guidance at author's Wiki:
http://ratamaa.fi/trac/dtoconverter
Quickest idea you get from the example page there:
Happy hunting...
Blaze-Persistence Entity Views have been created for exactly such a use case. You define the DTO structure as interface or abstract class and have mappings to your entity's attributes. When querying, you just pass in the class and the library will take care of generating an optimized query for the projection.
Here a quick example
#EntityView(Cat.class)
public interface CatView {
#IdMapping("id")
Integer getId();
String getName();
}
CatView is the DTO definition and here comes the querying part
CriteriaBuilder<Cat> cb = criteriaBuilderFactory.create(entityManager, Cat.class);
cb.from(Cat.class, "theCat")
.where("father").isNotNull()
.where("mother").isNotNull();
EntityViewSetting<CatView, CriteriaBuilder<CatView>> setting = EntityViewSetting.create(CatView.class);
List<CatView> list = entityViewManager
.applySetting(setting, cb)
.getResultList();
Note that the essential part is that the EntityViewSetting has the CatView type which is applied onto an existing query. The generated JPQL/HQL is optimized for the CatView i.e. it only selects(and joins!) what it really needs.
SELECT
theCat.id,
theCat.name
FROM
Cat theCat
WHERE theCat.father IS NOT NULL
AND theCat.mother IS NOT NULL

How to convert ResultSet object to one specific POJO class object?

For example i have a Resultset which contain Details about Employee details and I have a table For Employee in database and i am selecting all details from that
I have java POJO class which resembles Employee Table what is the best method to implement convert result set into Employee class object
what is the best way to implement if i have multiple classes Like Employee and multiple tables too how to write a reusable code.
I am Using following notation now.
public Employee{
private int empId;
public setEmpId(int empId){
this.empId = empId;
}
public int getEmpId(){
return this.empId;
}
}
public class SetResultSetToEmployee{
public Employee getEmployee(){
Employee e = new Employee();
e.setEmpId(resultSet.getInt("EmpId"));
return e;
}
}
Mybatis, or ORM like Hibernate may be your best solution.
But, if you really need to use jdbc's ResultSet, and want to convert ResultSet into java Beans, you can check:
ResultSetDynaClass of BeanUtils from apache.
or, write a bit code yourself like this.
I have java POJO class which resembles Employee Table what is the best
method to implement convert result set into Employee class object
If you are aware of JPA, then that will be best bet.. You won't
have to do a typecast to get your POJO object.. JPA works on ORM
(Object Relational Mapping). It directly Maps your POJO object to
Database table.. And when you can directly fetch your object from
database..
And if you have no other choice than using JDBC, then you have no
other choice than TypeCasting.
what is the best way to implement if i have multiple classes Like
Employee and multiple tables too how to write a reusable code.
In case of JDBC, I don't know what kind of Re-usability you are
looking for, but you can implement a DB class, and have all your
Query Templates there.. For any query you want to execute, you can
use that class.. That be comparatively better to go with rather than
have discrete queries, scattered all over your application..
Again if you can use JPA, there is a concept of Entities.. If you
want to work with multiple fetch, update or insert with database, it will be easier for
you.. You can just get appropriate object and dump it in a instance
of your Entity, or dump your object into your database without worrying about any kind of Queries.
For a start of JPA, you can start with any of these links: -
http://docs.oracle.com/javaee/5/tutorial/doc/bnbpz.html
http://www.vogella.com/articles/JavaPersistenceAPI/article.html
JPA-101-Java-Persistence-Explained
Defining-Your-Object-Model-with-JPA
If you really want to write reusable codes use ORM tools like http://www.hibernate.org/.

Categories

Resources