Select attribute of another attribute of entity - spring

Currently I have three entities Country, Office and User, where Country.gov is of type Office and where Office.holder is of type User. Country is the owning side in Country.gov and Office is the owning side of Office.holder.
Now I want to get Country.gov with a LEFT JOIN on Office.holder using an attribute of Country, e.g.
SELECT c.gov o FROM Country c LEFT JOIN FETCH c.gov LEFT JOIN FETCH c.gov.holder WHERE c.countryKey = :countryKey, but this does not work, it throws an exception:
org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list
[FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=dev.teamnight.game.entities.Country.gov,tableName=office,tableAlias=office1_,origin=country country0_,columns={country0_.gov_id,className=dev.teamnight.game.entities.Office}}]
[SELECT c.gov FROM dev.teamnight.game.entities.Country c LEFT JOIN FETCH c.gov LEFT JOIN FETCH c.gov.holder WHERE c.countryKey = :countryKey]

You can simplify a bit your query:
List<Office> calls = entityManager.createQuery(
"select o " +
"from Country c " +
"join c.gov o " +
"left join fetch o.holder " +
"where c.countryKey = :countryKey ", Office.class )
.setParameter( "countryKey", countryKey )
.getResultList();
For additional explanation, see this section of hibernate documentation.

Related

Failed to join three entity models in Query annotation via Spring JPA Data

I have three tables and mapping JPA entity models as followings in jsf 2.x application.
Foo Foo.java
Bar Bar.java
Zoo Zoo.java
The Foo has #oneToMany relationship to both Bar and Zoo in entity model context. In native sql, I was able to join three of them which worked fine.
select f.*, b.*, z.*
from Foo f
inner join Bar b
on f.foo_id = b.foo_id
inner join Zoo z
on z.foo_id = b.foo_id
where b.name = 'barName' and z.type = 'zooType";
I was trying to translate native sql in Query annotation via Spring JPA Data however I was keep getting org.hibernate.hgql.internal.ast.QuerySyntaxException: unexpected token.
Can someone kindly enough to point out what I am doing wrong? I tried having "one inner join" but I got same exception.
#Query("select f from Foo f inner join f.bars b inner join f.zoos z " +
"where b.name = ?1 " +
"where z.type = ?2")
List<Foo> findFoo(String name, String type);
This is because, you write two where in the #Query block, maybe you should use
#Query("select f from Foo f inner join f.bars b inner join f.zoos z " +
"where b.name = ?1 " +
"and z.type = ?2")
List<Foo> findFoo(String name, String type);
instead :)

simple join syntax not properly working

I'd like to show the number of records in the history table grouped by name of service
service(code,name)
history(id, code,....)
Please note that there is no relationship between the two table history and service, history stores the activity independently from the other tables
I have tested this sql query and it returns the expected result:
select s.name, count(*) from history c
join service s
on c.code=s.code
where c.state='INITIALE'
group by s.name
Actually, I'd like to write it in jpql, I did alike
Query query =entityManager.createQuery(" select s.name, count(*) from ServiceEntity s join"
+ " HistoryEntity c "
+ " where c.code=s.code and c.state='INITIALE'"
+ " group by c.name order by c.name"
);
I got this error : Path expected for join!....
Invalid path: 'c.code'....right-hand operand of a binary operator was null....unexpected end of subtree
Try this
Query query = entityManager.createQuery("select s.name, count(s) from ServiceEntity s, HistoryEntity c "
+ " where c.code = s.code and c.state = 'INITIALE'"
+ " group by s.name order by s.name"
);

Linq left outer group by, then left outer the group

I've this query that i'm trying to put as linq:
select *
from stuff
inner join stuffowner so on so.stuffID = stuff.stuffID
left outer join (select min(loanId) as loanId, stuffownerId from loan
where userid = 1 and status <> 2 group by stuffownerId) t on t.stuffownerid = so.stuffownerid
left outer join loan on t.LoanId = loan.LoanId
when this is done, I would like to do a linq Group by to have Stuff has key, then stuffowners + Loan as value.
I can't seem to get to a nice query without sub query (hence the double left outer).
So basically what my query does, is for each stuff I've in my database, bring the owners, and then i want to bring the first loan a user has made on that stuff.
I've tried various linq:
from stuff in Stuffs
join so in StuffOwners on stuff.StuffId equals so.StuffId
join tLoan in Loans on so.StuffOwnerId equals tLoan.StuffOwnerId into tmpJoin
from tTmpJoin in tmpJoin.DefaultIfEmpty()
group tTmpJoin by new {stuff} into grouped
select new {grouped, fluk = (int?)grouped.Max(w=> w.Status )}
This is not good because if I don't get stuff owner and on top of that it seems to generate a lot of queries (LinqPad)
from stuff in Stuffs
join so in StuffOwners on stuff.StuffId equals so.StuffId
join tmpLoan in
(from tLoan in Loans group tLoan by tLoan.StuffOwnerId into g
select new {StuffOwnerId = g.Key, loanid = (from t2 in g select t2.LoanId).Max()})
on so.StuffOwnerId equals tmpLoan.StuffOwnerId
into tmptmp from tMaxLoan in tmptmp.DefaultIfEmpty()
select new {stuff, so, tmptmp}
Seems to generate a lot of subqueries as well.
I've tried the let keyworkd with:
from tstuffOwner in StuffOwners
let tloan = Loans.Where(p2 => tstuffOwner.StuffOwnerId == p2.StuffOwnerId).FirstOrDefault()
select new { qsdq = tstuffOwner, qsdsq= (int?) tloan.Status, kwk= (int?) tloan.UserId, kiwk= tloan.ReturnDate }
but the more info i get from tLoan, the longer the query gets with more subqueries
What would be the best way to achieve this?
Thanks

sql query transfer to CodeIgniter's active record class

I was wondering if it's possible to change this raw SQL into CodeIgniter's Active Record Style?
$SQL = "SELECT DISTINCT c.ID, c.Title, c.VersionInfo, GROUP_CONCAT(DISTINCT i.FullName
ORDER BY cma.Ranking SEPARATOR ', ') AS Artists
FROM carriermodifications cm INNER JOIN carriers c ON cm.CarrierID=c.ID
LEFT JOIN carriermainartists cma ON cma.CarrierID=c.ID
LEFT JOIN identities i ON cma.IdentityID=i.ID
INNER JOIN carriermainartists scma ON scma.CarrierID=c.ID
INNER JOIN identities si ON scma.IdentityID=si.ID
WHERE cm.DataSourceID IN (16, 17) AND c.Verified=1 AND c.Hidden = 0 AND ((c.Filter & 1)=1)
AND (si.Nationality=2 OR c.Concept=4)
GROUP BY c.ID ORDER BY cm.ModificationDate DESC LIMIT 27";
Thanks a lot in advance

LINQ nested joins

Im trying to convert a SQL join to LINQ. I need some help in getting the nested join working in LINQ.
This is my SQL query, Ive cut it short just to show the nested join in SQL:
select distinct
txtTaskStatus as TaskStatusDescription,
txtempfirstname+ ' ' + txtemplastname as RaisedByEmployeeName,
txtTaskPriorityDescription as TaskPriorityDescription,
dtmtaskcreated as itemDateTime,
dbo.tblTask.lngtaskid as TaskID,
dbo.tblTask.dtmtaskcreated as CreatedDateTime,
convert(varchar(512), dbo.tblTask.txttaskdescription) as ProblemStatement,
dbo.tblTask.lngtaskmessageid,
dbo.tblMessage.lngmessageid as MessageID,
case when isnull(dbo.tblMessage.txtmessagesubject,'') <> '' then txtmessagesubject else left(txtmessagedescription,50) end as MessageSubject,
dbo.tblMessage.txtmessagedescription as MessageDescription,
case when dbo.tblMessage.dtmmessagecreated is not null then dbo.tblMessage.dtmmessagecreated else CAST(FLOOR(CAST(dtmtaskcreated AS DECIMAL(12, 5))) AS DATETIME) end as MessageCreatedDateTime
FROM
dbo.tblAction RIGHT OUTER JOIN dbo.tblTask ON dbo.tblAction.lngactiontaskid = dbo.tblTask.lngtaskid
LEFT OUTER JOIN dbo.tblMessage ON dbo.tblTask.lngtaskmessageid = dbo.tblMessage.lngmessageid
LEFT OUTER JOIN dbo.tblTaskCommentRecipient
RIGHT OUTER JOIN dbo.tblTaskComment ON dbo.tblTaskCommentRecipient.lngTaskCommentID = dbo.tblTaskComment.lngTaskCommentID
ON dbo.tblTask.lngtaskid = dbo.tblTaskComment.lngTaskCommentTaskId
A more seasoned SQL programmer wouldn't join that way. They'd use strictly left joins for clarity (as there is a strictly left joining solution available).
I've unraveled these joins to produce a hierarchy:
Task
Action
Message
TaskComment
TaskCommentRecipient
With associations created in the linq to sql designer, you can reach these levels of the hierarchy:
//note: these aren't outer joins
from t in db.Tasks
let actions = t.Actions
let message = t.Messages
let comments = t.TaskComments
from c in comments
let recipients = c.TaskCommentRecipients
DefaultIfEmpty produces a default element when the collection is empty. Since these are database rows, a default element is a null row. That is the behavior of left join.
query =
(
from t in db.Tasks
from a in t.Actions.DefaultIfEmpty()
from m in t.Messages.DefaultIfEmpty()
from c in t.Comments.DefaultIfEmpty()
from r in c.Recipients.DefaultIfEmpty()
select new Result()
{
TaskStatus = ???
...
}
).Distinct();
Aside: calling Distinct after a bunch of joins is a crutch. #1 See if you can do without it. #2 If not, see if you can eliminate any bad data that causes you to have to call it. #3 If not, call Distinct in a smaller scope than the whole query.
Hope this helps.
SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight], [t0].[ShipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[ShipPostalCode], [t0].[ShipCountry]
FROM [Orders] AS [t0]
LEFT OUTER JOIN ([Order Details] AS [t1]
INNER JOIN [Products] AS [t2] ON [t1].[ProductID] = [t2].[ProductID]) ON [t0].[OrderID] = [t1].[OrderID]
can be write as
from o in Orders
join od in (
from od in OrderDetails join p in Products on od.ProductID equals p.ProductID select od)
on o.OrderID equals od.OrderID into ood from od in ood.DefaultIfEmpty()
select o

Resources