HQL Query 2 sub classes with a property by the same name - nhibernate

I have 3 classes like:
public class Animal
{
... all sort of data ...
}
public class Cat : Animal
{
... more data ...
public virtual int Size { get; set; }
}
public class Dog : Animal
{
... other data ...
public virtual int Size { get; set; }
}
The classes are mapped using the "table per hierarchy" strategy in the hbm.xml mapping the Cat's Size property is mapped to a CatSize column and the Dog's property to DogSize
The following two queries work as expected:
From Cat o Where o.Size = 1
From Dog o Where o.Size = 1
Now I want to query Animal for all the cats where Size = 1 and all the dogs where size = 1, combining the two queries above I get:
From Animal Where o.Size = 1 Or o.Size = 1
That obviously doesn't do what I want, how can I tell NHibernate the first Size is Cat.Size and the second is Dog.Size (preferably without knowing the column names in the database).
Note: I understand the design is problematic an having two properties with the same name in different subclasses is not the smartest design decision ever made - but changing it now is even more problematic and I'd like to avoid it (the project should be delivered to the client in a few days and I really don't want to make design changes now).
I'm using NHibernate 2.0.1 (upgrading is not an option, the program does not work with later versions of NHibernate and we don't have time to fix it now).

Try this...
select a from Animal a where a in (select c from Cat c where c.Size = 1) or a in (select d from Dog d where d.Size = 1)

Why didn't you define the Size property on the Animal class ?
You can use a MultiQuery to execute the 2 queries in one roundtrip, and that will give you the results as well.
I think that this will be the best solution ...
IMultiQuery q = session.CreateMultiQuery();
q.Add("from Cat where Size = 1");
q.Add("from Dog where Size = 1");
var results = q.List();
var cats = (IList)results[0];
var dogs = (IList)results[1];

Related

How to create filters in Spring boot based on a List

I have to created filters data in spring boot. From frontEnd, I am sending a list containing the selected id of each category.I need to return items based on this list. I can do it this way
Service:
public List<ProductModel> getProductFilter(Integer[] categories) {
int size = categories.length;
if(size == 1){
return productRepository.getProductFilteredByOneCategory(Long.valueOf(categories[0]));
}else {
return productRepository.getProductFilteredByTwoCategory(Long.valueOf(categories[0]),Long.valueOf(categories[1]));
}
}
}
Repository:
#Query("SELECT c FROM ProductModel c WHERE c.categoryModel.id = :Category_id")
List<ProductModel> getProductFilteredByOneCategory(Long Category_id);
#Query("SELECT c FROM ProductModel c WHERE c.categoryModel.id = :Category_idOne OR c.categoryModel.id = :Category_idTwo")
List<ProductModel> getProductFilteredByTwoCategory(Long Category_idOne, Long Category_idTwo);
But if there are 50 of these categories, it is useless. Can anyone tell me how to make it better? There is some way to generate a query from a list?
You can use in instead of using multiple or operations as follows. It select all productModels match any categoryModel id in List.
#Query("SELECT c FROM ProductModel c WHERE c.categoryModel.id in category_ids")
List<ProductModel> getProductFilteredByCategoryIds(List<Long> Category_ids);
As #YJR said, IN clause is the solution, but you should also consider declaring query method as shown below, which doesn't require writing JPQL.
public List<ProductModel> findByCategoryModel_IdIn(List<Long> categoryIds);

Self Creating Objects in a Class

I'm just getting to grips with Object Oriented Programming and I am stuck on how multiple objects are created without explicit defining in the code. For example in a while loop, setting a name and age for a dog, creating an object for it, and then giving it a unique identifier.
This is probably a simple question and I am missing something obvious but I can't get my head around it and some help would be appreciated. I have tried looking before but I can't find anything that helps.
Thanks
Edit:
I've been asked for some code as an example. Strictly speaking, I'm not stuck on a specific problem, more of a general question but I was thinking of something along the lines of the following
class Dog(self,name,age):
#Class stuff
while True:
dog_name=input("Enter dog name: ")
dog_age=input("Enter dog age: ")
dog=Dog(dog_name,dog_age)
My aim of this is to create multiple dog objects with different names/ages. However, I am worried that I will not be able to target a specific dog later in the code since they will all be saved as "dog". Is there any way of looping this to create something along the effect of dog1, dog2, dog3 etc. or is there another way of doing it entirely?
Good question! I would recommend using a list, which has slightly different syntax in different languages, looks like python so I'll give a python example
dogs = [] # this starts out as an empty list
while True:
dog_name = input("Enter dog name: ")
dog_age = input("Enter dog age: ")
dogs.append(Dog(dog_name, dog_age)) # creates a dog then adds it to the list
Say you do this and enter 3 dogs: Hope, Betsy, and Basil. They are 6, 2, and 12 respectively
then dogs[0] is the first dog (lists start at 0). dogs[0].name is Hope, and dogs[0].age is 6
dogs[1].name is Betsy, and so on
Based on your example, you're absolutely correct: you'd be unable to access each Dog instance after leaving the loop it was created in.
Using a class definition like:
public class Dog
{
public string Name { get; set; }
public int Age { get; set; }
}
There is nothing stopping you from instantiating an object of type Dog inside a loop:
using System;
while(true)
{
var dog = new Dog();
dog.Name = Console.ReadLine("Enter dog name: ");
dog.Age = int.TryParse(Console.ReadLine("Enter dog age: "), out int age) ? age : 0;
}
The class Dog still exists after each loop, and we recreate the dog variable each iteration, but the value of dog is not the same and is not recoverable.
If you need to use the value of each instance later, you will need to save them as you are executing each iteration of the loop.
This may look like saving every Dog:
using System;
using System.Collections.Generic;
var dogs = new List<Dog>();
while(true)
{
var dog = new Dog();
dog.Name = Console.ReadLine("Enter dog name: ");
dog.Age = int.TryParse(Console.ReadLine("Enter dog age: "), out int age) ? age : 0;
// Save this instance of the Dog class.
dogs.Add(dog);
}
foreach (Dog d in dogs)
{
Console.WriteLine($"{d.Name} is a good dog.");
}
Or saving just the oldest Dog:
using System;
var oldestDog = new Dog();
while(true)
{
var dog = new Dog();
dog.Name = Console.ReadLine("Enter dog name: ");
dog.Age = int.TryParse(Console.ReadLine("Enter dog age: "), out int age) ? age : 0;
// Save this instance of the Dog class if
// it is older than the current oldest dog
// (or if it's the first dog).
if (oldestDog is null || dog.Age > oldestDog.Age)
{
oldestDog = dog;
}
}
Console.WriteLine($"The oldest dog was {oldestDog.Age} year(s) old.");
Microsoft has a free course on variable scoping in C# if you are looking for a good place to learn more about scope in Object-Oriented languages.

LINQ or Navigation Properties command to retrieve 1 to many data

I am looking for help with a LINQ SQL query please.
I have a blazor application that gets data from an Azure SQL database. I am seeking to get a dataset from the database for linking to a datagrid, where each row is a record from the main table joined with a record from the second table. The second table has millions of records, it needs to join one record which has the same key (securityId) and with the date being the record with the nominated date, or with the last date before the nominated date.
Because of the size of the 2nd file, I need an efficient query. Currently I am using the following, but I believe there must be more efficient ways to do it without the lag. Also tried Navigation Properties but couldn't get to work
reviewdateS is the date that I want the 2nd record to match or be the latest date prior to that date
result = (from cmpn in _ctx.MstarCompanies
join prcs in _ctx.MstarPrices
on cmpn.securityId equals prcs.securityId into cs
from c in cs.DefaultIfEmpty()
where c.date01 == reviewDateS
select new ClsMarketPrices { })
Following are the 3 relevant classes. ClsMarketPrices does not relate to a database table, it is simple a class that combines the other 2 classes which may not be necessary but with my limited knowledge it is how it is working.
_ctx is a repository that links to the data context.
public MySQLRepositories(ApplicationDbContext ctx)
{
_ctx = ctx;
}
public class ClsMarket
{
[Key]
public int CompanyId { get; set; } = 0;
public string securityId { get; set; } = "";
public string companyName { get; set; } = "";
public string mic { get; set; } = "";
public string currency { get; set; } = "";
[ForeignKey("securityId")]
public virtual ICollection<ClsPrices> Prices { get; set; }
}
public class ClsMarketPrices
{
[Key]
public int CompanyId { get; set; } = 0;
public string companyName { get; set; } = "";
public string period { get; set; } = "";
public string mic { get; set; } = "";
}
public class ClsPrices
{
[Key]
public int PricesId { get; set; }
[ForeignKey("securityId")]
public string securityId { get; set; } = "";
public string mic { get; set; } = "";
public string date01 { get; set; } = "";
public virtual ClsMarket ClsMarket {get; set;}
}
I want to get a record from the 1st file joined with a record from the 2nd file where that record from the 2nd file has a date equal to or the last before the nominated date.
So we are talking about files, not a database! This is important, because this means that your local process will execute the LINQ, not a database management system. In other words: the LINQ will be IEnumerable, not IQueryable.
This is important, because as Enumerable, you will be able to define your own LINQ extension methods.
Although you supplied an enormous amount of irrelevant properties, you forgot to give us the most important things: you were talking about two files, you told us that you have two classes with a one-to-many relation, but you gave us three classes. Which ones do have the relation that you are talking about?
I think that every object of ClsMarketPrices has zero or more ClsPrices, and that every ClsPrice is one of the prices of a ClsMarketPrices, namely the ClsMarketPrices that the foreign key SecurityId (rather confusing name) refers to.
First of all, let's assume you already have procedures to read the two sequences from your files. And of course, these procedures won't read more than needed (so don't read the whole file if you will only use the first ClsMarket). I assume you already know how to do that:
IEnumerable<ClsMarketPrices> ReadMarketPrices();
IEnumerable<ClsPrices> ReadPrices();
So you've go a DateTime reviewDate. Every MarketPrice has zero or more Prices. Every Price has a DateTime property DateStamp. You want for every MarketPrice the Price that has the largest value for DateStamp that is smaller or equal to reviewDate.
If a MarketPrice doesn't have such a Prices, for instance because it doesn't have a Price at all, or all its Prices have a DateStamp larger than reviewDate, you want a value null.
You didn't say what you want if a MarketPrice has several Prices with equal largest DateStamp <= reviewDate. I assume that you don't care which one is selected.
The straighforward LINQ method would be to use GroupJoin, Where, Orderby and FirstOrDefault:
DateTime reviewDate = ...
IEnumerable<ClsMarketPrices> marketPricess = ReadMarketPrices();
IEnumerable<ClsPrices> prices = ReadPrices().Where(price => price.DateStamp <= reviewDate);
// GroupJoin marketPrices with prices:
var result = markets.GroupJoin(prices,
marketPrice => marketPrice.CompanyId, // from every MarketPrice take the primary key
price => price.CompanyId, // from every price take the foreign key to its market
// parameter resultSelector: from every market, with its zero or more matching prices
// make one new:
(marketPrice, pricesOfThisMarketPrice) => new
{
// select the marketPrice properties that you plan to use:
Id = marketPrice.CompanyId,
Name = ...
...
// from all prices of this marketPrice, take the one with the largest DateStamp
// we know there are no marketPrices with a DataStamp larger than reviewData
LatestPrice = pricesOfThisMarketPrice.OrderbyDescending(price => price.DateStamp)
.Select(price => new
{
// Select the price properties you plan to use;
Id = price.PricesId,
Date = price.DateStamp,
...
})
.FirstOrDefault(),
});
The problem is: this must be done efficiently, because you have an immense amount of Markets and MarketPrices.
Althoug we already limited the amount of prices to sort by removing the prices that are after reviewDate, it is still a waste to order all Dates if you will only be using the first one.
We can optimize this, by using Aggregate for pricesOfThisMarketPrice. This will assert that pricesOfThisMarketPrice will be enumerated only once.
Side remarks: Aggregate only works on IEnumerable, not on IQueryable, so it won't work on a database. Furthermore, pricesOfThisMarketPrice might be an empty sequence; we have to take care of that.
LatestPrice = pricesOfThisMarketPrice.Any() ?
pricesOfThisMarketPrice.Aggregate(
// select the one with the largest value of DateStamp:
(latestPrice, nextPrice) => nextPrice.DateStamp >= latesPrice.DateStamp) ? nextPrice : latestPrice)
// do not do the aggregate if there are no prices at all:
: null,
Although this Aggregate is more efficient than OrderBy, your second sequence will still be enumerated more than once. See the source code of Enumerable.GroupJoin.
If you really want to enumerate your second source once, and limit the number of enumerations of the first source, consider to create an extension method. This way you can use it as any LINQ method. If you are not familiar with extension methods, see extension methods demystified.
You can create an extension method for your ClsPrices and ClsPrice, however, if you think you will need to "find the largest element that belongs to another element" more often, why not create a generic method, just like LINQ does.
Below I create the most extensive extension method, one with a resultSelector and equalityComparers. If you will use standard equality, consider to add an extension method without these comparers and let this extension method call the other extension method with null value for the comparers.
For examples about the overloads with and without equality comparers see several LINQ methods, like ToDictionary: there is a method without a comparer and one with a comparer. This first one calls the second one with null value for comparer.
I will use baby steps, so you can understand what happens.
This can slightly be optimized.
The most important thing is that you will enumerate your largest collection only once.
IEnumerable<TResult> TakeLargestItem<T1, T2, TKey, Tproperty, TResult>(
this IEnumerable<T1> t1Sequence,
IEnumerable<T2> t2Sequence,
// Select primary and foreign key:
Func<T1, TKey> t1KeySelector,
Func<T2, TKey> t2KeySelector,
// Select the property of T2 of which you want the largest element
Func<T2, TProperty> propertySelector,
// The largest element must be <= propertyLimit:
TProperty propertyLimit,
// From T1 and the largest T2 create one TResult
Func<T1, T2, TResult> resultSelector,
// equality comparer to compare equality of primary and foreign key
IEqualityComparer<TKey> keyComparer,
// comparer to find the largest property value
IComparer<TProperty> propertyComparer)
{
// TODO: invent a property method name
// TODO: decide what to do if null input
// if no comparers provided, use the default comparers:
if (keyComparer == null) keyComparer = EqualityComparer<TKey>.Default;
if (propertyComparer == null) propertyComparer = Comparer<TProperty>.Default;
// TODO: implement
}
The implementation is straightforward:
put all T1 in a dictionary t1Key as key, {T1, T2} as value, keyComparer as comparer
then enumerate T2 only once.
check if the property <= propertyLimit,
if so, search in the dictionary for the {T1, T2} combination with the same key
check if the current t2Item is larger than the T2 in the {T1, T2} combination
if so: replace
We need an internal class:
class DictionaryValue
{
public T1 T1 {get; set;}
public T2 T2 {get; set;}
}
The code:
IDictionary<TKey, DictionaryValue> t1Dict = t1Sequence.ToDictionary(
t1 -> t1KeySelector(t1),
t1 => new DictionaryValue {T1 = t1, T2 = (T2)null },
keyComparer);
The enumeration of t2Sequence:
foreach (T2 t2 in t2Sequence)
{
// check if the property is <= propertyLimit
TProperty property = propertySelector(t2);
if (propertyComparer.Compare(property, propertyLimit) < 0)
{
// find the T1 that belongs to this T2:
TKey key = keySelector(t2);
if (t1Dict.TryGetValue(key, out DictionaryValue largestValue))
{
// there is a DictionaryValue with the same key
// is it null? then t2 is the largest
// if not null: get the property of the largest value and use the
// propertyComparer to see which one of them is the largest
if (largestValue.T2 == null)
{
largestValue.T2 = t2;
}
else
{
TProperty largestProperty = propertySelector(largestValue.T2);
if (propertyComparer.Compare(property, largestProperty) > 0)
{
// t2 has a larger property than the largestValue: replace
largestValue.T2 = t2,
}
}
}
}
}
So for every t1, we have found the largest t2 that has a property <= propertyLimit.
Use the resultSelector to create the results.
IEnumerable<TResult> result = t1Dict.Values.Select(
t1WithLargestT2 => resultSelector(t1WithLargestT2.T1, t1WithLargestT2.T2));
return result;

Get all children of a node with Linq in VB.net

I have Hierarchical datatable for filling TreeList in VB.Net like following:
ID ParentID Name
----------------------
1 NUll a
2 NUll b
3 2 c
4 1 d
5 3 e
6 5 f
7 6 g
8 5 h
My question:
How Can get list of all children for a node(ID) with Linq in vb.net?
Please help me.
Unfortunately I don't know VB good enough to give you a VB answer. I'll give the answer in C#. You'll probably understand the Idea. Maybe you can add your VB-translation?
You forgot to describe your Node class. I assume you do not only want the Children, but also the Grandchildren (etc).
class TreeNode
{
public int Id {get; set;}
public string Name {get; set;}
// every node has zero or more sub-nodes:
public IEnumerable<TreeNode> Nodes {get; set;}
}
You want your hierarchy to an unknown level deep. Hence you can't use the Standard LINQ functions. But you can easily extend LINQ to create your own. See Extension Function demystified
public static IEnumerable<TreeNode> AsTreeNodes(this IEnumerable<Person> persons)
{
// Top TreeNodes: all persons without a Parent:
return persons.AsTreeNodes((int?)null);
}
The actual function that returns the sequence of Nodes, of all persons with parentId, using recursion:
public static IEnumerable<TreeNode> AsTreeNodes(this IEnumerable<Person> persons, int? parentId)
{
// Top Nodes: all persons with parentId
var personsWithParentId = persons.Where(person.ParentId == parentId);
foreach (var person in personsWithParentId)
{
// every person will become one TreeNode with sub-nodes using recursion
TreeNode node = new TreeNode()
{
Id = person.Id,
Name = person.Name,
// get all my Children and Children's children:
Nodes = persons.ToNodeCollection(person.Id),
};
yield return node;
}
}
Note that I choose to return IEnumerable instead of List / Array. This way items will only be created when you really enumerate over them. So if you don't want to use the Node of the person with Id 1, all his children will not be created.
Usage: get the family hierarchy of all descendants of "George Washington":
IEnumerable<Person> allPersons = ...
IEnumerable<TreeNode> allFamilyHierarchies = allPersons.AsTreeNodes();
IEnumerable<TreeNode> washingtonFamily = allFamilyHierarchies
.Where(familyHierarchy => familyHierarchy.Name == "George Washington");
Note: until now, no TreeNode has been created, only the IEnumerable has been created.
The enumeration starts as soon as you do something that does not return IEnumerable,
like foreach, ToList, Any, FirstOrDefault.
Hence all non-Washington families will be ignored, no Top nodes, nor sub-nodes for them will be created.
If you look closely, then you'll see that to find all Children I have to enumerate over the complete collection, to find all Persons with a certain ParentId. This can be done way more efficiently if you first group all persons into groups with same ParentId and then put these groups into a Dictionary with the ParentId as Key. This way it will be very fast to find all Children of the parent with Id X:
var personsWithSameParentId = persons.GroupBy(person => person.ParentId);
var dictionary = personsWithSameParentId.ToDictionary(group => group.Key)
Every element in the dictionary has key equal to parentId and as elements all persons with this parentId.
TreeNode CreateNodeForPerson(Person person)
{
IEnumerable<Person> children = dictionary[person.Id];
IEnumerable<TreeNode> childNodes = children
.Select(child => CreateNodeforPerson(child));
return new TreeNode()
{
Id = person.Id,
Name = person.Name,
Nodes = childNodes,
};
}
You'll see the same recursion. But once you have the dictionary, you don't have to enumerate over the complete Person collection, you only access the children / children's children etc of the Person you are creating the nod for.

Retrieving entities by checking a filtered subset of childen

I have entities of the following structure:
#Entity
public Class MyObject {
...
public List<Status> statuses;
...
}
#Entity
public Class Status {
...
public Long creationTime;
public Double range;
#JoinColumn(name = "myObject",
nullable = false,
unique = false,
updatable = false)
#ManyToOne(optional = false)
#NotNull
private MyObject myObject;
...
}
I got n entities of the type MyObject which can have m statuses. Ownership lays on the side of the Status entity which cannot be changed. I am currently trying to filter the entities that are greater-equal than a certain range. My current query retrieves a result for every status that has a range that is greater-equal than the one given as a parameter:
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<MyObject> cq = cb.createQuery(MyObject.class);
final Root<MyObject> myObject = cq.from(MyObject.class);
final Root<Status> statuses = cq.from(Status.class);
final Predicate[] predicates = {
cb.equal(myObject.get(MyObject_.id), statuses.get(Status_.myObject).get(MyObject_.id)),
cb.ge(statuses.get(Status_.range), range)
};
cq.select(myObject).where(predicates);
return entityManager.createQuery(cq).getResultList();
Desired is, that I only get the entities of MyObject, for which the most current status that has a range is greater-equal than the one given as a paramter. I am pretty sure that I have to group the list of statuses first and sort by creation time but I am not really sure how to combine all of this, especially not in the Criteria API.
EDIT:
At least I was able to create a SQL query that does what I want:
SELECT * FROM MyObject O INNER JOIN (SELECT * FROM Status WHERE Status.CREATIONTIME IN (SELECT MAX(Status.CREATIONTIME) FROM Status WHERE (Status.RANGE IS NOT NULL AND Status.RANGE >= 30) GROUP BY Status.myObject)) S ON O.ID = S.myObject;
I am trying to rebuild this query using the Criteria API but don't really know how to include the embedded SELECT in the JOIN. Any help would be appreciated!