I have two entities, Ablum and Image, which are in many to many relationship.
I wanna make a criteria query that to get all Albums and the counts on how many Images they have.
I don't want to get all Albums first then loop the result to get the counts as there would be so many sql requests.
I've been working for 2 nights and complete lost. If cannot find a way out maybe I need to fallback to use SQL.
Thanks to digitaljoel's inspiration, I found that CriteriaBuilder has a method call "size" that can be put on collections. Below is the code:
CriteriaBuilder cb = getCriteriaBuilder();
CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
Root<AlbumEntity> albums = query.from(AlbumEntity.class);
query.select(cb.array(albums.get(AlbumEntity_.id), cb.size(albums.get(AlbumEntity_.images))));
query.groupBy(albums.get(AlbumEntity_.id));
Here the groupBy call is a must otherwise error will occur.
But this method is to load the IDs of AlbumEntity, not the entity itself. The Album entity can be load if below code is used:
query.select(cb.array(albums, cb.size(albums.get(AlbumEntity_.images))));
query.groupBy(albums.get(AlbumEntity_.id), ...);
The groupBy must include all properites of the album entity. And it still does not work if the album entity has blob type property.
I'm going to have to make some assumptions since you haven't posted your JPA mapping, so I'm assuming each album has a List<YourImageClass> images for the many to many mapping. With that, something like this would work.
select a, size(a.images) from Album a
That would return a List<Object[]> where List.get(i)[0] would be the album and List.get(i)[1] would be the corresponding size of the image collection.
Alternately, you could define a simple bean to select into. Something like
public class AlbumResult {
private Album album;
private Integer imageCount;
public AlbumResult( Album a, Integer size ) {
album = a;
imageCount = size;
}
// getters and setters here
}
Then you could do
select new AlbumResult(a, size(a.images)) from Album a;
I never deal with criteria queries, but the JPQL is simple enough it should be trivial to translate it into a criteria query.
Related
I have researched this for a few days but can't seem to find the right information.
Here is what I need, I have a Database, with multiple tables, I need to join a few tables together to make a sort of "search" API. I have to implement the ability to dynamically search fields (from various tables in the query), sortable, with pagination.
I have found that I cannot combine the #Query annotation with Specification API, and I looked into using the specification API to do the joins I needed but, the problem is the root must be one table/repository.
For example:
If I have a users table that has to join on addresses, phone_numbers, and preferences
the base repository will be UserResposiory and it will return the User entity model, but I need it to return a custom DTO
AccountUserDTO which contains fields from the User, Address, PhoneNumber, and Preference entities.
Would anyone know if this is possible at all??
I am at wits end here and I really want to build this the correct way.
Cheers!
You may do this way:
Build hql query as an string, depend on how the filter condition is requested, you can build the corresponding query, eg:
if (hasParam(searchName)) {
queryString = queryString + " myEntity.name = :queryName"
}
Query query = session.createQuery(queryString);
and the parameter providing
if (hasParam(searchName)) {
query.setParameter("queryName", searchName);
}
...
and execute it.
To create a customized object, the easiest way is treating the object as an array of field:
Query query = session.createQuery("select m.f1, m.f2, m.f3 from myTable m");
List managers = query.list();
Object[] manager = (Object[]) managers.get(0); //first row
System.out.println(manager[0]) //f1
System.out.println(manager[1]) //f2
System.out.println(manager[2]) //f3
There is also some other solution to select, such as
String query = "select new mypackage.myclass(m.f1, m.f2, m.f3) from myTable m";
-> And when execute the above query, it will return a list of object.
Or to be simpler, make your own view in db and map it to one entity.
(as advised re-posting this question here... originally posted in msdn forum)
I am striving to write a "generic" routine for some simple CRUD operations using EF/Linq to Entities. I'm working in ASP.NET (C# or VB).
I have looked at:
Getting a reference to a dynamically selected table with "GetObjectByKey" (But I don't want anything from cache. I want data from database. Seems like not what this function is intended for).
CRM Dynamic Entities (here you can pass a tablename string to query) looked like the approach I am looking for but I don't get the idea that this CRM effort is necessarily staying current (?) and/or has much assurance for the future??
I looked at various ways of drilling thru Namespaces/Objects to get to where I could pass a TableName parameter into the oft used query syntax var query = (from c in context.C_Contacts select c); (for example) where somehow I could swap out the "C_Contacts" TEntity depending on which table I want to work with. But not finding a way to do this ??
Slightly over-simplyfing, I just want to be able to pass a tablename parameter and in some cases some associated fieldnames and values (perhaps in a generic object?) to my routine and then let that routine dynamically plug into LINQ to Entity data context/model and do some standard "select all" operations for parameter table or do a delete to parameter table based on a generic record id. I'm trying to avoid calling the various different automatically generated L2E methods based on tablename etc...instead just trying to drill into the data context and ultimately the L2E query syntax for dynamically passed table/field names.
Has anyone found any successful/efficient approaches for doing this? Any ideas, links, examples?
The DbContext object has a generic Set() method. This will give you
from c in context.Set<Contact>() select c
Here's method when starting from a string:
public void Test()
{
dynamic entity = null;
Type type = Type.GetType("Contract");
entity = Activator.CreateInstance(type);
ProcessType(entity);
}
public void ProcessType<TEntity>(TEntity instance)
where TEntity : class
{
var result =
from item in this.Set<TEntity>()
select item;
//do stuff with the result
//passing back to the caller can get more complicated
//but passing it on will be fine ...
}
I am new in JPA, so excuse me if my question seems basic.
I have an entity called User, which is related to a list of other entities like follow:
#OneToMany(cascade = CascadeType.ALL , mappedBy = "user")
private List<session> sessionList;
In a controller class, I defined a find method in a RESTFull manner like follow:
#GET
#Path("/Users")
#Produces("application/json")
public List<UserDevice> findAllUsers()
{
return em.createQuery("SELECT u FROM User u").getResultList();
}
The returned result contains all the sessions of the users which is normal, but make the result huge though I just want to retrieve the basic information of the users (all the simple columns).
My question is: is it possible to ignore the related entities and just keep the columns of the actual entity? Thank you very much
Unless you explicitely map the association as eagerly loaded (using #OneToMany(fetch = FetchType.EAGER)), the above query should only return the fields of the users, and should not load their sessionList.
If the sessions are loaded, then the association is marked as eagerly loaded, or you load them lazily by calling a method of the List<Session>.
I have the following Entities; Ticket contains a set of 0,N WorkOrder:
#Entity
public class Ticket {
...
#OneToMany(mappedBy="ticket", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<WorkOrder> workOrders = null;
...
}
#Entity
public class WorkOrder {
...
#ManyToOne
#JoinColumn(nullable = false)
private Ticket ticket;
}
I am loading Tickets and fetching the attributes. All of the 0,1 attributes present no problem. For workOrders, I used this answer to get the following code.
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Ticket> criteriaQuery = criteriaBuilder
.createQuery(Ticket.class);
Root<Ticket> rootTicket = criteriaQuery.from(Ticket.class);
ListAttribute<? super Ticket, WorkOrder> workOrders =
rootTicket.getModel().getList("workOrders", WorkOrder.class);
rootTicket.fetch(workOrders, JoinType.LEFT);
// WHERE logic
...
criteriaQuery.select(rootTicket);
TypedQuery<Ticket> query = this.entityManager.createQuery(criteriaQuery);
return query.getResultList();
The result is that, in a query that should return me 1 Ticket with 5 workOrders, I am retrieving the same Ticket 5 times.
If I just make the workOrders an Eager Fetch and delete the fetch code, it works as it should.
Can anyone help me? Thanks in advance.
UPDATE:
One explanation about why I am not just happy with JB Nizet's answer (even if in the end it works).
When I just make the relationship eager, JPA is examining exactly the same data that when I make it lazy and add the fetch clause to the Criteria / JPQL. The relationships between the various elements is also clear, as I define the ListAttribute for the Criteria query.
There is some reasonable explanaition for the reason that JPA does not return the same data in both cases?
UPDATE FOR BOUNTY: While JB Nizet's answer did solve the issue, I still find it meaningless that, given two operations with the same meaning ("Get Ticket and fetch all WorkOrder inside ticket.workOrders"), doing them by an eager loading needs no further changes while specifying a fetch requires a DISTINCT command
There is a difference between eager loading and fetch join. Eager loading doesn't mean that the data is loaded within the same query. It just means that it is loaded immediately, although by additional queries.
The criteria is always translated to an SQL query. If you specify joins, it will be join in SQL. By the nature of SQL, this multiplies the data of the root entity as well, which leads to the effect you got. (Note that you get the same instance multiple times, so the root entity is not multiplied in memory.)
There are several solutions to that:
use distinct(true)
Use the distinct root entity transformer (.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)).
When you don't need to filter by child properties, avoid the join
When you need to filter by child properties, filter by a subquery (DetachedCriteria).
Optimize the N+1 problem by using batch-size
Have you tried calling distinct(true) on the CriteriaQuery?
The JPA 2 specification, page 161, says:
The DISTINCT keyword is used to specify that duplicate values must be
eliminated from the query result.
If DISTINCT is not specified, duplicate values are not eliminated.
The javadoc also says:
Specify whether duplicate query results will be eliminated.A true
value will cause duplicates to be eliminated. A false value will cause
duplicates to be retained. If distinct has not been specified,
duplicate results must be retained.
The reason why you don't need the distinct when the association is eagerly loaded is probably just that the association is not loaded using a fetch join, but using an additional query.
In EF eager loading related entities is easy.
But I'm having difficulties including inherited entities when loading data using table-per-type model.
This is my model:
Entities:
ArticleBase (base article entity)
ArticleSpecial (inherited from ArticleBase)
UserBase (base user entity)
UserSpecial (inherited from UserBase)
Image
Relations are as shown on the image (omitting many columns):
In reality my users are always of type UserSpecial, since UserBase is used in another application, thus we can share credentials. That's the only reason I have two separate tables. UserBase table can't be changed in any way shape or form, because the other app would break.
Question
How am I suppose to load ArticleSpecial with both CreatedBy and EditedBy set, so that both are of type UserSpecial (that defines Image relation)?
I've tried (unsuccessfully though) these options:
1.
Using lambda expressions:
context.ArticleBases
.OfType<ArticleSpecial>()
.Include("UserCreated.Image")
.Include("UserEdited.Image");
In this case the problem is that both CreatedBy and EditedBy are related to UserBase, that doesn't define Image navigation. So I should somehow cast these two to UserSpecial type like:
context.ArticleBases
.OfType<ArticleSpecial>()
.Include("UserCreated<UserSpecial>.Image")
.Include("UserEdited<UserSpecial>.Image");
But of course using generics in Include("UserCreated<UserSpecial>.Image") don't work.
2.
I have tried using LINQ query
var results = from articleSpecial in ctx.ArticleBase.OfType<ArticleSpecial>()
join created in ctx.UserBase.OfType<UserSpecial>().Include("Image")
on articleSpecial.UserCreated.Id equals created.Id
join edited in ctx.UserBase.OfType<UserSpecial>().Include("Image")
on articleSpecial.UserEdited.Id equals edited.Id
select articleSpecial;
In this case I'm only getting ArticleSpecial object instances without related properties being set. I know I should select those somehow, but I don't know how?
Select part in my LINQ could be changed to something like
select new { articleSpecial, articleSpecial.UserCreated, articleSpecial.UserEdited };
but images are still not loaded into my context. My joins in this case are barely used to filter out articleSpecial results, but they don't load entities into context (I suppose).
This seems to be a limitation in the current version of Entity Framework (1.0) Have a look at this related SO question.
In your case including the related UserCreated and UserEdited properties in the projection is the right solution. However if you also want to populate the Image property on the UserSpecial object, you must be sure to include that as well:
var results = from articleSpecial in ctx.ArticleBase.OfType<ArticleSpecial>()
select new
{
articleSpecial,
articleSpecial.UserCreated,
((UserSpecial)articleSpecial.UserCreated).Image,
articleSpecial.UserEdited,
((UserSpecial)articleSpecial.UserEdited).Image
};
Of course this query builds on the assumption that all ArticleSpecial entities always refer to a UserSpecial entity, otherwise the casting will fail.
If this assumption isn't always true, you could express the same query using the LINQ extension methods and a multi-line lambda function to perform a safe casting:
var results = ctx.ArticleBase
.OfType<ArticleSpecial>()
.AsEnumerable()
.Select(a =>
{
var userCreated = a.UserCreated as UserSpecial;
if (userCreated != null)
{
var image = userCreated.Image;
}
var userEdited = a.UserEdited as UserSpecial;
if (userEdited != null)
{
var image = userEdited.Image;
}
return a;
});
In the latter example, you also do not need to include UserSpecial and Image entities in the results. Instead you just need to access the navigation properties on the ArticleSpecial entities during the projection phase in order to force Entity Framework to eager load the related objects.