Having an entity Items that have a collection field called categories, I need to find items that belong at least to two specific categories, but I cant find the way to build the correct JPQL query.
#Entity
public class Items {
#ManyToMany (fetch = FetchType.EAGER)
private List<Category> categories;
}
I can find an item that have one category:
SELECT i FROM Item i WHERE :cat MEMBER OF item.categories
I can select items with any of several categories:
SELECT i FROM Item i WHERE :cat1 MEMBER OF item.categories OR :cat MEMBER OF item.categories
But when I try to select for items that have at least two specific categories, the following query gets no items:
SELECT i FROM Item i WHERE :cat1 MEMBER OF item.categories AND :cat2 MEMBER OF item.categories
Which is the correct way to do that?
Best regards,
Pablo.
That problem happened to me with ObjectDB.
I have contacted them too, and this was the answer:
This seems to be the result of how JPQL queries are converted into SQL
like syntax before executing. MEMBER OF is implemented using JOIN with
a new synthetic variable for iteration over the collection. In your
query item.categories appears twice, but the same synthetic variable for
iterating over that collection is used for both occurrences, and that
variable cannot match both sides of the AND with the same value.
Possibly we may have to use separate iteration per MEMBER OF as the
results as demonstrated by your post seem unacceptable (although JQPL
itself has strange behaviour in some known cases due to the conversion
to JOIN). However, using separate variables may sometimes make the
query more complex and slow unnecessarily (e.g. for the OR query in
your post), so any change requires careful planning.
As a quick solution, you may replace AND of MEMBER OF with 2 explicit
JOIN variables for iteration over the collection with 2 independent
variables.
So, the solution is to use the following query:
SELECT DISTINCT item
FROM Item item JOIN item.categories cat1 JOIN item.categories cat2
WHERE cat1 = :cat1 AND cat2 = :cat2
I don't know if this a problem only for this specific JPA implementation (ObjectDB).
Anyway, if anybody has a similar problem, I hope that this post can help.
Related
I am developing an API where I am confused as to what is the efficient way to handle join query.
I want to join 2 tables data and return the response. Either I can query the database with join query and fetch the result and then return the response OR I can fire two separate queries and then I would handle the join in the API on the fly and return the response. Which is the efficient and correct way ?
Databases are pretty much faster than querying and joining as class instances. Always do joins in the database and map them from the code. Also look for any lazy loading if possible. Cause in a situation like below:
#Entity
#Table(name = "USER")
public class UserLazy implements Serializable {
#Id
#GeneratedValue
#Column(name = "USER_ID")
private Long userId;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private Set<OrderDetail> orderDetail = new HashSet();
// standard setters and getters
// also override equals and hashcode
}
you might not want order details when you want the initial results.
Usually it's more efficient to do the join in the database, but there are some corner cases, mostly due to the fact that application CPU time is cheaper than database CPU time. Here are a few examples that come to mind, with a query like "table A join table B":
B is a small table that rarely changes.
In this case it can be profitable to cache the contents of this table in the application, and not query it at all.
Rows in A are quite large, and many rows of B are selected for each row of A.
This will cause useless network traffic and load as rows from A are duplicated many times in each result row.
Rows in B are quite large, and there are few distinct b_id's in A
Same as above, except this time the same few rows from B are duplicated in the result set.
In the previous two examples, it could be useful to perform the query on table A, then gather a set of unique b_id's from the result, and SELECT FROM b WHERE b_id IN (list).
Data structure and ORMs
If each table contains a different object type, and they have a "belongs to" relationship (like category and product) and you use an ORM which will instantiate objects for each selected row, then perhaps you only want one instance of each category, and not one per selected product. In this case, you could select the products, gather a list of unique category_ids, and select the categories from there. The ORM may even do that for you behind the scene.
Complicated aggregates
Sometimes, you want some stuff, and some aggregates of other stuff related to the first stuff, but it just won't fit in a neat GROUP BY, or you may need several ones.
So basically, usually the join works better in the database, so that should be the default. If you do it in the application, then you should know why you're doing it, and decide it's a good reason. If it is, then fine. I gave a few reasons, based on performance, data model, and SQL constraints, these are only examples of course.
I have 2 types of collections:
Classes
Students
Each class can have multiple students and vice versa.
What is my best way to store and fetch all students in a class (or the other way around)?
Here's a snapshot of a class:
What I did so far is to have a field of type array for each item that should contain the list of IDs of the students/classes. But using whereField to retrieve the students is no good since it's limited to 10 only. Also, for some reason, even when I fetch the 9 students for a class it won't reflect changes in real-time when I change the data in Firestore so I suspect there's a better way to do this.
I also tried to fetch all students and loop on this list against the classes list and append it to a new array - but this works very poorly and again, I suspect it's useless overhead.
Is there a good practice for my need?
In case anyone will ever seek a solution for this - here's the approach that I took and worked.
My first (complex and not so good) approach was:
Fetch students array from a class
Fetch students from students collection base on the students' array
(*this won’t work if you have more than 10 items in the array)
If you have more than 10 items, fetch all students, loop on them against the students' array, and filter out whichever student is not in the array.
The approach that worked:
For every student in the class, add the classID
Fetch all students and use whereField to filter by classID
.whereField("students", arrayContains: classID)
Here's a diagram of everything:
I am wondering how to create predicate which will filter entities which collection property contains elements from collection given as parameter using JPA specification.
In this example I am building query for UserEntity to return users which belong to groups contained in parameter List.
List<String> groups = query.getGroups();
predicates.add(cb.isTrue(r.get("groups").in(groups)));
But if I understand it correctly it will return true if all or less groups are in this list.
I am looking for solution where a) all elements from list are in entity collection b) at least one (or like way)
Is there some easy way to achieve this? In fact I don't really need anything from GroupEntity so I don't think joining is necessary
Thanks for help
Is it possible to include a computed field in an EF entity? Example, lets say I have a shop selling products, and the products are grouped into categories.
When the list of categories is displayed for editing to the administrator of the shop I wish to list the number of products in each category as part of the list.
In NHibernate I would use a formula e.g.
<property name="Products" formula="dbo.Category_NumProducts(id)" />
I can't work out if something similar is possible for EF. I know I could make a category view and map this to a second entity but that seems wrong as its almost a complete duplication.
EDIT: If this isn't possible in EF, then what is the best way to accomplish this?
Unfortunately if your Category table doesn't have this as computed column you will not be able to map it without creating second entity - it leads to database view, defining query in EDMX or perhaps Query view in EDMX (but Query view may not work because you require aggregate function to be computed).
Edit:
IMHO the simplest solution (and also the best) is simply creating a ViewModel (some non mapped class) and use projection in Linq query:
var query = from c in context.Categories
where ...
select new CategoryView {
Id = c.Id,
Name = c.Name,
...
Products = c.Products.Count()
};
Greetings,
Considering the Northwind sample tables Customers, Orders, and OrderDetails I would like to eager load the related entities corresponding to the tables mentioned above and yet I need ot order the child entities on the database before fetching entities.
Basic case:
var someQueryable = from customer in northwindContext.Customers.Include("Orders.OrderDetails")
select customer;
but I also need to sort Orders and OrderDetails on the database side (before fetching those entities into memory) with respect to some random column on those tables. Is it possible without some projection, like it is in T-SQL? It doesn't matter whether the solution uses e-SQL or LINQ to Entities. I searched the web but I wasn't satisfied with the answers I found since they mainly involve projecting data to some anonymous type and then re-query that anonymous type to get the child entities in the order you like. Also using CreateSourceQuery() doesn't seem to be an option for me since I need to get the data as it is on the database side, with eager loading but just by ordering child entities. That is I want to do the "ORDER BY" before executing any query and then fetch the entities in the order I'd like. Thanks in advance for any guidance. As a personal note, please excuse the direct language since I am kinda pissed at Microsoft for releasing the EF in such an immature shape even compared to Linq to SQL (which they seem to be getting away slowly). I hope this EF thingie will get much better and without significant bugs in the release version of .NET FX 4.0.
Actually I have Tip that addresses exactly this issue.
Sorting of related entities is not 'supported', but using the projection approach Craig shows AND relying on something called 'Relationship Fixup' you can get something very similar working:
If you do this:
var projection = from c in ctx.Customers
select new {
Customer = c,
Orders = c.Orders.OrderByDescending(
o => o.OrderDate
)
};
foreach(var anon in projection )
{
anon.Orders //is sorted (because of the projection)
anon.Customer.Orders // is sorted too! because of relationship fixup
}
Which means if you do this:
var customers = projection.AsEnumerable().Select(x => x.Customer);
you will have customers that have sorted orders!
See the tip for more info.
Hope this helps
Alex
You are confusing two different problems. The first is how to materialize entities in the database, the second is how to retrieve an ordered list. The EntityCollection type is not an ordered list. In your example, customer.Orders is an EntityCollection.
On the other hand, if you want to get a list in a particular order, you can certainly do that; it just can't be in a property of type EntityCollection. For example:
from c in northwindContext.Customers
orderby c.SomeField
select new {
Name = c.Name,
Orders = from o in c.Orders
orderby c.SomeField
select new {
SomeField = c.SomeField
}
}
Note that there is no call to Include. Because I am projecting, it is unnecessary.
The Entity Framework may not work in the way you expect, coming from a LINQ to SQL background, but it does work. Be careful about condemning it before you understand it; deciding that it doesn't work will prevent you from learning how it does work.
Thank you both. I understand that I can use projection to achieve what I wanted but I thought there might be an easy way to do it since in T-SQL world it's perfectly possible with a few nested queries (or joins) and order bys. On the other hand seperation of concerns sounds reasonable and we are in the entity domain now so I will use the way you two both recommended though I have to admit this is easier and cleaner to achieve in LINQ to SQL by using AssociateWith.
Kind regards.