How to query a collection that can be null in JPQL? - spring

Say I have an entity like this
class Parent {
#OneToMany(mappedBy = "par")
Set<Child> children
String stuff;
}
class Child {
#ManyToOne
#JoinColumn(name="par_id", nullable=false)
private Parent par;
String value;
}
I want to have a query like this:
Select DISTINCT par from Parent par LEFT JOIN par.children chi
WHERE
( par.stuff = :stuff or (:stuff is null))
AND ((chi is not empty) and chi.value = :value))
But this gives me back parents that have empty children.
I need to select all Parent that have a set of non empty children AND also children matching value = x

You can try using inner join to make sure children exists:
select distinct par from Parent par join par.children chi where chi.value = :value
You can use exists operator:
select distinct par from Parent par
where exists
(select chi
from Child chi
where chi.value = :value and chi.parent = par)

Related

LINQ Left Outer Join with Greater Than and Less Than Date Conditions

I've been struggling with this for a while and can't find the syntax for a LINQ outer join that has multiple conditions based on date. I've been looking into the GroupJoin syntax, but that only let's you compare one field value (normally IDs).
I would like to test if the parent table has a date (e.g. "UpdateDate") that falls within multiple values defined in the child table (e.g. "StartDate" and "EndDate"). If the parent date fits the condition, pull a column or two from the child table. If not, those columns from the child table should be null (classic left join stuff).
I don't think query syntax will work because it only recognizes equijoins.
Is there a way to do this in LINQ using Lambda syntax? I've been trying to use some combination of "SelectMany" and "DefaultIfEmpty" but keep getting stuck trying to define the join.
The way to do this in linq:
var q = from a in TableA
from b in TableB.where(x => a.Date > x.StartDate && a.Date < x.EndDate).DefaultIfEmpty()
select {...}
Use parameter ResultSelector of Queryable.GroupJoin to select what you want:
var result = dbContext.Parents.GroupJoin(dbContext.Children,
// outer and inner key Selectors:
parent => parent.Id, // from every parent take the primary key
child => child.ParentId, // from every child take the foreign key to parent
// ResultSelector: take the parent and all his children to make one new object
(parent, children) => new
{
// Select only the Parent properties you actually plan to use:
Id = parent.Id,
Name = parent.Name,
...
Children = children.Select(child => new
{
// select only Child properties you plan to use:
Id = child.Id,
// No need: you know the value: ParentId = child.ParentId,
...
"If the parent date fits the condition, pull a column or two from the child table, otherwise those columns from the child table should be null "
SpecialColumnA = (parent.BirthDay.Year < 2000) ?? child.BirthDay : null,
SpecialColumnB = (parent.Name == "Kennedy" ?? child.Name : null,
});
If the conditions are the same for a lot of columns, consider to check this only once:
SpecialColumns = (parent.Birthday.Year >= 2000) ? null :
// else fill the special columns:
new
{
Name = child.Name,
SomeWeirdProperty = parent.Id + child.Id,
...
},
});

how do i union int? and int variables in linqpad

In LinqPad
Getting the following error trying to union int? and int variables
Researched but can't find a solution that seems to work.
CS1929 'IQueryable<>' does not contain a definition for 'Union' and the best extension method overload 'ParallelEnumerable.Union<>(ParallelQuery<>, IEnumerable<>)' requires a receiver of type 'ParallelQuery<>'
//Parent not null
var parent =
from s in Students
where s.Id==5027
select new {
ID_PK = s.CaseOwnerIdAspnet_Users.User_ID_FKDYN_User_Profile.Organization_ID_FKDYN_Organization.Parent_ID_FK == null ?
s.CaseOwnerIdAspnet_Users.User_ID_FKDYN_User_Profile.Organization_ID_FKDYN_Organization.ID_PK
:
s.CaseOwnerIdAspnet_Users.User_ID_FKDYN_User_Profile.Organization_ID_FKDYN_Organization.Parent_ID_FK
};
var orgs =
from o in DYN_Organizations
join p in parent on o.Parent_ID_FK equals p.ID_PK
select new {ID_PK = o.ID_PK};
parent.Union(orgs);
Suppose parent is returning int? and orgs is returning int, then you can change orgs to return int? by casting the value, eg
change it to
var orgs =
from o in DYN_Organizations
join p in parent on o.Parent_ID_FK equals p.ID_PK
select new {ID_PK = (int?) o.ID_PK};
Your error message also mentions ParallelQuery and IEnumerable and I'm not sure if you can create a union between this types. If not, then the easiest way is to add .ToList() to the end of each query and then you are creating a union between two Lists which will work.

Hibernate: LazyLoading - Search by Object in One-To-Many Collection

I got an Entity 1 -> SubEntity n One-To-Many-relation.
I just would like to check, if this is the most efficient way to search for an Entity, including a specified SubEntity in its "One-To-Many-Collection". It works, but do I have to join fetch the SubEntities or is there a more lightweight solution if I don't need all SubEntities to be loaded? (FetchMode = Lazy)
public Entity getEntityBySubEntity(SubEntity subEntity) {
List<Entity> result = (List<Entity>)getHibernateTemplate.findByNamedParam(
"From Entity as e left join fetch e.subEntities as sub where sub.id = :id","id",subEntity.getId());
if (!result.isEmpty()) {
return result.get(0);
} else {
throw new NoResultException();
}
}
(by the way, there should always be just one result...)
thx in adv,
cav
Your query is fine, but needlessly fetches the subEntities. Just remove the fetch keyword. And since the sub-entity can't be null to satisfy the condition, an inner join is OK:
select e from Entity e inner join e.subEntities sub where sub.id = :id

LINQ Query - how sort and filter on eager fetch

How do I do a eager query of a parent child relationship that:
filters a on child fields
sorts on both parent and child
return a List or Parents with the children pre-populated
If I try
from p in _context.Parents.Include("children")
join c in _context.childrenon p.Id equals c.ParentId
where d.DeletedDate == null
orderby p.Name ascending, c.Name
select p
Then I get the Parent object back but each Parent has NULL for children
if I try
from p in _context.Parents.Include("children")
orderby p.Name ascending
select p
The query returns all Parents and children but they are not filtered or sorted.
The result I want back is a IEnumerable<Parent>
i.e.
Parent[0].name = "foo"
Parent[0].children = IEnumerable<Child>
Parent[1].name = "bar"
Parent[1].children = IEnumerable<Child>
There is no direct way of doing this, but you can use somewhat of a workaround - project the parent and children onto an annonymous object and then select and return the parent from the object.
See similar question: Linq To Entities - how to filter on child entities
In your case you will have something along the lines of:
var resultObjectList = _context.
Parents.
Where(p => p.DeletedDate == null).
OrderBy(p => p.Name).
Select(p => new
{
ParentItem = p,
ChildItems = p.Children.OrderBy(c => c.Name)
}).ToList();
List<Parent> resultingCollection = resultObjectList.Select(o => o.ParentItem).ToList();
The solution depends on what exactly you are trying to do.
The first query gives the impression that you want to "flatten out" the results in objects, like this (pseudocode, I hope it's clear what I mean):
{ Parent1, Child1 }
{ Parent1, Child2 }
{ Parent1, Child3 }
{ Parent2, Child1 }
In this case each result "row" would be an object having a Parent and a Child property, and you could sort by parent name and then by child name.
The second query just returns the Parent objects and (you don't show it but I assume EF has been instructed to do that) each one has a Children collection. In this case you can only sort by parent name; if you want to sort each Parent's children, sort the Children collection on that object by itself.
Which of the two do you want to do?
Update
OK, it seems you want the second one. I don't believe it can be done directly. You can just do it when you enumerate the results - since the Parents are already sorted, simply sort each one's children:
var sortedChildren = parent.Children.OrderBy(c => c.Name);
prefetching child fields:
using (BlogDataContext context = new BlogDataContext())
{
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Blog>(c => c.Categories);
options.LoadWith<Blog>(c => c.Title);
context.LoadOptions = options;
Blog blog = context.Blogs.Single<Blog>(c => c.BlogId == 1);
}

linq subquery returning null

I have an odd linq subquery issue.
Given the following data structure:
Parents Children
------- --------
Id Id
ParentId
Location
HasFoo
(obviously this is not the real structure, but it's close enough for this example)
I'm able to run this query and get a desired result:
bool b = (from p in Parents
from c in Children
where p.Id == 1 && c.ParentId == p.Id && c.Location == "Home"
select c.HasFoo).SingleOrDefault();
So if there is a child that has the Location "Home" for a Parent of Id 1, I will get that Child's "HasFoo" value, otherwise, I'll get false, which is the "default" value for a bool.
However, if I try and write the query so I have a list of Parent objects, like so:
var parentList = from p in Parents
select new ParentObject
{
ParentId = p.ParentId,
HasHomeChildren = p.Children.Count(c => c.Location == "Home") > 0,
HasHomeChildrenWithFoo = (from c in p.Children where c.Location == "Home" select c.HasFoo).SingleOrDefault()
}
I get the following error when iterating over the list:
The null value cannot be assigned to a member with type System.Boolean which is a non-nullable value type.
I don't see where this "null" value is coming from, however.
I wonder if the compiler is inferring HasHomeChildrenWithFoo to be bool, but then actually casting to a nullable bool (thus messing up your SingleOrDefault call). At any rate, I'd be willing to bet you could fix it with a cast to a nullable type in that final select which you can then manually default to false when null. It'd probably make the error go away, but it's kind of a brute-force kludge.
var parentList = from p in Parents
select new ParentObject
{
ParentId = p.ParentId,
HasHomeChildren = p.Children.Any(c => c.Location == "Home"),
HasHomeChildrenWithFoo = (from c in p.Children where c.Location == "Home" select (bool?)c.HasFoo) ?? false)
}

Resources