JPA EclipseLink #ElementCollection join fetch via CriteriaQuery - jpa-2.0

I have an entity (Book) with an ElementCollection for a List property (tags).
#Entity
#Table(name = "BOOK")
public class Book {
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "BOOK_TAGS", joinColumns = #JoinColumn(name = "BOOK_ID"))
#Column(name = "TAG", length = "4000")
private List<String> tags;
}
For getting a single book, I'm doing entityManager.find and for getting all the books, I'm doing criteria query. This all works. I now want to make the list of tags lazily loaded, which will prevent them from loading when I get all books (which I want) but I still want to load them when I get a particular Book.
My first attempt is to change the entityManager.find to a query. The following works:
select b from Book b left outer join fetch b.tags where b.id = :id
However, I want to do this as a CriteriaQuery as well as add the option for getting tags on the get all books query. Doing the following is not working:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Book.class);
Root<Book> b = cq.from(Book.class);
b.fetch("tags", JoinType.LEFT);
cq.select(b);
cq.where(cb.equal(b.get("id"),cb.parameter(String.class,"id"));
TypedQuery<Book> q = em.createQuery(cq);
q.setParameter(id);
return q.getSingleResult();
This is failing in that it is not a single result - its looking like N objects where N is the number of tags.
I also tried adding:
q.setHint("eclipselink.join-fetch","b.tags");
and taking out the fetch but that also doesn't work.
I'm getting the columns added to the SQL but its like the processing of those extra columns is messing up the returned object - its either multiples or no object (for hints).
Any ideas how to turn the JOIN FETCH into a proper CriteriaQuery?

Answering my own question - join fetch is not the way to go here as that will mess up result counting - trick is to use batch fetching via QueryHints - and pre-loading the tags as well as a QueryHint.

Related

Django Query - Multiple Inner Joins

we currently have some issues with building complex Q-object queries with multiple inner joins with django.
The model we want to get (called 'main' in the example) is referenced by another model with a foreign key. The back-reference is called 'related' in the example below. There are many objects of the second model that all refer to the same 'main' object all having ids and values.
We want to get all 'main' objects for wich a related object with id 7113 exists that has the value 1 AND a related object with id 7114 that has the value 0 exists.
This is our current query:
(Q(related__id=u'7313') & Q(related__value=1)) & (Q(related__id=u'7314') & Q(related__value=0))
This code evaluates to
FROM `prefix_main` INNER JOIN `prefix_related` [...] WHERE (`prefix_related`.`id` = 7313 AND `prefix_related`.`value` = 1 AND `prefix_related`.`id` = 7314 AND `prefix_related`.`value` = 0)
What we would need is quite different:
FROM `prefix_main` INNER JOIN `prefix_related` a INNER JOIN `prefix_related` b [...] WHERE (a.`id` = 7313 AND a.`value` = 1 AND b.`id` = 7314 AND b.`value` = 0)
How can I rewrite the ORM query to use two INNER JOINS / use different related instances for the q-objects? Thanks in advance.
i don't think you even need Q-objects for this. You can just use multiple filters like this:
Mainmodel.objects.filter(related__id = 7114, related__value=1).filter(related__id = 7113, related__value=0)
the first filter matches all objects that have a related object with the id 7114 and value 1. The returned objects are filtered once again with the id 7113 and the value 0.

Populate Model from a Query with a One-to-Many Relationship

I have two tables:
surveyTemplateHeader
surveyTemplateQuestions
What I was looking to do was get the information from both tables:
var q = dao.getSurveyTemplateDetails( surveyTemplateID = ARGUMENTS.surveyTemplateID );
Then use Coldbox to populate my model, surveyTemplate, with the query data:
var surveyTemplateObj = populator.populateFromQuery( beanFactory.getInstance("surveyTemplate"), q );
This works, however, the model is only populated with one question. The surveyTemplate I am testing has three questions.
I have tried to set up a property name correctly in my model to use fieldtype="one-to-many" but that does not appear to have made a difference.
property name="surveyTemplateQuestionID" fieldtype="one-to-many";
While Coldbox's documentation for models overall is quite good, I am not able to find an answer for this, which probably means I am off track a good bit in my implementation of this.
Any insight would be appreciated.
So, what I did for this is I injected a surveyTemplateQuestions model into my surveyTemplate model:
property name="surveyTemplateQuestions" inject="surveyTemplateQuestions";
then I set the surveyTemplateQuestions property with the questions query:
surveyTemplateObj.setSurveyTemplateQuestions(qQuestions);
Not exactly what I was looking for but it works for now.
You can loop over query to build Arrays of objects. populateFromQuery method takes query row-number to get data from query.
var surveyTemplateObj = [];
for(var row in q){
ArrayAppend(surveyTemplateObj, populator.populateFromQuery( beanFactory.getInstance("surveyTemplate"), q , row.currentRow));
}
API Doc info
http://apidocs.ortussolutions.com/coldbox/4.3.0/coldbox/system/core/dynamic/BeanPopulator.html#populateFromQuery()

Get a list of entities that match ALL the supplied ids for a related entity field in a many-to-many association

I am trying to get a list of users from the database that have ALL the tags in a criteria.
The User entity has a many-to-many association to a Tag entity.
The or version where just one of the tags have to match is working using the following code
$tagIds = array(29,30);
$this->createQueryBuilder('u')
->select('u','t')
->leftJoin('u.tags','t')
->where("t IN(:tagIds)")
->setParameter("tagIds",$tagIds)
;
Can anybody help me with getting it to work so ALL tag ids must match ?
Keep in mind this is a query to get a list of users, not just one user , so i guess every user must be checked to see if they match all the supplied tag ids.
I have tried a bunch of queries but not having any luck so far...
simple bruteforce query:
$tagIds = array(29,30);
$qb = $this->createQueryBuilder('u');
$qb
->select('u')
;
foreach($tagIds as $idx => $tagId)
{
$joinAlias = "t{$idx}";
$qb->leftJoin('u.tags', $joinAlias)
->addSelect($joinAlias)
->andWhere("{$joinAlias}.id = $tagId AND $joinAlias IS NOT NULL")
;
}
this is really bruteforce and costly query, you join each tag as a separate join, if you have lots of users and tags, this will take ages to execute.
since database is the bottleneck of your application, you should make a simple query to the database and then parse the data in your application, so you should use your query and then check which users have those 2 tags in their collections.
Ok... after a lot of searching this seems to work for me :
$tagIds = array(29,30);
$this->createQueryBuilder('u')
->select('u','t')
->leftJoin('u.tags','t')
->where("t IN(:tagIds)")
->groupBy('u.id')
->having('COUNT(DISTINCT t.id) = ' . count($tagIds))
->setParameter("tagIds",$tagIds)
;

Preventing lazy-loading gives me 1 "subentity"

I have a problem with Doctrine (Symfony2.1). I want to prevent lazy-loading by join fetching subentities (OneToMany relation) but I got only one result for those subentities.
For example:
public function getSingleProjectQuery($project){
$query = $this->createQueryBuilder('p')
->select(array("p", "fb"))
->where('p.id = :project_id')->setParameter('project_id', $project)
->leftJoin('p.feedbacks', 'fb')
->groupBy('p.id')
->getQuery();
return $query;
}
In this example Doctrine returns me the "Project"-object and one single "feedback" object (but there are more than one feedbacks...).
When I replace the select to this: ->select(array("p"))
I got all the "Feedback" objects but there are then lazy-loaded (many queries).
see http://docs.doctrine-project.org/en/latest/reference/dql-doctrine-query-language.html#joins
You should remove the groupBy clause.

django annotate count filter

I am trying to count daily records for some model, but I would like the count was made only for records with some fk field = xy so I get list with days where there was a new record created but some may return 0.
class SomeModel(models.Model):
place = models.ForeignKey(Place)
note = models.TextField()
time_added = models.DateTimeField()
Say There's a Place with name="NewYork"
data = SomeModel.objects.extra({'created': "date(time_added)"}).values('created').annotate(placed_in_ny_count=Count('id'))
This works, but shows all records.. all places.
Tried with filtering, but it does not return days, where there was no record with place.name="NewYork". That's not what I need.
It looks as though you want to know, for each day on which any object was added, how many of the objects created on that day have a place whose name is New York. (Let me know if I've misunderstood.) In SQL that needs an outer join:
SELECT m.id, date(m.time_added) AS created, count(p.id) AS count
FROM myapp_somemodel AS m
LEFT OUTER JOIN myapp_place AS p
ON m.place_id = p.id
AND p.name = 'New York'
GROUP BY created
So you can always express this in Django using a raw SQL query:
for o in SomeModel.objects.raw('SELECT ...'): # query as above
print 'On {0}, {1} objects were added in New York'.format(o.created, o.count)
Notes:
I haven't tried to work out if this is expressible in Django's query language; it may be, but as the developers say, the database API is "a shortcut but not necessarily an end-all-be-all.")
The m.id is superfluous in the SQL query, but Django requires that "the primary key ... must always be included in a raw query".
You probably don't want to write the literal 'New York' into your query, so pass a parameter instead: raw('SELECT ... AND p.name = %s ...', [placename]).