JPA How to build a join criteria query - mysql

I have the following situation 3 tables, person, job, category.
Person has a job and Job has a category. How do I get the person records from the same category.
public List<Person> findPplByCategory(Category category) {
javax.persistence.criteria.CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root<Person> e = cq.from(Person.class);
javax.persistence.criteria.Root<Job> a = cq.from(Job.class);
//...not sure how to create the query here..
}

This is easy to do using a JPQL query
String query = "select P from Person P join P.job J join J.category C where C = :cat"
List<Person> = entitiyManager.createQuery(query, Person.class).setParameter("cat", myCategory).getResultList();
Some assumptions:
One Person has one Job
One Job has one Category
Person's job field is named job
Job's category field is named category

haven't tested but may be something like
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> q = cb.createQuery(Person.class);
Root<Person> person = q.from(Person.class);
Join<Person,Job> job = person.join(Person_.job);
Predicate predicate = cb.conjunction();
Predicate p = cb.equal(job.get(Job_.category), cat);
predicate = cb.and(p);
q.where(predicate);
q.select(person);
TypedQuery<Person> tq = entityManager.createQuery(q);
List<Person> all = tq.getResultList();
return all;

javax.persistence.criteria.CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root<Person> e = cq.from(perdon.class);
cq.where(cb.equal(e.get("idCompany").get("idCategory"), cat));
cq.orderBy(cb.desc(e.get("idPerson")));
return getEntityManager().createQuery(cq).getResultList();

my version is:
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> root = cq.from(Person.class);
Join<Person,Job> job = root.join(Person_.job);
Join<Job,Category> category =car.join(Car_.category).where(cb.equal(job.get(Job_.category).get(Category_.name),"your category"));
cq.select(root);
TypedQuery<Person> query = getEntityManager().createQuery(cq);
return query.getResultList();
Here you have that Person is one-to-one with Job and the latter is one-to-one with Category :) You will receive you all persons which is in specified category of job.

Related

I don't know how to convert sql query to criteriabuilder in hibernate?

I need an total budget value. I can getting the answer from sql query but the problem is how to convert sql query to criteriaBuilder in hibernate.Here below the sql query:
SELECT SUM(ab.daily_budget) as todayBudgetTar FROM api_budget ab INNER JOIN api_ad_groups ad on ad.ad_group_id= ab.adgroup_id INNER JOIN api_campaigns c on c.campaign_id =ad.campaign_id INNER JOIN api_user_account ac on ac.user_id=ad.user_id WHERE ad.user_id = 234 AND ad.status=0 AND c.status=1 GROUP by "todayBudgetTar"
If someone knows plz help me.
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> query = criteriaBuilder.createQuery(Object[].class);
Root<ApiBudget> budget = query.from(ApiBudget.class);
Root<ApiAdGroups> adGroups = query.from(ApiAdGroups.class);
Root<ApiCampaigns> campaigns = query.from(ApiCampaigns.class);
Root<ApiUserAccount> userAccount = query.from(ApiUserAccount.class);
//this one is on your entity field name
query.select(criteriaBuilder.sum(budget.get("dailyBudget"));
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(
qb.equal(adGroups.get("adGroupId"), budget.get("adGroupId")));
predicates.add(
qb.equal(campaigns.get("campaignId "), adGroups.get("campaignId")));
predicates.add(
qb.equal(userAccount.get("userId"), adGroups.get("userId")));
predicates.add(
qb.equal(adGroups.get("userId"), 234));
predicates.add(
qb.equal(adGroups.get("status"), 0));
predicates.add(
qb.equal(campaigns.get("status"), 1));
query.where(predicates.toArray(new Predicate[]{}));
query.groupBy(budget.get("dailyBudget"));
TypedQuery<Object[]> typedQuery = entityManager.createQuery(query);
List<Object[]> resultList = typedQuery.getResultList();
Hi, i think this should work
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Object> query = builder.createQuery(Object.class);
Root<Budget> budget = query.from(Budget.class);
Join adsetJoin = budget.join("adsetId");
Join campaignJoin = adsetJoin.join("campaign");
query.multiselect(builder.sum(budget.get("dailyBudget")));
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(builder.equal(adsetJoin.get("status"), 0));
predicates.add(builder.equal(adsetJoin.get("userId"), userId));
predicates.add(builder.equal(campaignJoin.get("campaignStatus"), CAMPAIGNSTATUS.RUNNING));
if ((dashboardRequest.getStartDate() != null) && (dashboardRequest.getEndDate() != null)) {
Date fromDate = new Date(dashboardRequest.getStartDate());
Date toDate = new Date(dashboardRequest.getEndDate());
predicates.add(builder.between(adsetJoin.get("createdDate"), fromDate, toDate));
}
if (!predicates.isEmpty()) {
Predicate[] pr = new Predicate[predicates.size()];
pr = predicates.toArray(pr);
query.where(pr);
}
query.groupBy(budget.get("dailyBudget"));
List<Object> resultList = session.createQuery(query).getResultList();
List<Long> resultList2=resultList.stream().map(v ->((Number)v).longValue()).collect(Collectors.toList());
return resultList2;

Convert complex nested selects in Entity Framework query

I need to create a table in View by this View Model:
public class ApplicationContentViewModel
{
public BPMSPARS.Models.MySql.application application {get; set;}
public BPMSPARS.Models.MySql.content content { get; set; }
public BPMSPARS.Models.MySql.app_delegation app_delegation { get; set; }
}
But the query for creating new Table is very complex.
I use this query in MySQL, and I can get correct results by using it.
SELECT APP_UID, (SELECT CON_VALUE FROM content WHERE CON_CATEGORY = 'PRO_TITLE' AND CON_ID =
(SELECT PRO_UID from app_delegation WHERE del_thread_status='open' and USR_UID = '00000000000000000000000000000001' AND APP_UID = '9134216305aaaea1b67c4e2096663219')) AS TASK_NAME,
(SELECT CON_VALUE FROM content WHERE CON_CATEGORY = 'TAS_TITLE' AND CON_ID =
(SELECT TAS_UID from app_delegation WHERE del_thread_status='open' and USR_UID = '00000000000000000000000000000001' AND APP_UID = '9134216305aaaea1b67c4e2096663219')) AS PROCESS_NAME FROM app_delegation
WHERE del_thread_status='open' and USR_UID = '00000000000000000000000000000001' AND APP_UID = '9134216305aaaea1b67c4e2096663219'
But, I have to convert this query in linq or EF in MVC.
How Can I write This Query in Entity Framework query?
And How Can I display results in View?
Your SQL query seems (very) peculiar to me, as it is quite redundant. I am going to assume the sub-queries return a single value and enforce it with LINQ.
First I pulled out the common sub-query over app_delegation:
var USR_APP_Delegation = from a in app_delegation
where a.del_thread_status == "open" &&
a.USR_UID == "00000000000000000000000000000001" &&
a.APP_UID == "9134216305aaaea1b67c4e2096663219"
select a;
In LINQ it is easy to combine the two UID queries into one query:
var UIDs = (from a in USR_APP_Delegation
select new { a.PRO_UID, a.TAS_UID })
.Single();
Now you can do the name subqueries:
var TASK_NAME = (from c in content
where c.CON_CATEGORY == "PRO_TITLE" &&
c.CON_ID == UIDs.PRO_UID
select c.CON_VALUE)
.Single();
var PROCESS_NAME = (from c in content
where c.CON_CATEGORY == "TAS_TITLE" &&
c.CON_ID == UIDs.TAS_UID
select c.CON_VALUE)
.Single();
Then you can put all the queries together for the final result:
var ans = (from a in USR_APP_Delegation
select new {
a.APP_UID,
TASK_NAME,
PROCESS_NAME
})
.Single();
Again, this makes it obvious that your e.g. returning APP_UID when you know exactly what it is, and you are combining TASK_NAME and PROCESS_NAME into a query for no real advantage.
I would suggest using join against content makes a much more understandable query (even in SQL) and makes it clearer what is being returned:
var names = from a in app_delegation
join cpro in content on new { CON_ID = a.PRO_UID, CON_CATEGORY = "PRO_TITLE" } equals new { cpro.CON_ID, cpro.CON_CATEGORY }
join ctas in content on new { CON_ID = a.PRO_UID, CON_CATEGORY = "TAS_TITLE" } equals new { ctas.CON_ID, ctas.CON_CATEGORY }
where a.del_thread_status == "open" &&
a.USR_UID == "00000000000000000000000000000001" &&
a.APP_UID == "9134216305aaaea1b67c4e2096663219"
select new {
a.APP_UID,
Task_Name = ctas.CON_VALUE,
Process_Name = cpro.CON_VALUE
};

(Laravel) Join 3 or more tables using "Eloquent Relationships"

I'm trying to join 3 tables using eloquent relationships, but it doesn't give the expected results.
Shipment model
class Shipment extends Model
{
protected $table = 'ccctadm.Shipment';
public function customergroupcode()
{
return $this->hasMany(DocumentRuleSet::class,'customergroupcode','customergroupcode');
}
public function shipmentcategory()
{
return $this->hasMany(DocumentRuleSet::class,'shipmentcategory','shipmentcategory');
}
public function status()
{
return $this->hasMany(DocumentRuleSet::class,'status','status');
}
}
to get the data i'm using this code
$shipment_data = Shipment::With(['customergroupcode' , 'shipmentcategory','status'])->
Where('shipment_cycle','!=','closed')->get();
I'm trying to make it equivalent to this query
select B.rulesetname,B.icon ,COUNT(*)As Total from
[ccct].[ccctadm]. [Shipment] A INNER JOIN
[ccct].[ccctadm].[documentruleset] B
ON
A.customergroupcode = B.customergroupcode AND A.shipmentcategory =
B.shipmentcategory AND A.status = B.status INNER
JOIN [ccctadm].[shipassign] S ON A.id = S.shipmentid AND
A.shipment_cycle != 'closed' GROUP BY rulesetname,B.icon
The first query returns all the data in 3 tables, but when the second one returns Release only and this what I want
I only want the data that has relation among these three tables not everything
What I'm doing wrong ?
Use has() method. It limits the records based on existence of the relationship.
$shipment_data = Shipment::has('customergroupcode' , 'shipmentcategory','status')->
Where('shipment_cycle','!=','closed')->get();

Select record where id is one of collection id

In a spring mvc application i have 3 database tables (including one mapping table) and their 2 corresponding java entities.
The entities are:-
public class User {
private Long id;
private String userName;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "user_location",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "location_id", referencedColumnName = "id")}
)
private Set<Location> location;
}
public class Location {
private Long id;
private String locationCode;
}
Three tables are users, location and user_location.
I want to select a user whose location id is equal to a particular id.
Since user can have multiple locations i am not sure how to write a hibernate query for this. I tried few combinations below but i am either getting a exception,
illegal attempt to dereference collection [{synthetic-alias}{non-qualified-property-ref}] with element property reference [id]
or getting a list of User and Location objects. i just want a list of user objects.
from User where userName = :userName and :locationId in (location.id)
from User user inner join user.location loc where user.userName = :usersName and loc.id = :locationId
Update:
I tried query,
Query query = getCurrentSession().createQuery("from User user inner join user.location loc where user.userName = :usersName and loc.id = :locationId");
Hibernate generates following plain SQL from above query and returns a list of User and Location objects. For example if there is one location for an user that matches above query hibernate returns a list that contains one User and one Location object.
select
user0_.id as id1_18_0_,
location2_.id as id1_5_1_,
user0_.user_name as user_na11_18_0_,
location2_.location_code as location3_5_1_
from
users user0_
inner join
user_location location1_
on user0_.id=location1_.user_id
inner join
location location2_
on location1_.location_id=location2_.id
where
user0_.user_name=?
and location2_.id=?
You could try to use next criteria:
Criteria criteria = getCurrentSession().createCriteria(User.class, "u");
criteria.createAlias("location", "loc");
criteria.add(Restrictions.eq("u.userName", "userName");
criteria.add(Restrictions.eq("loc.id", locationId);
List<User> users = criteria.list();
Or you could try HQL typed query:
TypedQuery<User> query =
getCurrentSession().createQuery("SELECT User.* FROM User u JOIN user_location ul ON u.id = ul.user_id JOIN Location l ON ul.location_id = l.id WHERE u.userName = :userName AND l.id = :locationId", User.class)
.setParameter("userName", "userName")
.setParameter("locationOd", locationId);
List<User> users = query.getResultList();

Why is LINQ to SQL Omitting Columns in Select

I'm using LINQ to SQL to select records. I need to union two queries together but the select statements are being changed so that the expressions no longer match preventing the union.
This LINQ query omits my forced columns 'resultType' and 'imageFile' from the final result.
var taglist = from t in dc.ProductTags
where t.Tag.StartsWith(prefixText)
select new AutoSearch {
resultType = "Tag",
name = t.Tag,
imageFile = string.Empty,
urlElement = t.Tag };
This is the query that is presented.
{SELECT [t0].[Tag] AS [name] FROM [dbo].[ProductTag] AS [t0] WHERE [t0].[Tag] LIKE #p0}
This is the second query to be unioned with the initial one.
var brandlist = from b in dc.Businesses
join t in dc.Tags on b.BusinessId equals t.BusinessId
where b.Name.StartsWith(prefixText)
where b.IsActive == true
where t.IsActive == true
select new AutoSearch
{
resultType = "Business",
name = b.Name,
imageFile = t.AdImage,
urlElement = b.BusinessId.ToString() };
This is the sql for the second query.
SELECT [t0].[Name] AS [name], [t1].[AdImage] AS [imageFile], CONVERT(NVarChar(MAX) [t0].[BusinessId]) AS [urlElement] FROM [dbo].[Business] AS [t0] INNER JOIN [dbo].[Tag] AS [t1] ON ([t0].[BusinessId]) = [t1].[BusinessId] WHERE ([t0].[Name] LIKE #p0)
The union... that throws the error.
var unionedResults = taglist.Union(brandlist);
The error thrown.
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
This is the AutoSearch class.
public class AutoSearch
{
public string name { get; set; }
public string imageFile { get; set; }
public string resultType { get; set; }
public string urlElement { get; set; }
}
Suggestions as to what is going???
UPDATE***
Found a work around...
Found the issue.
This is a known bug in LINQ, several discussions found here on SO that pointed me in the right direction. Turns out most of the work arounds listed on the site are no longer valid because version 4.0 of broke them too. I found another that worked..
LINQ omits duplicate values for optimization purposes. I was able to change the values of the throw away fields by converting them to strings or lower case or concatenating them.
Terribly inefficient, but it works. Whole day lost for me on this one, perhaps it will save others time.
var taglist = from t in dc.ProductTags
where t.Tag.StartsWith(prefixText)
let resultType = "Tag"
select new AutoSearch() {
resultType = resultType,
name = t.Tag,
imageFile = t.Tag.ToString(),
urlElement = t.Tag.ToLower()
};
var brandlist = from b in dc.Businesses
join t in dc.Tags on b.BusinessId equals t.BusinessId
where b.Name.StartsWith(prefixText)
where b.IsActive == true
where t.IsActive == true
where t.AdImage != null
where t.AdImage != String.Empty
let resultType = "Business"
select new AutoSearch
{
resultType = resultType,
name = b.Name,
imageFile = t.AdImage,
urlElement = b.BusinessId.ToString()
};
The only property you reference when you do the select part of your query is Tag, Linq to Sql knows this and optimizes the query to only select columns you're referencing.
In other words, this section of your query only refers to the "Tag" property, which is tied to the Tag column on your database.
new AutoSearch {
resultType = "Tag",
name = t.Tag,
imageFile = string.Empty,
urlElement = t.Tag };
What Linq does in this case is pass an expression to the underlying provider (very similar to a binary tree data structure). The provider then parses this tree and creates a SQL query from it at run time. The optimization is done by the provider at runtime which results in the SQL query you're seeing.
Update
For the second problem with the union you basically are trying to union two different SQL statements which is causing the union error. So lets take a look.
The resulting statement that would be causing the error would look something like this
SELECT [t0].[Tag] AS [name] FROM [dbo].[ProductTag] AS [t0] WHERE [t0].[Tag] LIKE #p0
UNION
SELECT [t0].[Name] AS [name], [t1].[AdImage] AS [imageFile], CONVERT(NVarChar(MAX) [t0].[BusinessId]) AS [urlElement] FROM [dbo].[Business] AS [t0] INNER JOIN [dbo].[Tag] AS [t1] ON ([t0].[BusinessId]) = [t1].[BusinessId] WHERE ([t0].[Name] LIKE #p0)
Obviously this is problametic since there is not the same number of columns between the two and that doesn't fly with SQL. While I do not have a pure linq solution there is a workaround.
First You'll need to create a SQL function that just returns a string sent to it.
CREATE FUNCTION ReturnString( #string varchar(max) )
RETURNS varchar(max)
AS
BEGIN
RETURN #string
END
GO
Next drag and drop this new SQL function into your dbml file, and finally in your query simply call the method where appropriate.
var taglist = from t in dc.ProductTags
where t.Tag.StartsWith(prefixText)
select new AutoSearch
{
resultType = dc.ReturnString("Tag"),
name = t.Tag,
imageFile = dc.ReturnString(string.Empty),
urlElement = dc.ReturnString(t.Tag)
};
var brandlist = from b in dc.Businesses
join t in dc.Tags on b.BusinessId equals t.BusinessId
where b.Name.StartsWith(prefixText)
where b.IsActive == true
where t.IsActive == true
select new AutoSearch
{
resultType = dc.ReturnString("Business"),
name = b.Name,
imageFile = t.AdImage,
urlElement = b.BusinessId.ToString()
};
Now you should be able to perform the union.