duplicate result of criteria Api in join query using JpaSpecification - spring

i am new to criteria api, so i have two models: GithubRepository and Issue where GithubReposity has many issue and every issue has one GithubRepository.
I have created many filters but the filter that did not work is the filter for GithubRepository where i want to check if a githubRepository has issues or Not.
after executing and applying has issues unfortunately something wrong happened and i see many duplicates.
here what i did in RepoSpecification:
public Specification<GithubRepository> getReposByIdIn(List<Long> iDs, String repoName) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (repoName != null && !repoName.isEmpty()) {
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("repositoryName")),
"%" + repoName.toLowerCase() + "%"));
}
if (iDs != null && !iDs.isEmpty()) {
predicates.add( criteriaBuilder.in(root.get("id")).value(iDs) );
}
query.orderBy(criteriaBuilder.asc(root.get("repositoryName")));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
public Specification<GithubRepository> getRepos(Boolean hasIssues,Boolean IsPrivate , String repoName) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (repoName != null && !repoName.isEmpty()) {
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("repositoryName")),
"%" + repoName.toLowerCase() + "%"));
}
if ( hasIssues!= null && hasIssues.equals(true)) {
// JOINS INSTANCE
Join<GithubRepository, Issue> repoIssueJoin = root.join("issues");
//Predicate predicate = criteriaBuilder.equal(repoIssueJoin.get("githubRepository").get("repositoryName"),
repoName);
//predicates.add(criteriaBuilder.isNotNull(repoIssueJoin));
predicates.add(criteriaBuilder.equal(repoIssueJoin.get("githubRepository"),root));
}
if (IsPrivate != null && IsPrivate.equals(true)) {
predicates.add(criteriaBuilder.equal(root.get("isPrivate"), true));
}
query.orderBy(criteriaBuilder.asc(root.get("repositoryName")));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}
my problem is in here exactly:
if ( hasIssues!= null && hasIssues.equals(true)) {
// JOINS INSTANCE
Join<GithubRepository, Issue> repoIssueJoin = root.join("issues");
//Predicate predicate = criteriaBuilder.equal(repoIssueJoin.get("githubRepository").get("repositoryName"),
repoName);
//predicates.add(criteriaBuilder.isNotNull(repoIssueJoin));
predicates.add(criteriaBuilder.equal(repoIssueJoin.get("githubRepository"),root));
}
i do not know what is wrong exactly and how to fix the problem but i am stacked with it because i am planning to add a new filter to filter greatOrEqual by number of issues

Related

Spring data jpa "IN" clause using specification

i am trying to get the records from the database using spring data jpa Speicification API.
here i need to put a condition for "In" clause for status column, for that i am code like below.
public static Specification<UserEntity> userSpecificationsforOperator(String orgName, String groupID,
List<String> status, Date startDate, Date endDate) {
return (root, query, builder) -> {
List<Predicate> predicates = new ArrayList<>();
if (orgName != null) {
Join<UserEntity, OrganizationEntity> organization = root.join("organization");
predicates.add(builder.equal(organization.get("name"), orgName));
}
/*
* if (groupID != null) {
* predicates.add(builder.equal(root.get("refApprovalGroupId"), groupID)); }
*/
if (status != null && status.size()>0) {
predicates.add(root.get("status").in(status));
}
if (startDate != null) {
predicates.add(builder.greaterThanOrEqualTo(root.get("createdDate"), startDate.toInstant()));
}
if (endDate != null) {
predicates.add(builder.lessThanOrEqualTo(root.get("createdDate"), endDate.toInstant()));
}
Predicate[] p = predicates.toArray(new Predicate[predicates.size()]);
return p.length == 0 ? null : p.length == 1 ? p[0] : builder.and(p);
};
}
the above code generating the query in cosole like below
SELECT userentity0_.id AS id1_68_,
userentity0_.created_by AS created_2_68_,
userentity0_.created_date AS created_3_68_,
userentity0_.last_modified_by AS last_mod4_68_,
userentity0_.last_modified_date AS last_mod5_68_,
userentity0_.group_id AS group_id6_68_,
userentity0_.group_name AS group_na7_68_,
userentity0_.is_enrollment_updated AS is_enrol8_68_,
userentity0_.is_federated AS is_feder9_68_,
userentity0_.name AS name10_68_,
userentity0_.organization_id AS organiz17_68_,
userentity0_.ref_approval_group_id AS ref_app11_68_,
userentity0_.reference_name AS referen12_68_,
userentity0_.status AS status13_68_,
userentity0_.uims_id AS uims_id14_68_,
userentity0_.user_status AS user_st15_68_,
userentity0_.version AS version16_68_
FROM user userentity0_
INNER JOIN organization organizati1_
ON userentity0_.organization_id = organizati1_.id
WHERE organizati1_.name ='utopia'
AND ( userentity0_.status =(?,?)
when i take the query into db tool and passing the values i am getting the data.
but while running from the application i am not getting the data.
here i understood that i am able to generate the query properly but my values are not passing correctly.
so could you please suggest how i can get my code return the data.
Maybe the implementation of it causes the issue...
Here, use this and let me know if it works
if (status != null && status.size()>0) {
predicates.add(builder.in(root.get("status")).value(status));
}

How to select data in spring boot using jpa based on where condition of jsonb column of postgres(without native query)?

I have Postgres database with jsonb field. I have used Predicate list for where condition with criteria builder in jpa. Now, I want to fetch JSON data stored in a database based on multiple where cause along with JSON. How it could be possible to do?
Database table
private List<Predicate> whereClause(CriteriaBuilder criteriaBuilder, Root<KafkaLog> kafkaLog,
KafkaLogSearchDto search) {
List<Predicate> predicates = new ArrayList<>();
if (search.getTopic() != null && !search.getTopic().isEmpty()) {
Expression<String> literal =
criteriaBuilder.literal("%" + search.getTopic().toUpperCase() + "%");
predicates.add(criteriaBuilder.like(criteriaBuilder.upper(kafkaLog.get("topic")), literal));
}
if (search.getOffset() != null) {
predicates.add(criteriaBuilder.equal(kafkaLog.get("offset"), search.getOffset()));
}
if (search.getResourceId() != null) {
predicates.add(criteriaBuilder.equal(kafkaLog.get("invoiceId"), search.getResourceId()));
}
if (search.getPartition() != null) {
predicates.add(criteriaBuilder.equal(kafkaLog.get("partition"), search.getPartition()));
}
if (search.getStatus() != null) {
predicates.add(criteriaBuilder.equal(kafkaLog.get("status"), search.getStatus()));
}
if (search.getCreatedAtFrom() != null && search.getCreatedAtTo() != null) {
predicates.add(criteriaBuilder.and(
criteriaBuilder.greaterThanOrEqualTo(kafkaLog.get("createdAt").as(Date.class),
search.getCreatedAtFrom()),
criteriaBuilder.lessThanOrEqualTo(kafkaLog.get("createdAt").as(Date.class),
search.getCreatedAtTo())));
}
if (search.getPayload() != null && !search.getPayload().isEmpty()) {
Expression<String> literal =
criteriaBuilder.literal("%" + search.getPayload().toUpperCase() + "%");
}
return predicates;
}
private CriteriaQuery<KafkaLog> getKafkaBySearchCriteria(KafkaLogSearchDto search, String orderBy,
int sortingDirection) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<KafkaLog> criteriaQuery = criteriaBuilder.createQuery(KafkaLog.class);
Root<KafkaLog> kafkaLog = criteriaQuery.from(KafkaLog.class);
List<Predicate> predicates = whereClause(criteriaBuilder, kafkaLog, search);
criteriaQuery.where(predicates.toArray(new Predicate[] {}));
if (orderBy == null || orderBy.isEmpty()) {
orderBy = "topic";
}
Expression<?> orderColumn = kafkaLog.get(orderBy);
if (orderColumn != null) {
if (sortingDirection == 0) {
criteriaQuery.orderBy(criteriaBuilder.asc(orderColumn));
} else if (sortingDirection == 1) {
criteriaQuery.orderBy(criteriaBuilder.desc(orderColumn));
}
}
return criteriaQuery;
}
apparently, custom data type support down to the JDBC/database level is not part of JPA itself. For just reading the JSON you might be able to do it with an entity mapping to String (if the JPA implementation does allow that).
However, you have some alternatives:
If you need to stick with JPA, you can implement a converter to handle the JSON for you (either generic as in this article or more specific - as you like): Using JPA with PostgreSQL JSON
If you can, ditch JPA and do it with JDBC/SQL directly, which gives you the full potential of PostgreSQL: how to store PostgreSQL jsonb using SpringBoot + JPA?
When reading the JSONB field, you can let the database do the conversion to String (as there is no direct JSON support in JDBC, yet) using explicit type conversion, e.g.:
SELECT payload::text FROM my_table WHERE ...
...or do some fancy filtering on the JSON field itself or return only a portion of the JSON data, aso.
This is example predicate to search jsonb with like clause :
predicate.getExpressions().add(cb.and(cb.like(cb.function("jsonb_extract_path_text", String.class, root.get("tags"), cb.literal(this.key)), "%"+ this.value + "%" )));

How to unit test a jpa repository method?

I have coded a JPA repository method and I am now realizing it is impossible to unit test.
Can anyone please advise how to unit test the following method or how to refactor my repository so that it is unit-testable?
Here is the problematic method:
#Override
public List<Pli> findPlisByMultiField(String identifiant, Date dateReceptionFrom, Date dateReceptionTo, PaiementEnum paiement, AREnum ar, String numeroAR, FDVEnum FDV, ConteneurNum conteneurNum, StatutPli statut) {
log.debug("findPlisByMultiField");
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Pli> c = criteriaBuilder.createQuery(Pli.class);
Root<Pli> pli = c.from(Pli.class);
List<Predicate> criteria = new ArrayList<Predicate>();
if (identifiant != null && !identifiant.trim().equals("")) {
ParameterExpression<String> parameterIdentifiant = criteriaBuilder.parameter(String.class, "identifiant");
Predicate conditionIdentifiant = criteriaBuilder.like(pli.<String> get("identifiant"), parameterIdentifiant);
criteria.add(conditionIdentifiant);
}
if (dateReceptionFrom != null && dateReceptionTo != null) {
ParameterExpression<Date> parameterDateReceptionFrom = criteriaBuilder.parameter(Date.class, "dateReceptionFrom");
ParameterExpression<Date> parameterDateReceptionTo = criteriaBuilder.parameter(Date.class, "dateReceptionTo");
Predicate conditionDateReception = criteriaBuilder.between(pli.<Date> get("dateReception"), parameterDateReceptionFrom, parameterDateReceptionTo);
criteria.add(conditionDateReception);
}
if (paiement != null) {
if (paiement.equals(PaiementEnum.IsPaiement)) {
Predicate conditionPaiementEnum = criteriaBuilder.equal(pli.<PaiementEnum> get("paiement"), true);
criteria.add(conditionPaiementEnum);
} else {
Predicate conditionPaiementEnum = criteriaBuilder.equal(pli.<PaiementEnum> get("paiement"), false);
criteria.add(conditionPaiementEnum);
}
}
if (ar != null) {
if (ar.equals(AREnum.IsAR)) {
Predicate conditionAREnum = criteriaBuilder.equal(pli.<AREnum> get("AR"), true);
criteria.add(conditionAREnum);
} else {
Predicate conditionAREnum = criteriaBuilder.equal(pli.<AREnum> get("AR"), false);
criteria.add(conditionAREnum);
}
}
if (numeroAR != null && !numeroAR.trim().equals("")) {
ParameterExpression<String> parameterNumeroAR = criteriaBuilder.parameter(String.class, "numeroAR");
Predicate conditionNumeroAR = criteriaBuilder.like(pli.<String> get("numeroAR"), parameterNumeroAR);
criteria.add(conditionNumeroAR);
}
if (FDV != null) {
if (FDV.equals(FDVEnum.IsFDV)) {
Predicate conditionFDVEnum = criteriaBuilder.equal(pli.<FDVEnum> get("FDV"), true);
criteria.add(conditionFDVEnum);
} else {
Predicate conditionFDVEnum = criteriaBuilder.equal(pli.<FDVEnum> get("FDV"), false);
criteria.add(conditionFDVEnum);
}
}
if (conteneurNum != null) {
ParameterExpression<ConteneurNum> parameterConteneurNum = criteriaBuilder.parameter(ConteneurNum.class, "conteneurNum");
Predicate conditionConteneurNum = criteriaBuilder.equal(pli.<ConteneurNum> get("conteneurNum"), parameterConteneurNum);
criteria.add(conditionConteneurNum);
}
if (statut != null) {
ParameterExpression<StatutPli> parameterStatut = criteriaBuilder.parameter(StatutPli.class, "statut");
Predicate conditionStatut = criteriaBuilder.equal(pli.<StatutPli> get("statut"), parameterStatut);
criteria.add(conditionStatut);
}
if (criteria.size() == 0) {
return Pli.findAllPlis();
} else if (criteria.size() == 1) {
c.where(criteria.get(0));
} else {
c.where(criteriaBuilder.and(criteria.toArray(new Predicate[0])));
}
TypedQuery<Pli> q = em.createQuery(c);
if (identifiant != null && !identifiant.trim().equals("")) {
q.setParameter("identifiant", "%" + identifiant + "%");
}
if (dateReceptionFrom != null && dateReceptionTo != null) {
q.setParameter("dateReceptionFrom", dateReceptionFrom);
q.setParameter("dateReceptionTo", dateReceptionTo);
}
if (numeroAR != null && !numeroAR.trim().equals("")) {
q.setParameter("numeroAR", "%" + numeroAR + "%");
}
if (conteneurNum != null) {
q.setParameter("conteneurNum", conteneurNum);
}
if (statut != null) {
q.setParameter("statut", statut);
}
return q.getResultList();
}
Well, I don't think you will be able to Unit Test it as it's strictly specified, but you can make a test using an in memory database (look out for HSQL) so that the app don't need to actually go to the real database just for testing.
That way you will be able to create an automated test that could run inside JUnit for example, mocking maybe only some of the methods.

Nhibernate generating OUTER JOIN for a fetch

Despite setting up mapping to be Not.Nullable() and Not.LazyLoad()
For some reason NH is joining a table twice, once with a INNER JOIN to appease the WHERE, and secondly on a OUTER JOIN to select the data.
Surely, as we've already JOINED the data, it would make sense to just use the joined table...
SELECT
...Tables..
from Tasks taskentity0_,
outer Cases caseentity1_,
outer Grades gradeentit2_,
Cases caseentity5_
WHERE
....
My LINQ query for this is:
IQueryable<TaskEntity> tasks = TaskRepo.Find(
t => t.DueDate <= DateTime.Now
&& (t.TaskInitials == userInitials || (t.TaskInitials == "" || t.TaskInitials == null))
&& t.Team.GST.Any
(x => x.Initials == userInitials
&& x.WorkType.WorkTypeCode == t.WorkType.WorkTypeCode
&& x.Team.TeamCode == t.Team.TeamCode
)
&& (t.Case.CaseOnHold <= DateTime.Now || t.Case.CaseOnHold == null || (t.SingleTask == "M" || t.SingleTask == "m"))
&& (t.Case.CaseMatter.StartsWith("0") || t.Case.CaseMatter.StartsWith("9"))
).Fetch(t => t.Case,FetchProvider)
My Reference Mapping:
References(x => x.Case).Column("ta_c_ref").Not.Nullable();
Thoughts?
We are using the repository pattern, and have reimplemented the Fetch extension method to work this way (Hence passing the FetchProvider in).
Also, QueryOver<T> is not an option here as we require IQueryables..
I am using NH 3.1.
For the masses:
We no longer use the Fetch or LINQ, we moved to HQL...
/// <summary>
/// Interfaces for Fetch() statements
/// </summary>
public interface IFetchingProvider
{
IFetchRequest<TOriginating, TRelated> Fetch<TOriginating, TRelated>(IQueryable<TOriginating> query, Expression<Func<TOriginating, TRelated>> relatedObjectSelector);
IFetchRequest<TOriginating, TRelated> FetchMany<TOriginating, TRelated>(IQueryable<TOriginating> query, Expression<Func<TOriginating, IEnumerable<TRelated>>> relatedObjectSelector);
IFetchRequest<TQueried, TRelated> ThenFetch<TQueried, TFetch, TRelated>(IFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, TRelated>> relatedObjectSelector);
IFetchRequest<TQueried, TRelated> ThenFetchMany<TQueried, TFetch, TRelated>(IFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector);
}
public class NhFetchingProvider : IFetchingProvider
{
public IFetchRequest<TOriginating, TRelated> Fetch<TOriginating, TRelated>(IQueryable<TOriginating> query, Expression<Func<TOriginating, TRelated>> relatedObjectSelector)
{
var fetch = EagerFetchingExtensionMethods.Fetch(query, relatedObjectSelector);
return new FetchRequest<TOriginating, TRelated>(fetch);
}
public IFetchRequest<TOriginating, TRelated> FetchMany<TOriginating, TRelated>(IQueryable<TOriginating> query, Expression<Func<TOriginating, IEnumerable<TRelated>>> relatedObjectSelector)
{
var fecth = EagerFetchingExtensionMethods.FetchMany(query, relatedObjectSelector);
return new FetchRequest<TOriginating, TRelated>(fecth);
}
public IFetchRequest<TQueried, TRelated> ThenFetch<TQueried, TFetch, TRelated>(IFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, TRelated>> relatedObjectSelector)
{
var impl = query as FetchRequest<TQueried, TFetch>;
var fetch = EagerFetchingExtensionMethods.ThenFetch(impl.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
public IFetchRequest<TQueried, TRelated> ThenFetchMany<TQueried, TFetch, TRelated>(IFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector)
{
var impl = query as FetchRequest<TQueried, TFetch>;
var fetch = EagerFetchingExtensionMethods.ThenFetchMany(impl.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
}
public static IFetchRequest<TOriginating, TRelated> Fetch<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, TRelated>> relatedObjectSelector, Func<IFetchingProvider> FetchingProvider)
{
return FetchingProvider().Fetch(query, relatedObjectSelector);
}
I've added support for it!!
https://www.nuget.org/packages/NHibernate.Linq.InnerJoinFetch
Enjoy!!!
Using inner joins with linq in NHibernate isn't supported yet. more info can be found here: https://nhibernate.jira.com/browse/NH-2790

Dynamically Set Column Name In LINQ Query

Im trying to write a method which will allow me to search different DataTables, over different columns.
So far i have the following:
string selectedValue;
string searchColumn;
string targetColumn;
var results = (from a in dt.AsEnumerable()
where a.Field<string>(searchColumn) == selectedValue
select new
{
targetColumn = a.Field<string>(targetColumn)
}).Distinct();
Which kind of gets the job done, but I'm left with the column name as targetColumn rather than the actual column name I want.
Is there any way to resolve this?
Thanks in advance
CM
I make a LINQ to Datatables
public List<DataRow> Where(this DataTable dt, Func<DataRow, bool> pred)
{
List<DataRow> res = new List<DataRow>();
try {
if (dt != null && dt.Rows.Count > 0) {
for (i = 0; i <= dt.Rows.Count - 1; i++) {
if (pred(dt(i))) {
res.Add(dt(i));
}
}
}
} catch (Exception ex) {
PromptMsg(ex);
}
return res;
}
Usage :
var RowsList = dt.Where(f => f("SomeField").toString() == "SomeValue" ||
f("OtherField") > 5);

Resources