Hibernate CriteriaQuery where - ManyToOne field - spring

that's fragment of my Employee entity/class:
#Entity
#Table
public class Employee {
...
#ManyToOne(.....)
#JoinColumn(name="department_id")
private Department department;
}
Now, I want to fetch all Employees from specific Department (employees with specific department_id):
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = builder.createQuery(Employee.class);
Root<Employee> employeeRoot = criteriaQuery.from(Employee.class);
criteriaQuery.select(employeeRoot);
criteriaQuery.where(builder.equal(employeeRoot.get(????????????), id));
There's my problem. How do I access Department.id? I know i have to get({specific field}) but i dunno how. Should I somehow join those tables first? Looking forward for your answers!

You need create a join with your relationed table.
Ex:
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = builder.createQuery(Employee.class);
Root<Employee> employeeRoot = criteriaQuery.from(Employee.class);
Join<Employee, Departament> join = from.join("department", JoinType.INNER);
criteriaQuery.where(builder.equal(join.get("specific_fied"), your_specific_fied));
TypedQuery<Employee> typedQuery = session.createQuery(criteriaQuery);
List<Employee> employees = typedQuery.getResultList();
for (Employee e : employees) {
System.out.println(e.getName());
}
session.getTransaction().commit();
session.close();
entityManagerFactory.close();
If you want anything same as this query:
SELECT c, p.name FROM Country c LEFT OUTER JOIN c.capital p
you can build this:
CriteriaQuery<Country> q = cb.createQuery(Country.class);
Root<Country> c = q.from(Country.class);
Join<Country> p = c.join("capital", JoinType.LEFT);
q.multiselect(c, p.get("name"));
If you what build anything same as this query:
SELECT c FROM Country c JOIN FETCH c.capital
you can build this:
CriteriaQuery<Country> q = cb.createQuery(Country.class);
Root<Country> c = q.from(Country.class);
Fetch<Country,Capital> p = c.fetch("capital");
q.select(c);
For more exemples see FROM clause (JPQL / Criteria API)

Related

Linq Query across 3 tables

My Domain model is as follows:
User { Id, FirstName, LastName, TeamId }
Team { Id, Name, Description }
Topic { Id, Title, UserId }
My application logic says that a user should be able to view all topics within his/her team.
Knowing the above, how can I write a linq query that will get all topics that have been made by people on the same team as the user?
I have tried the following, but it is obviously wrong, and I just can't see through the logic for the linq query :(
var topicList = (from u in context.Users
join t in context.Topics on u.Id equals t.UserId
where u.TeamId == id
select new Tourist.WEB.Models.TopicListViewModel
{
Id = t.Id,
Title = t.Title,
TopicAuthor = u.FirstName,
NoOfReplies = 3
}).ToList();
like this:
from u in context.Users
join t in context.Team on u.TeamId equals t.Id
join to in context.Topic on u.id equals to.UserId
where u.TeamId == id
Complete query:
var topicList = (from u in context.Users
join t in context.Team on u.TeamId equals t.Id
join to in context.Topic on u.id equals to.UserId
where u.TeamId == id
select new Tourist.WEB.Models.TopicListViewModel
{
Id = to.Id,
Title = to.Title,
TopicAuthor = u.FirstName,
NoOfReplies = 3
}).ToList();

How to update a particular field of a particular object in Generic list in c#.net using LINQ

I am creating a Generic List of objects of Employee class.
List<Employee> EmployeeList = new List<Employee>
{
new Employee { ecode=1, ename="Rohit", salary=20000, mgrid="3" },
new Employee { ecode=2, ename="Sangeeta", salary=12000, mgrid="5"},
new Employee { ecode=3, ename="Sanjay", salary=10000, mgrid="5"},
new Employee { ecode=4, ename="Arun", salary=25000, mgrid="3"},
new Employee { ecode=5, ename="Zaheer", salary=30000, mgrid=null}
};
'mgrid' is the Employee ID of the Manager. I want just increment the salary of the Managers.
I tried doing it using the following approach, but salaries of all the employees are getting incremented.
var mgrsal = from e1 in emplst
join e2 in emplst
on e1.ecode.ToString() equals e2.mgrid into empdata
select new Employee
{
ecode = e1.ecode,
ename=e1.ename,
salary=e1.salary*1.1,
mgrid = e1.mgrid
};
I am coding in C#.net in Visual Studio IDE and I also tried to use Contains() and Any() methods, but I am unable to use.
LINQ is all about querying, not updating. You can use LINQ to get all managers from the list,
var managers = from e in emplst
where emplst.Any(x => x.mgrid == e.ecode.ToString())
select e;
or using JOIN (it would require implementing Equals and GetHashCode methods on Employee class to make Distinct work:
var managers = (from e in emplst
join e2 in emplst on e.ecode.ToString() equals e2.mgrid
select e).Distinct();
and then iterate over and change the salary:
foreach(var manager in managers)
manager.salary *= 1.1;

Why org.hibernate.TransientObjectException during join/select?

Trying to wrap my head around Criteria API (ouch). I have 3 classes: Devices, Offices, and SiteCodes. all joined
Devices.java
private Offices office;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "office_id")
public Offices getOffice() {
return office;
}
public void setOffice(Offices office) {
this.office = office;
}
Offices.java:
private List<SiteCodes> siteCodes;
#ManyToMany(cascade = CascadeType.PERSIST)
#JoinTable(name = "site_code_map", joinColumns = {
#JoinColumn(name = "office_id") }, inverseJoinColumns = {
#JoinColumn(name = "site_code_id") })
#OrderBy("siteCode ASC")
public List<SiteCodes> getSiteCodes() {
return this.siteCodes;
}
public void setSiteCodes(List<SiteCodes> siteCodes) {
this.siteCodes = siteCodes;
}
SiteCodes.java
private id;
private String siteCode;
<getters and setters>
I'm trying to find Devices.devId using the site code. The sql would look like this:
SELECT d.dev_id
FROM devices d, offices o, site_code_map s, site_codes ss
WHERE d.office_id=o.office_id
AND s.office_id=o.office_id
AND s.site_code_id=ss.site_code_id
AND ss.`site_code`='0S21'
I'm trying to use joins, but don't quite get how to do it. I got the following to compile:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Integer> cq = cb.createQuery(Integer.class);
Root<Devices> d = cq.from(Devices.class);
Join<Devices, Offices> join1 = d.join(Devices_.office);
ListJoin<Offices,SiteCodes> join2 = join1.join(Offices_.siteCodes);
I couldn't figure how to use join2 so I used join1 and a path like this
ParameterExpression<SiteCodes> p = cb.parameter(SiteCodes.class, "sitecode");
Predicate pr = cb.isMember(p, join1.get(Offices_.siteCodes));
cq.where(pr);
and eventually
TypedQuery<Integer> tq = em.createQuery(cq);
<set parameter here to a SiteCode object>
List<Integer> idList = tq.getResultList();
It produces sql like the following. That subquery doesn't belong - I want it to use a join and anyway it throws a TransientObjectException even though all I'm doing is selecting:
select
devices0_.dev_id as col_0_0_
from
devices devices0_
inner join
offices offices1_
on devices0_.office_id=offices1_.office_id
inner join
site_code_map sitecodes2_
on offices1_.office_id=sitecodes2_.office_id
inner join
site_codes sitecodes3_
on sitecodes2_.site_code_id=sitecodes3_.site_code_id
where
? in (
select
sitecodes4_.site_code_id
from
site_code_map sitecodes4_
where
offices1_.office_id=sitecodes4_.office_id
And anyway it also throws this error (not including whole stack trace unless sopmeone wants to see it):
Caused by: org.hibernate.TransientObjectException: object references an
unsaved transient instance - save the transient instance before
flushing: dne.nmst.dac.model.SiteCodes
This confused me because I wasn't doing any saving, just selecting.
How do I get this to work? I want the criteria constructed properly and of course to not have an error occur.
I got this worked out. Hope it helps someone else. Still don't know why I got the error it did, but I figured out the join's and that made the query work correctly
Joins:
Root<Devices> d = cq.from(Devices.class);
Join<Devices, Offices> join1 = d.join(Devices_.office);
ListJoin<Offices,SiteCodes> join2 = join1.join(Offices_.siteCodes);
Parameter expression and Predicate:
ParameterExpression<String> p = cb.parameter(String.class, "sitecode");
Predicate pr = cb.equal(join2.get(SiteCodes_.siteCode),p);
Produced SQL like this:
select
devices0_.dev_i a col_0_0
from
devices devices0
inner join
office offices1
o devices0_.office_id=offices1_.office_i
inner join
site_code_map sitecodes2
o offices1_.office_id=sitecodes2_.office_i
inner join
site_code sitecodes3
o sitecodes2_.site_code_id=sitecodes3_.site_code_i
where
sitecodes3_.site_code=?

org.hibernate.hql.internal.ast.QuerySyntaxException: writes is not mapped

I set up a Database Application with Spring an Hibernate and I'm using a Many-To-Many-Relationship.
Here's the code:
Author.java
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "writes", joinColumns = {#JoinColumn(name = "authorId")}, inverseJoinColumns = {#JoinColumn(name = "publicationId")})
private Set<Publication> publications = new HashSet<Publication>();
Publication.java
#ManyToMany(mappedBy = "publications")
private Set<Author> authors = new HashSet<Author>();
these lines of code generate a connection-table named writes, but when I try to run a query over all Tables int gives me the error, named above.
this is the method, that schould run the query:
#Transactional
public List<Author> findAuthorByLastname(String lastName) {
String hql = "from Author a, Publication p, writes w where a.id = w.authorId and p.id = w.publicationId and a.lastname = :lastName";
Query q = sessionFactory.getCurrentSession().createQuery(hql);
q.setParameter("lastName", lastName);
List<Author> result = q.list();
return result;
}
You don't need to mention connection table explicitly in HQL queries (moreover, you even cannot mention them - only mapped entities). You need to use join on mapped relationships instead.
Also, it's not quite clear what you want to achieve.
If you want to fetch Authors with eagerly filled collections of their Publications in one query, use join fetch:
select distinct(a) from Author a join fetch a.publications where a.lastName = :lastName
If you want to fetch a list of pairs (Author, Publication), use regular join:
select a, p from Author a join a.publications p where a.lastName = :lastName

How to use join to make this queries cleaner?

I have the feeling that using joins could make this cleaner
public override string[] GetRolesForUser(string username)
{
using (TemplateEntities ctx = new TemplateEntities())
{
using (TransactionScope tran = new TransactionScope())
{
int userId = (from u in ctx.Users
where u.UserName == username
select u.UserId).Single();
int[] roleIds = (from ur in ctx.UserInRoles
where ur.UserId == userId
select ur.RoleId).ToArray();
string[] roleNames = (from r in ctx.Roles
where roleIds.Contains(r.RoleId)
select r.RoleName).ToArray();
tran.Complete();
return roleNames;
}
}
}
You should be able to use the navigation properties to follow the relations instead of using the primary keys (Entity Framework will join behind the scenes for you)
If you have (and need) UserInRoles because there are other properties defined on the junction table, you can use:
return (from u in cts.Users
from ur in u.UserInRoles
from r in ur.Roles
select r.roleName).ToArray();
Otherwise make sure the N-M relation is mapped as such, and don't map the junction table. Then you can just use:
return (from u in cts.Users
from r in u.Roles
select r.roleName).ToArray();
I'm not a c# guy, but essentially you would want to do
select u.userId, ur.roleId, r.roleName
from Users u, UserInRoles ur, Roles r
where u.userId = ? and ur.userId = u.userId and r.roleId = ur.roleId;
You can also use the in syntax if you opt for nested queries.
ie: where user_id in (select userId from UserInRoles)

Resources