JPA find first by created in given list - spring-boot

I have a model class which has these fields:
class Student {
Long roleNo;
Long version;
Date createdAt;
Date updateAt;
}
Now I'm trying to write a JPA query in which I can pass a list of role numbers and for every role number I can get a latest record by version.
I tried to do this but I'm only getting one record:
findFirstByRoleNoIn(List<Long> roleNo);

One way is to make use of #Query annotation to write simple query. Sample query for your case:
#Query(nativeQuery = true, value = """
SELECT s.* FROM student s
INNER JOIN (SELECT
role_no,
MAX(version) AS latest
FROM student GROUP BY role_no) t_version
ON s.role_no = t_version.role_no
AND s.version = t_version.latest
WHERE s.role_no IN (:roleNo)
""")
findLatestVersion(#Param("roleNo") List<Long> roleNo);

Related

Join 2 Tables using Spring Boot JPA Criteria Specification and Predicate

I'm trying to create a criteria to retrieve Rows from 2 tables (UserDetail, Vehicle). A UserDetail has reference to Vehicle. My objective is to retrieve a list of specified field from both Tables, given an UserDetail id. In #Query was easy to do but the client said that this must be coded with criteria with specification and predicate to Join Tables.
My #Query was:
#Repository
public interface UserDao extends CrudRepository<UserDetail, Integer>{
#Query("SELECT ud.userId, ud.userName ,vh.vehicleId, vh.vehicleName "
+ "FROM UserDetail ud LEFT JOIN ud.vehicle vh where ud.vehicleId = vh.vehicleId")
public List<UserVehicleDTO> findAllUserVehicle();
}
Can anyone help me? I Fetch Data Using Specification with predicate as given Query is needed
Criteria query(I did not tested/debugged it, may need modify):
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = cb.createTupleQuery();
Root<UserDetail> root = query.from(UserDetail.class);
Join<UserDetail, Vehicle> join = root.join("vehicle", JoinType.LEFT);
query.multiselect(
root.get("userId"),
root.get("userName"),
join.get("vehicleId"),
join.get("vehicleName")
))
List<Tuple> x = entityManager.createQuery(query).getResultList();
BTW. JPQL query is wrong. Don't need where condition, it will be created by JPA automatically.
SELECT ud.userId, ud.userName ,vh.vehicleId, vh.vehicleName
FROM UserDetail ud
LEFT JOIN ud.vehicle

QueryDSL Paging Sorting duplicates

I have got an Entity A say Car with a OneToMany relationship to an Entity B say CarProperty.
My Car Repository extends QueryDslPredicateExecutor to support paging and sorting:
Page<T> findAll(Predicate predicate, Pageable pageable);
I'm trying to execute a query where I sort the results, by a column of CarProperty, defining the sort on pageable variable.
However since it is a One To Many relationship between Car and CarProperty, I have returned duplicates of Car.
Is it possible to obtain distinct results on Car using this structure?
If it is not possible to use distinct on the Predicate, how could I represent the following query in the predicate (using exists to eliminate duplicates):
SELECT Car.*
FROM Car C LEFT JOIN CarProperty CP ON (C.ID = CP.CAR_ID)
WHERE EXISTS (SELECT 1
FROM CarProperty CP2
WHERE CP2.CAR_ID = C.ID AND CP2.ID = CP.ID)
ORDER BY CP.PROPERTY_NAME ASC;
Thanks in advance
You can use named entity graph to avoid duplicate records, in my case it worked.
#NamedEntityGraph(name = "Car.carProperty" ,attributeNodes = #NamedAttributeNode("carProperties"))
#Entity
public class Car {
#OneToMany
#JoinColumn(name = "carProperties")
private List<CarProperty> carProperties;
}
and then override findall method
#EntityGraph(value = "Car.carProperty" , type = EntityGraphType.LOAD)
Page<Car> findAll(Predicate predicate, Pageable pageable);

Filter with Spring-Data and QueryDSL on nullable reference attribute

I have following issue. I am using Jquery Datatable serverside and I am now implementing the search box. But I have an issue there in special case, when a dataset has an attribute, what is "null". So the dataset will not be found although it should found cos it matches on one attibute.
The situtation in beginning is like follows. You see there is a dataset with apprentice Fabio Bartels, who has not Fachrichtung. And a dataset with Viktoria.
Now when I search for Viktoria, the filter works as expected:
When I search for Fabio, then Dataset is not found:
=====
The problem I have is, that I don't know how to handle the filter, that a attribute will only be validated against the search string when the attribute is not null.
=====
Serverside Java Classes see like follows:
QueryClass:
class ContractSearchQuery {
private static QContract contract = QContract.contract;
static BooleanExpression getPredicate(final ContractSearch filter) {
BooleanExpression predicate;
if (filter == null || filter.isEmpty()) {
// SHOW ALL PREDICATE ...
} else {
final String search = filter.getSearch();
final List<BooleanExpression> expressions = new ArrayList<BooleanExpression>();
// EXPRESSIONS CURRENTLY ONLY ON AUSZUBILDENDER AND FACHRICHTUNG
// FOR SHOWCASE
expressions.add(containsApprenticeName(search)); // AUSZUBILDENDER
expressions.add(containsSpecialisation(search)); // FACHRICHTUNG
BooleanExpression expression = expressions.get(INTEGER_ZERO);
for (int i = 1; i < expressions.size(); i++) {
expression = expression.or(expressions.get(i));
}
predicate = expression;
}
return predicate;
}
private static BooleanExpression containsApprenticeName(final String search) {
final BooleanExpression expLastName = contract.apprentice.lastName.containsIgnoreCase(search);
final BooleanExpression expFirstName = contract.apprentice.firstName.containsIgnoreCase(search);
return expLastName.or(expFirstName);
}
private static BooleanExpression containsSpecialisation(final String search) {
return contract.companyOccupationCombination.occupationCombination.specialisation.name.containsIgnoreCase(search);
}
}
Spring-Data-Repository Call:
final PageRequest pageRequest = new PageRequest(firstResult / maxResults, maxResults, orderSort);
final Page<Contract> page = contractRepository.findAll(predicate, pageRequest);
return page.getContent();
=======
Database:
By the way I recognized when I do direct request against my db with joining Specialisation Table, then I only get Fabio as record, when not joining Specialisation, I get all three persons. Maybe somethings to do with my issue:
select a.first_name, a.last_name from contract c
join company_occupation_combination coc on c.company_occupation_combination = coc.id
join occupation_combination oc on coc.occupation_combination = oc.id
join apprentice a on c.apprentice = a.id
Result:
"Fabio";"Bartels"
"Viktoria";"Kruczek"
"Lina";"Ehleiter"
With Join:
select a.first_name, a.last_name from contract c
join company_occupation_combination coc on c.company_occupation_combination = coc.id
join occupation_combination oc on coc.occupation_combination = oc.id
join specialisation s on oc.specialisation = s.id
join apprentice a on c.apprentice = a.id
Result: "Viktoria";"Kruczek"
====
EDIT:
Okay, on db site I found out (with Hibernate and JPA I start forgetting SQL-Basices ;-)), that I need a left join for the nullable relation, so my query should result to an sql like:
select a.first_name, a.last_name from contract c
join company_occupation_combination coc on c.company_occupation_combination = coc.id
join occupation_combination oc on coc.occupation_combination = oc.id
left join specialisation s on oc.specialisation = s.id
join apprentice a on c.apprentice = a.id
====
So my question is, how can I manage left Join when I have a Query-Class using QueryDSL and Spring-Data-Repository like mentioned above?
If you really need left join, you can't achieve that via predicate (instead it is possible via sub-query)
To be able to do left-join, you will need JPAQuery.
Assuming you have already configured repositories, and able to use EntitiManager, implement ContractRepositoryCustom , so that in your implementation you can have
#PersistenceContext(unitName = "unitname")
protected EntityManager entityManager;
public List<Contract> findAllContracts() {
return new JPAQuery(entityManager, HQLTemplates.DEFAULT)
.from(QContract.contract)
.join(QContract.contract.companyOccupationCombination, QCompanyOccupationCombination.companyOccupationCombination)
.join(QCompanyOccupationCombination.companyOccupationCombination.occupationCombination, QOccupationCombination.occupationCombination)
.leftJoin(QOccupationCombination.occupationCombination.specialization, QSpecialization.specialization)
.join(QSpecialization.specialization.apprentice, QApprentice.apprentice)
.list(QContract.contract);
}
And for pagination you always apply limit(maxResults) and offset(firstResult)
I really like working with Spring-Data and Query-DSL, cos it makes my code really tidy. But I am really suprised, that for the case of nullable references there seems no solution. Sure you can use another solution like #vtorosyan mentioned and thank you again for that solution, but when you project is builded up with combination of QueryDSL and Spring-Data, you really don't want to bring a second style in your application.
But I needed a solution, so I did now the follows.
The point of the issue was, that when I used data from a nullable entity, a join has been executed what hided the datasets, who had a null reference on it, see examples above. What I now did and I hope I will not get another issue then with that solution on later time of that project. I did the null references to not null and defined something like null-record.
Example I added a record for specialisation like
ID NAME
0 Keine
Instead of null I now use that record what has until now following effects:
First my table shows now "Keine" (engl. "None") for all attributes what are not set. It looks more consistent when having a textoutput then empty string.
Now I can explicitly search for "Keine", when I am interested for data records what have no specialisation set.
And my searchbox works as expected for records, which have no speciafication set. (THAT WAS MY ISSUE FROM BEGINNING WHAT I WANTED TO SOLVE):
Additional to that searchbox I use a modal dialog for filtering. Now I can explicitly filter "Keine" for "nullable" records:
If you think there is another good solution for that issue without rebuild code using Spring-Data and QueryDSL konsequently, don't hesitate to post ;-)

SQL Group by and having in coherence

I would like to execute the following query in Coherence
Select AVG(Salary)
, Count(*)
from Employees
group by Company
Having AVG(Salary)> 1000
I was trying to use GroupAggregators but I have problem with refering to an avarage salary as it is not defined in the Employee Class but in Aggregator:
GroupAggregator high_salary = GroupAggregator.createInstance("getDepartment"
, new DoubleAverage("getSalary")
, new GreaterFilter("DoubleAverage", 1000));
How can I do this?
You should use IdentityExtractor.INSTANCE instead of "DoubleAverage" as a value extractor in GreaterFilter, because value passed to this filter is the avarage salary itself, not some object with "DoubleAverage" property.
But if you would like to get avarage salary as well as count (that would be consistent with your SQL query), you will have to use CompositeAggregator. In that case the value passed to the filter will not be a number anymore, but a List and you will have to use ValueExtractor that will extract avarage salary from this list. In Coherence 12.2.1 it would look like this:
DoubleAverage<Employee> avgSalary = new DoubleAverage<>(Employee::getSalary);
Count<String, Employee> count = new Count<>();
CompositeAggregator<String, Country> compositeAggregator = CompositeAggregator
.createInstance(new InvocableMap.EntryAggregator[] { avgSalary, count });
ValueExtractor<Object, ? extends Double> salaryExtractor =
list -> ((List<Number>) list).get(0).doubleValue();
Filter having = Filters.greater(salaryExtractor, 1000.0);
GroupAggregator<String, Country, Employee, String, List> groupAggregator =
GroupAggregator.createInstance(Employee::getDepartment, compositeAggregator, having);
It can be done also in older versions, but it would require some more work to implement salaryExtractor without using lambda expression.

Get Random Rows Using JPQL

Is it possible to use JPQL for getting random rows? For example in SQL Server I would use:
select * from myTable where columnName = 4 order by newid()
Thanks,
Rod
This is what I use. I first get the number of rows for the entity and I then limit the results of the fetch query to a random row. This involves two queries, so if this is a problem for you you might want to watch native queries. If not here is the code I use:
public <T> T randomEntity(EntityManager em, Class<T> clazz) {
Query countQuery = em.createQuery("select count(id) from "+clazz.getName());
long count = (Long)countQuery.getSingleResult();
Random random = new Random();
int number = random.nextInt((int)count);
Query selectQuery = em.createQuery("from "+clazz.getName());
selectQuery.setFirstResult(number);
selectQuery.setMaxResults(1);
return (T)selectQuery.getSingleResult();
}
As of today (April 9th 2010), JPQL does not support random ordering

Resources