Can anyone give me the equivalent Hibernate query for the MySQL query given below. I am not trying it from past few day but no success.
This is MySql query
SELECT * FROM product WHERE category_id IN (SELECT id FROM category WHERE parent_category_id IN (SELECT id FROM category WHERE parent_category_id=53));
The Hibernate query which i had written is.
public List<Product> findBy2ndLevel(String categoryName) {
Query query = null;
StringBuilder hql = new StringBuilder();
try {
hql.append("from Product product where product.category.id in");
hql.append("(select id from Category category where category.parentCategory.id in");
hql.append("(select id from Category category where category.parentCategory.id=:category_id))");
query = sessionFactory.getCurrentSession().createQuery(hql.toString());
query.setParameter("category_id",Integer.parseInt(categoryName));
} catch (HibernateException e) {
e.printStackTrace();
}
return query.list();
}
It is not working though. Give anyone correct or give me the equivalent HQ.
I'm assuming that the entity Product has 1 category and a variable for this (called category), and that a category has 1 parent category and a variable for this (called parent).
But, something like:
select p
from Product p join p.category c join c.parent pc
where pc.id = :categoryId
Furthermore, I don't really get your variable use, you link the id of category with categoryName, and include a variable orgaId which is never really used for anything.. Also, your code will produce a NullPointerException if the creation of the query fails.
Related
I am trying to created a set of records based on some criteria and i need to figure out the best way to do this.
I want to create a record for every object A and B that has an specific Account object. object A needs to have a status of "DONE", and B a status of "READY" in order to create the record (which will be an Object A with an "Active" status) the fields on the new Object A will copy from some of the Object A and B fields.
This is a process im not triggering from user action but a set of records i need to already dump in the database. I do have a sandbox to start working on and testing then rolling out.
Please let me know the easiest way to do this.
I appreciate the help!
You didn't provide enough info, we don't know how the relations look like. Are A and B related lists under Account? Are they independent or is there some link from one to another?
I'll write what I would do as a script (if needed you could make a batch job out of it or perhaps you'll be more comfortable with Data Loader, reports with cross filters, MS Excel and orgy of VLOOKUPs...)
To identify all "candidate" accounts you can try with this skeleton of a query
SELECT Id, Name
FROM Account
WHERE Id IN (SELECT Account__c FROM A__c WHERE Status__c = 'Done')
AND Id IN (SELECT Account__c FROM B__c WHERE Status__c = 'Ready')
LIMIT 10000
Now, the question about amounts of data. Will it return 10K (which is limit of records you can insert/update/modify in single transaction), if more - you might have to chunk it somehow... Maybe ORDER BY Id, record Id of last processed Account and in next iteration add AND Id > '001....'
Anyway, we got a "candidate", well, maybe he already has an Active A record, we wouldn't want to make a duplicate. And besides we need to pull some fields from B so they'd be copied across. So let's modify the query a bit, to add "subqueries" (think of them as related lists or LEFT INNER JOINs if that helps)
SELECT Id, Name,
(SELECT Id FROM As__r WHERE Status__c = 'Active' LIMIT 1),
(SELECT SomeField__c, SomeOtherField__c FROM Bs__r WHERE Status__c = 'Ready' LIMIT 1)
FROM Account
WHERE Id IN (SELECT Account__c FROM A__c WHERE Status__c = 'Done')
AND Id IN (SELECT Account__c FROM B__c WHERE Status__c = 'Ready')
LIMIT 10000
Nice. So now you need to loop through accounts, see if they contain that at least 1 active record (and if they do - skip). If they don't - create new one.
List<A__c> toInsert = new List<A__c>();
for(Account a : [SELECT...]){
if(a.As__r.isEmpty() && !a.Bs__r.isEmpty()){
toInsert.add(new A__c(
Account__c = a.Id,
Status__c = 'Active',
Foo__c = a.Bs__r[0].SomeField__c,
Bar__c = a.Bs__r[0].SomeOtherField__c + 7
));
}
}
insert toInsert;
I have custom objects Team member and Employment, and there's a lookup relationship from employment(many) to team member(one), plus another lookup record on team member called current employment.
The employment record may have attachments.
I want a SOQL query, to run in an APEX class, which will return attachments information for specific team members.
So far I have this:
SObject[] results = [select id,(select id,name from Attachments) from Employment__c where id in (select Current_Employment__c from Team_Member__c where id=:id)];
Wnen I run the query in the schema browser, it works OK and I'm able to drill-down to the attachments, but when I run it in Apex (Anonymous), the result set does not contain the attachments:
for (SObject result : results) {
System.debug(result);
}
I can only see the Employment id in the results.
How can I get attachments in APEX?
You do following to get list of attachment related to that object.
Employment__c[] results = [select id,(select id,name from Attachments) from Employment__c where id in (select Current_Employment__c from Team_Member__c where id=:id)];
for (Employment__c result : results) {
if(result.Attachments!=null){
List<Attachment> AttachmentList=result.Attachments;
}
}
I can't for the life of me figure out how to translate the following SQL query using NHibernate's Criteria API:
SELECT r.* from ContentItemVersionRecords as r
INNER JOIN (
SELECT ContentItemId as CID, Max(Number) as [Version]
FROM ContentItemVersionRecords
GROUP BY ContentItemId
) AS l
ON r.ContentItemId = l.CID and r.Number = l.[Version]
WHERE Latest = 0 and Published = 0
The table looks like this:
The result of the SQL query above will return the highlighted records.
The idea is to select the latest version of content items, so I basically need to group by ContentItemId and get the record with the highest Number.
So the result will look like this:
I started out with a detached criteria, but I am clueless as to how to use it in the criteria:
// Sub select for the inner join:
var innerJoin = DetachedCriteria.For<ContentItemVersionRecord>()
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty("ContentItemId"), "CID")
.Add(Projections.Max("Number"), "Version"));
// What next?
var criteria = session.CreateCriteria<ContentItemVersionRecord>();
Please note that I have to use the Criteria API - I can't use LINQ, HQL or SQL.
Is this at all possible with the Criteria API?
UPDATE: I just came across this post which looks very similar to my question. However, when I apply that as follows:
var criteria = session
.CreateCriteria<ContentItemVersionRecord>()
.SetProjection(
Projections.ProjectionList()
.Add(Projections.GroupProperty("ContentItemId"))
.Add(Projections.Max("Number")))
.SetResultTransformer(Transformers.AliasToBean<ContentItemVersionRecord>());
I get 2 results, which looks promising, but all of the integer properties are 0:
UPDATE 2: I found out that if I supply aliases, it will work (meaning I will get a list of ContentItemVersionRecords with populated objects):
var criteria = session
.CreateCriteria<ContentItemVersionRecord>()
.SetProjection(
Projections.ProjectionList()
.Add(Projections.Max("Id"), "Id")
.Add(Projections.GroupProperty("ContentItemId"), "ContentItemId")
.Add(Projections.Max("Number"), "Number"))
.SetResultTransformer(Transformers.AliasToBean<ContentItemVersionRecord>());
However, I can't use the projected values as the end result - I need to use these results as some sort of input into the outer query, e.g.
SELECT * FROM ContentItemVersionRecord WHERE Id IN ('list of record ids as a result from the projection / subquery / inner join')
But that won't work, since the projection returns 3 scalar values (Id, ContentItemId and Number). If it would just return "Id", then it might work. But I need the other two projections to group by ContentItemId and order by Max("Number").
OK, so in a nutshell, you need to unwind that nested query, and do a group by with a having clause, which is pretty much a where on aggregated values, as in the following HQL:
SELECT civ.ContentItem.Id, MAX(civ.Number) AS VersionNumber
FROM ContentItemVersionRecord civ
JOIN ContentItem ci
GROUP BY civ.ContentItem.Id " +
HAVING MAX(civ.Latest) = 0 AND MAX(civ.Published) = 0
This gives you, for each deleted content items (those have all their latest and published flags to zero on all their content item version records), the maximum version number, i.e. the latest version of each deleted content item.
I'm working with an Nhibernate Query where I have to do some complex queryover join aliases to eagerly load the children of my root entity. When loading, I want to filter the root entity results returned by a number of properties, including some which are on the children.
I've got this all working fine using joinaliases, but where I'm stumped is filtering the results returned down to the top "X" instances of the root entity when ordered by a property other than the root entities Id. Since I'm grabbing children, there are a number of duplicate rows returned by the SQL. If I try to filter the number of results with a .Take, the take executes before NHibernate collapses the result set down to the distinct root entities. For reference here's my domain model.
public class Project{
public int Id {get;set;}
public double Value {get;set;}
public IList<ProjectRole> Team {get;set;}
}
public class ProjectRole{
public User User {get;set;}
public Role Role {get;set;}
}
public class User{
public string LoginName {get;set;}
}
So I'm trying to grab all the projects where a User with the given LoginName is on the Project's Team. Then I want to order by the Project's value. I want to do this as efficiently as possible, without select n+1's etc.
What does this community recommend?
Additional Information:
As a stopgap, I'm currently returning all the results and then taking the top X in memory, but I don't want that to be permanent, because the query can return close to 10,0000 items, and I only want to top 7 or so. If I was writing straight SQL I'd just do something like this.
SELECT *
FROM Projects as p1
INNER JOIN (
SELECT distinct TOP (7)
topProjects.PGISourceItem_id as topsId,
topProjects.Value as topsValue
FROM Projects topProjects
left outer join ProjectRoles roles on topProjects.Id=roles.Project_id
left outer join PGUsers users on roles.User_id=users.Id
WHERE
(users.LoginName like 'DEV\APPROVER' or this_0_1_.IsPrivate = 0)
ORDER BY topProjects.Value desc
) as p2 on p1.Id = p2.topsId
But I can't figure out how to do this with NHibernate. The only subqueries I can create are either WHERE EXISTS or WHERE IN. And since I'm doing an ORDER BY Value I can't use WHERE IN because my select returns multiple properties.
if users have under 1k projects this might work
var subquery = QueryOver.Of<User>()
.Where(...)
.JoinAlias(x => x.Projects, () => proj)
.Select(Projections.Distinct(Projections.Property(() => proj.Id)));
session.QueryOver<Foo>()
.WithSubquery.WhereProperty(x => x.Id).In(subquery)
.Fetch(p => p.Collection)
.OrderBy(x => x.Value)
.Take(5)
.List();
Relatively new to JPA, so I have one kind of architectural question.
Let's say I have tables EMPLOYEE and DEPARTMENT with many to one relationship (i.e. many employees work for one department):
EMPLOYEE
EMPLOYEE_ID
EMPLOYEE_NAME
DEPARTMENT_ID
DEPARTMENT
DEPARTMENT_ID
DEPARTMENT_NAME
So I can define proper entities for Employee and Department, there's no problem. However, in one view I would like to display list of departments with number of employees working for that department, something like this:
SELECT D.DEPARTMENT_NAME,
(SELECT COUNT(*) FROM EMPLOYEE E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID) NUMBER_OF_EMPLOYEES
FROM DEPARTMENT D
I'm just not sure what is the right strategy to accomplish this using JPA...
I don't want to always fetch number of employees for Department entity, as there is only one view when it is needed.
It looks like Hibernate's #Formula would be one possible approach, but afaik it does not conform with JPA standard.
You can create any object in your QL using the "new" syntax - your class just needs a constructor that takes the values returned by your query.
For example, with a class like DepartmentEmployeeCount, with a constructor:
public DepartmentEmployeeCount(String departmentName, Integer employeeCount)
you could use QL something like:
SELECT NEW DepartmentEmployeeCount(D.DEPARTMENT_NAME, count(E.id)) from Department D left join D.employees E GROUP BY D.DEPARTMENT_NAME
Or if you were just selecting the count(*) you could simply cast the query result to a Number.
Alternatively, to do the same without the DepartmentEmployeeCount class, you could leave out the NEW, so:
SELECT D.DEPARTMENT_NAME, count(E.id)
This would return a List<Object[]> where each list item was an array of 2 elements, departmentName and count.
To answer your later question in the comments, to populate all fields of a Department plus a transient employeeCount field, one suggestion would be to do 2 queries. This would still be more efficient than your original query (a subselect for each employee count).
So one query to read the departments
SELECT D from Department D
giving you a List<Department>
Then a 2nd query returning a temporary array:
SELECT D.DEPARTMENT_ID, count(E.id) from Department D left join D.employees E GROUP BY D.DEPARTMENT_ID
giving you a List<Object[]> with DEPARTMENT_ID and count in it.
Then you use the 2nd list to update the transient count property on your first list.
(You could try selecting into a Map to make this lookup easier, but I think that's a Hibernate feature).
Option 1: I suggested this since you didn't like the constructor route MattR was suggesting. You mentioned the word "view" several times, and I know you were talking about the view to the user, but why not setup a view in your database that includes the computed columns and then create a read-only entity that maps to the computed columns?
Option 2: In response to your comment about not wanting to create a view. You could create a container object that holds the entity and the calculated column, then much like MattR suggests, you use a new in your select. Something like:
public class DepartmentInfo {
private Department department;
// this might have to be long or something
// just see what construct JPA tries to call
private int employeeCount;
public DepartmentInfo( Department d, int count ) {
department = d;
employeeCount = count;
}
// getters and setters here
}
Then your select becomes
SELECT new my.package.DepartmentInfo( D,
(SELECT COUNT(*) FROM EMPLOYEE E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID))
FROM DEPARTMENT D
With that you can use the DepartmentInfo to get the properties you are interested in.
You could create a member in your entity as an additional column, and then reference it with an alias in your query. The column name in the #Column annotation must match the alias.
Say, for your original query, you can add a countEmployees member as following. Also add insertable=false and updatable=false so the entity manager wont try to include it in insert or update statements:
public class Department {
#Column(name="DEPARTMENT_ID")
Long departmentId;
#Column(name="DEPARTMENT_NAME")
String departmentName;
#Column(name="countEmployees", insertable=false, updatable=false)
Long countEmployees;
//accessors omitted
}
And your query:
SELECT D.DEPARTMENT_NAME,
(SELECT COUNT(*) FROM EMPLOYEE E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID) AS countEmployees
FROM DEPARTMENT D
This also applies when working with Spring Data Jpa Repositories.