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

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

Related

Linq Left Outer Join Two Tables on Two Fields

How do I left outer join two tables on two fields in linq?
I have a sql:
select a.*, b.* from courselist as a
left outer join Summary as b
on a.subject = b.Subject and a.catalog =
b.Catalogno
where a.degree_id = 1
order by a.sequenceNo
Below is my linq query, but there is error underline "join", failed in the call to "Groupjoin". I don't know how to correct that.
var searchResults = (from a in db.courselist
join b in db.Summary on
new { a.subject,a.catalog } equals
new { b.Subject, b.Catalogno } into ab
where a.degree_id == 1
orderby a.degree_sequenceNo
from b in ab.DefaultIfEmpty()
select new
{
Courselist = a,
Summary = b
}
).ToList();
Thanks.
I've checked your code again,
I found it's fault
you just need to specify join parameters name like this:
new { suject = a.subject, catalog = a.catalog } equals
new { suject = b.subject, catalog = b.Catalogno } into ab
It seems you are missing the reference, the query doesn't have an error
try to use this:
using System.Linq;
The main issue when people start using LINQ is that they keep thinking in the SQL way, they design the SQL query first and then translate it to LINQ. You need to learn how to think in the LINQ way and your LINQ query will become neater and simpler. For instance, in your LINQ you don't need joins. You should use Associations/Navigation Properties instead. Check this post for more details.
There should be a relationship between courselist and Summary, in which case, you can access Summary through courselist like this:
var searchResults = (from a in db.courselist
where a.degree_id == 1
orderby a.degree_sequenceNo
select new {
Courselist = a,
Summary = a.Summary
}).ToList();
If there is no relationship between the two, then you should reconsider your design.

Filter with Spring-Data and QueryDSL on nullable reference attribute

I have following issue. I am using Jquery Datatable serverside and I am now implementing the search box. But I have an issue there in special case, when a dataset has an attribute, what is "null". So the dataset will not be found although it should found cos it matches on one attibute.
The situtation in beginning is like follows. You see there is a dataset with apprentice Fabio Bartels, who has not Fachrichtung. And a dataset with Viktoria.
Now when I search for Viktoria, the filter works as expected:
When I search for Fabio, then Dataset is not found:
=====
The problem I have is, that I don't know how to handle the filter, that a attribute will only be validated against the search string when the attribute is not null.
=====
Serverside Java Classes see like follows:
QueryClass:
class ContractSearchQuery {
private static QContract contract = QContract.contract;
static BooleanExpression getPredicate(final ContractSearch filter) {
BooleanExpression predicate;
if (filter == null || filter.isEmpty()) {
// SHOW ALL PREDICATE ...
} else {
final String search = filter.getSearch();
final List<BooleanExpression> expressions = new ArrayList<BooleanExpression>();
// EXPRESSIONS CURRENTLY ONLY ON AUSZUBILDENDER AND FACHRICHTUNG
// FOR SHOWCASE
expressions.add(containsApprenticeName(search)); // AUSZUBILDENDER
expressions.add(containsSpecialisation(search)); // FACHRICHTUNG
BooleanExpression expression = expressions.get(INTEGER_ZERO);
for (int i = 1; i < expressions.size(); i++) {
expression = expression.or(expressions.get(i));
}
predicate = expression;
}
return predicate;
}
private static BooleanExpression containsApprenticeName(final String search) {
final BooleanExpression expLastName = contract.apprentice.lastName.containsIgnoreCase(search);
final BooleanExpression expFirstName = contract.apprentice.firstName.containsIgnoreCase(search);
return expLastName.or(expFirstName);
}
private static BooleanExpression containsSpecialisation(final String search) {
return contract.companyOccupationCombination.occupationCombination.specialisation.name.containsIgnoreCase(search);
}
}
Spring-Data-Repository Call:
final PageRequest pageRequest = new PageRequest(firstResult / maxResults, maxResults, orderSort);
final Page<Contract> page = contractRepository.findAll(predicate, pageRequest);
return page.getContent();
=======
Database:
By the way I recognized when I do direct request against my db with joining Specialisation Table, then I only get Fabio as record, when not joining Specialisation, I get all three persons. Maybe somethings to do with my issue:
select a.first_name, a.last_name from contract c
join company_occupation_combination coc on c.company_occupation_combination = coc.id
join occupation_combination oc on coc.occupation_combination = oc.id
join apprentice a on c.apprentice = a.id
Result:
"Fabio";"Bartels"
"Viktoria";"Kruczek"
"Lina";"Ehleiter"
With Join:
select a.first_name, a.last_name from contract c
join company_occupation_combination coc on c.company_occupation_combination = coc.id
join occupation_combination oc on coc.occupation_combination = oc.id
join specialisation s on oc.specialisation = s.id
join apprentice a on c.apprentice = a.id
Result: "Viktoria";"Kruczek"
====
EDIT:
Okay, on db site I found out (with Hibernate and JPA I start forgetting SQL-Basices ;-)), that I need a left join for the nullable relation, so my query should result to an sql like:
select a.first_name, a.last_name from contract c
join company_occupation_combination coc on c.company_occupation_combination = coc.id
join occupation_combination oc on coc.occupation_combination = oc.id
left join specialisation s on oc.specialisation = s.id
join apprentice a on c.apprentice = a.id
====
So my question is, how can I manage left Join when I have a Query-Class using QueryDSL and Spring-Data-Repository like mentioned above?
If you really need left join, you can't achieve that via predicate (instead it is possible via sub-query)
To be able to do left-join, you will need JPAQuery.
Assuming you have already configured repositories, and able to use EntitiManager, implement ContractRepositoryCustom , so that in your implementation you can have
#PersistenceContext(unitName = "unitname")
protected EntityManager entityManager;
public List<Contract> findAllContracts() {
return new JPAQuery(entityManager, HQLTemplates.DEFAULT)
.from(QContract.contract)
.join(QContract.contract.companyOccupationCombination, QCompanyOccupationCombination.companyOccupationCombination)
.join(QCompanyOccupationCombination.companyOccupationCombination.occupationCombination, QOccupationCombination.occupationCombination)
.leftJoin(QOccupationCombination.occupationCombination.specialization, QSpecialization.specialization)
.join(QSpecialization.specialization.apprentice, QApprentice.apprentice)
.list(QContract.contract);
}
And for pagination you always apply limit(maxResults) and offset(firstResult)
I really like working with Spring-Data and Query-DSL, cos it makes my code really tidy. But I am really suprised, that for the case of nullable references there seems no solution. Sure you can use another solution like #vtorosyan mentioned and thank you again for that solution, but when you project is builded up with combination of QueryDSL and Spring-Data, you really don't want to bring a second style in your application.
But I needed a solution, so I did now the follows.
The point of the issue was, that when I used data from a nullable entity, a join has been executed what hided the datasets, who had a null reference on it, see examples above. What I now did and I hope I will not get another issue then with that solution on later time of that project. I did the null references to not null and defined something like null-record.
Example I added a record for specialisation like
ID NAME
0 Keine
Instead of null I now use that record what has until now following effects:
First my table shows now "Keine" (engl. "None") for all attributes what are not set. It looks more consistent when having a textoutput then empty string.
Now I can explicitly search for "Keine", when I am interested for data records what have no specialisation set.
And my searchbox works as expected for records, which have no speciafication set. (THAT WAS MY ISSUE FROM BEGINNING WHAT I WANTED TO SOLVE):
Additional to that searchbox I use a modal dialog for filtering. Now I can explicitly filter "Keine" for "nullable" records:
If you think there is another good solution for that issue without rebuild code using Spring-Data and QueryDSL konsequently, don't hesitate to post ;-)

Child collection and one to one

My entity "TimeRecord" has a collection of "WayPoints" and two one-to-one properties "Location" and "WayData".
Each property can be null.
I need to export all Time Records with initialized properties for a specific User.
I actually had a working solution but then I started to use NHibernateProiler and first I noticed that this code results in a ridiculous number of query’s against db.
var query = (from timeRecord in Session.Query<TimeRecord>()
.Where(tr => tr.User.Id == userid)
select timeRecord);
Then I changed my code to:
var query = (from post in Session.Query<TimeRecord>()
.Fetch(x => x.Location)
.Fetch(x => x.WayData)
.FetchMany(x => x.WayPoints)
.Where(tr => tr.User.Id == userid)
select post);
Which lead me to the Cartesian product problem.
Right now I’m experimenting with this piece of code:
var sql1 = "from TimeRecord b left outer join fetch b.Location where b.User.Id=:User_id";
var sql2 = "from TimeRecord b left outer join fetch b.WayData where b.User.Id=:User_id";
var sql3 = "from TimeRecord b left inner join fetch b.WayPoints where b.User.Id=:User_id";
var result = Session.CreateMultiQuery()
.Add(Session.CreateQuery(sql1))
.Add(Session.CreateQuery(sql2))
.Add(Session.CreateQuery(sql3))
.SetParameter("User_id", userid)
.List();
But I can’t say if this is the correct approach or if this is even possible with nHibernate. Can someone help me with that?
The 1+N issue is a usual with the entity/collection mapping and ORM tools. But NHibernate has a very nice solution how to manage it properly. It is called:
19.1.5. Using batch fetching
This setting will allow:
To continue querying the root entity (TimeRecord in our case)
No fetching inside of the query (Session.Query<TimeRecord>()). That means we do have support for correct paging. (Take(), Skip() will be executed over the flat root table)
All the collections will be loaded with their own SELECT statements (could seem as disadvantage but se below)
There will be much more less SELECTs then 1+N. All of them will be batched. E.g. by 25 records
all the native mapping (lazy loading of collections) will still be in place...
The xml mapping example:
-- class level
<class name="Location" batch-size="25 ...
-- collection level
<batch name="Locations" batch-size="25" ...
I would suggest to apply that over all your collections/classes. With Fluent mapping it could be done also with conventions
The fluent mapping:
// class
public LocationMap()
{
Id(x => x....
...
BatchSize(25);
// collection
HasMany(x => x.Locations)
...
.BatchSize(25);

LINQ Join with Multiple Conditions in On Clause

I'm trying to implement a query in LINQ that uses a left outer join with multiple conditions in the ON clause.
I'll use the example of the following two tables Project (ProjectID, ProjectName) and Task (TaskID, ProjectID, TaskName, Completed). I want to see the full list of all projects with their respective tasks, but only those tasks that are completed.
I cannot use a filter for Completed == true because that will filter out any projects that do not have completed tasks. Instead I want to add Completed == true to the ON clause of the join so that the full list of projects will be shown, but only completed tasks will be shown. Projects with no completed tasks will show a single row with a null value for Task.
Here's the foundation of the query.
from t1 in Projects
join t2 in Tasks
on new { t1.ProjectID} equals new { t2.ProjectID } into j1
from j2 in j1.DefaultIfEmpty()
select new { t1.ProjectName, t2.TaskName }
How do I add && t2.Completed == true to the on clause?
I can't seem to find any LINQ documentation on how to do this.
You just need to name the anonymous property the same on both sides
on new { t1.ProjectID, SecondProperty = true } equals
new { t2.ProjectID, SecondProperty = t2.Completed } into j1
Based on the comments of #svick, here is another implementation that might make more sense:
from t1 in Projects
from t2 in Tasks.Where(x => t1.ProjectID == x.ProjectID && x.Completed == true)
.DefaultIfEmpty()
select new { t1.ProjectName, t2.TaskName }
Here you go with:
from b in _dbContext.Burden
join bl in _dbContext.BurdenLookups on
new { Organization_Type = b.Organization_Type_ID, Cost_Type = b.Cost_Type_ID } equals
new { Organization_Type = bl.Organization_Type_ID, Cost_Type = bl.Cost_Type_ID }
You can't do it like that. The join clause (and the Join() extension method) supports only equijoins. That's also the reason, why it uses equals and not ==. And even if you could do something like that, it wouldn't work, because join is an inner join, not outer join.

Entity Framework creating IQueryable of the most recent

I have an Entity set that has Entities with a compound key containing ID (GUID) and CreatedAt (DateTime). This CreatedAt is when the entity was created. Each record represents each version of each entity such that muliple records can have the same ID.
I want to create an IQueryable-returning method that I can re-use such that it will only return the latest version of the entity requested, but I'm struggling to find the answer (if, indeed, there is an answer).
I know that I can write a query such as
(from e in context.Entities where e.ID = myID orderby e.CreatedAt).FirstOrDefault();
But I want to be able to do this instead:
(from e in context.GetCurrentEntities() where e.ID = myID).FirstOrDefault();
Such that it will only return the latest versions of the entity required.
Is this doable?
Many thanks for your help.
Lee
If you group by ID, you can select only the most recent from each group using something like this:
public IQueryable<Entity> GetCurrentEntities()
{
return from e in this.Entities
group e by e.ID into g
select g.OrderByDescending(e => e.CreatedAt).First();
}
You don't need FirstOrDefault() because a group won't be created unless there's at least one Entity in the group.
How about:
public partial class MyEntities // or whatever you call your ObjectContext
{
public IQueryable<Entity> GetCurrentEntities()
{
return from e in context.Entities
group e by e.ID into g
from ge in g
orderby ge.CreatedAt desc
select ge.First();
}
}
This is off the top of my head, but it should get you started.
I think you are looking at a smaller picture. Have you considered implementing repository pattern?LINK otherwise looking at your requirements you will need to add this GetCurrentEntities method to your context class.

Resources