How would you make it generic? - java

This following code works properly, but I wanted it to have in generic style.
I tried to write it in a generic way, but I had some problems.
I have a method that is to get some data from two collections from MongoDB.
I use MongoOperations from package org.springframework.data.mongodb.core to handle MongoDB operations. The method find which I am calling, needs parameters: query, and a class of which the Mongo Collection is made of. Classes EvaluationWork and ArchivalWork implement an interface WorkContainer.
What I'd like to do is to make use of that T generic parameter, which is upper bounded by WorkContainer, by make generic the call of find method, instead of EvaluationWork.class and ArchivalWork.class if possible. And I am also curious whether is possible to have the function convertToWorkWithId generic and the possibility to call it in the same way as now, without casting (.map(w->convertToWorkWithId((T)w))) ?
public abstract class AbstractWorkMongoDao<T extends WorkContainer> {
protected WorkWithId convertToWorkWithId(WorkContainer evaluationWork) {
return evaluationWork.getWorkWithId();
}
/* there is some code here */
public List<WorkWithId> getByDate(String institutionId, String startDate, String endDate, boolean repository) {
Query query = buildSearchQuery(institutionId, startDate, endDate);
List<WorkWithId> workWithIds = mongoOperations.find(query, EvaluationWork.class)
.stream()
.map(this::convertToWorkWithId)
.collect(Collectors.toList());
if(repository){
workWithIds.addAll(mongoOperations.find(query, ArchivalWork.class)
.stream()
.map(this::convertToWorkWithId)
.collect(Collectors.toList())
);
}
return workWithIds;
}
}

Related

QueryDsl Tuple in List<Tuple>

Assume me having a table of cats with plenty of columns.
I am trying to do equivalent of this query in QueryDsl:
select * from cat where (cat.pattern, cat.breed) in (('calico', 'Siberian'), ('grey', 'Siamese'), ('tabby', 'Maine Coon'));
In Java I have this class for parametrizing my cats:
class CatParameters {
public String pattern;
public String breed;
}
And this method (unfortunately incorrect) to fetch cats from database:
public List<CatDto> getCatsByParameters(List<CatParameters> params) {
// something like this
QCat cat = QCat.cat;
return query.from(cat)
.where(Expressions.list(cat.pattern, cat.breed).in(params))
.list(ConstructorExpression.create(CatDto.class, cat.field1, cat.field2, cat.field3, .../* etc */))
}
This obviously leads to an error "Cannot resolve method 'in(java.util.List<my.package.name.Cat>)'".
So how can I query some cats with (colX, colY) being in list of java objects with those properties?
UPD: I have found this question on fairly similar topic with subquery instead of collection, yet I wonder if there is a way to do it with collection (or maybe somehow create List<Tuple> from List<CatProperties>).
Per the maintainers of QueryDSL, using either CollectionExpression or nested Expression.list isn't the right approach, and instead one needs to use Template expressions (as hinted at in the comments on this question). Here's how this might look:
public List<CatDto> getCatsByParameters(List<CatParameters> params) {
QCat cat = QCat.cat;
return query.from(cat)
.where(Expressions.list(cat.pattern, cat.breed).in(Expressions.list(
params.stream()
.map(catParam -> makeTuple(
Expressions.constant(catParam.pattern), Expressions.constant(catParam.breed)))
.toArray(new Expression[0])
)))
.list(ConstructorExpression.create(CatDto.class, cat.field1, cat.field2, cat.field3, .../* etc */))
}
private static SimpleExpression<Object> makeTuple(Expression<?>... args) {
return Expressions.template(Object.class, "({0}, {1})", (Object[]) args);
}

Spring Data JPA: how to return empty result when repository method parameter of Collection type is empty?

I'm using Spring Data JPA v1.10.2
And there's a use-case:
ClientDao.java:
List<Client> getClientsByUnitsIn(#NonNull Collection<Unit> units);
This method generates an SQL query like this one:
SELECT * FROM clients WHERE units in (?1)
There's a similar case when I add #Query annotation for the repository method:
#Query("SELECT c FROM Client c WHERE c.unit IN (?1)")
List<Client> getSpecificClients(#NonNull Collection<Unit> units)
But in many cases parameter units may be empty. And in such cases the method should return empty result, but it just fail with a message about an erroneous SQL statement.
I use a workaround: adding a default method to the repository like this one:
default List<Client> getSpecificClientsOrEmpty(#NonNull Collection<Unit> units){
if (units.isEmpty) {
return emptyList();
}
return getSpecificClients(units);
}
But I don't like this workaround:
I have to create one extra method for each case
I have to check that only default method is using in code, as there's no compile-time checking, and if I miss some using, I get a runtime Exception.
Does anybody have a better solution?
1) Write your own query with the boiler plate code in the getSpecificClients() repository implementation :
public List<Client> getSpecificClients(#NonNull Collection<Unit> units){
if (units.isEmpty()) {
return emptyList();
}
return em.createQuery("SELECT c FROM Client c WHERE c.unit IN (?1)", Unit.class)
.setParameter(1, units)
.getResultList();
}
If this pre-processing is a uncommon requirement in your repository, this way should be favored.
It is a little verbose way but it is still acceptable for a handful of cases.
2) Make it in a transverse way with AOP.
Define a Aspect to do this processing before each method you need :
if (units.isEmpty) {
return emptyList();
}
Note that this way should be used only if the pre-processing requirement occurs frequently enough as it increases the application complexity and the general setup.
3) You could create a generic default method in a base interface repository that accepts a Function as parameter to be able to passe to the method any method to execute :
#SuppressWarnings("unchecked")
default<T, U> List<U> selectWithIn(Collection<T> valueForInClause, Function<Collection<T>, List<U>> function) {
if (valueForInClause.isEmpty()) {
return new ArrayList<U>();
}
return function.apply(valueForInClause);
}
In ClientDAO class you would have still this one :
#Query("SELECT c FROM Client c WHERE c.unit IN (?1)")
List<Client> getSpecificClients(#NonNull Collection<Unit> units)
And in the client code of the DAO you could invoke the selectWithIn() method in this way :
private ClientDAO clientDAO;
...
List<Unit> units = ...;
List<Client> clients = clientDAO.selectWithIn(units, (o) -> clientDAO.getSpecificClients(o));
It is not too much verbose, it spares some code lines but I don't like really this way as it makes a little more complex unit tests of the DAO client classes.

cqengine compare by datetime

I'm trying to find a list of objects that are after a certain DateTime. In order to do so, I've created the following query:
return foos.retrieve(QueryFactory.equal(EXPIRY_INDEX, new DateTime()));
I then created the following index:
public static final Attribute<Foo, DateTime> EXPIRY_INDEX = new SimpleAttribute<Foo, DateTime>() {
#Override
public DateTime getValue(Foo foo, QueryOptions queryOptions) {
return foo.getEXPIRY();
}
};
So far all good, except, the equal(...) method will invoke DateTime.equals(...) as far as I know which will ultimately return false all the time. What I need is a way to call DateTime.isAfterNow(...) or DateTime.isAfter(...).
How can I find all elements in a collection that have a DateTime after right now?
If I understand correctly, you should use a greaterThan() query instead of an equals() query.
These will rely on Comparable.compareTo(), instead of Object.equals(). So it should work if your DateTime object implements the Comparable interface properly.

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;
}

MyBatis SelectList Output CopyOnWriteArrayList

Please be patient with the newbie question as I'm trying to learn MyBatis and java at the same time. I have an application in which I need to use threadsafe variables. Based on some research and my ideas of how the application will be used, I've settled on a CopyOnWriteArrayList over a Vector.
When I call a selectList from the mybatis sql session, is there any way to tell it to create an CopyOnWriteArrayList as its return rather than an ArrayList? Admittedly, my code to configure this is two lines instead of one, but something in me says that there's got to be a better way and/or I'm not the first one to have encountered this.
List<Team> teams = session.selectList("getTeamsByGameID", gameID);
List<Team> arrayListReturn = new CopyOnWriteArrayList<Team>(teams);
return arrayListReturn;
Thanks in advance,
I know of two ways to handle this.
Option 1: Use a Mapper class and specify the type of List to return there.
Define a Mapper interface:
public interface TeamMapper {
CopyOnWriteArrayList<Team> getTeamsByGameID();
}
Your mapper xml file stays the same. The code to do the query changes to:
TeamMapper m = session.getMapper(TeamMapper.class);
List<Team> lt = m.getTeamsByGameID();
System.out.println(lt.getClass());
//=> prints out "class java.util.concurrent.CopyOnWriteArrayList"
Option 2: Create a ResultHandler and pass that into the session.select() method.
Here you use the ResultHandler interface. That interface requires you to override one method, handleResult, which is given each result that comes back from the database as the query is in progress.
In your case, your ResultHandler would look something like this:
public class TeamResultHandler implements ResultHandler {
private List<Team> teams = new CopyOnWriteArrayList<Team>();
#Override
public void handleResult(ResultContext rc) {
countries.add((Team) rc.getResultObject());
}
// provide a getter so you can retrieve it when finished
public List<Team> getTeamList() {
return teams;
}
}
Instead of using selectList as you do above, you would now use the session.select(String, ResultHandler), like so:
TeamResultHandler rh = new TeamResultHandler();
session.select("getTeamsByGameID", rh);
List<Team> lt = rh.getTeamList();
return lt;
This solution is more verbose than your solution (requires an extra class and three lines in your query code, rather than 2), but it only creates one List, rather than two, so you'll have to decide which fits your needs best.
In addition, ResultHandlers can be useful for additional things - making sure results are sorted in a certain way or filtered or something else, in case you need that.

Categories

Resources