Select values from related entities by related entity id - spring

I am trying to select child element (many to one) modsVersionses of parent element Mods with Hibernate 5. In the same time, modsVersionses should be filtered by modsVersionses.id.
I've tried something like this:
Session s = sessionFactory.getCurrentSession();
CriteriaBuilder criteriaBuilder = s.getEntityManagerFactory().getCriteriaBuilder();
CriteriaQuery<Mods> criteriaQuery = criteriaBuilder.createQuery(Mods.class);
Root<Mods> root = criteriaQuery.from(Mods.class);
criteriaQuery.select(root.get("modsVersionses"));
criteriaQuery
.where(criteriaBuilder.equal(root.get("id"), id)).where(criteriaBuilder.equal(root.get("modsVersionses").get("id"), versionId));
List<Mods> resultList = s.createQuery(criteriaQuery).getResultList();
Mods mod = resultList.get(0);
But I got IllegalStateException on code .where(criteriaBuilder.equal(root.get("modsVersionses").get("id"), versionId)) .
How to correctly filter child elements by id?
Thank you!
Update 1: With #JB Nizet help I made changes and my code now is:
Session s = sessionFactory.getCurrentSession();
CriteriaBuilder cb = s.getEntityManagerFactory().getCriteriaBuilder();
CriteriaQuery<Mod> cq = cb.createQuery(Mod.class);
SetJoin<Mod, ModVersion> modsVersionsesNode = cq.from(Mod.class).join(Mod_.modsVersionses);
cq.where(
cb.equal(
modsVersionsesNode.get(ModVersion_.id), versionId
)
);
List<Mod> mods = s.createQuery(cq).getResultList();
But I still can't understand, how to select not Mod object, but to select only ModVersion?

Solved, thanks to #JB Nizet :
Session s = sessionFactory.getCurrentSession();
CriteriaBuilder cb = s.getEntityManagerFactory().getCriteriaBuilder();
CriteriaQuery<ModVersion> cq = cb.createQuery(ModVersion.class);
Root<Mod> root = cq.from(Mod.class);
SetJoin<Mod, ModVersion> join = root.join(Mod_.modsVersionses);
cq.select(
join
);
cq.where(
cb.equal(
join.get(ModVersion_.id), versionId
)
);
List<ModVersion> mods = s.createQuery(cq).getResultList();

Related

Get last record with criteria api

everyone.
My task is to get the last added record from my database. Since the last of the records in the database has the maximum id, I wrote a method like this, but it always returns null to me.
#Override
public Address getLast() {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Address> criteriaQuery = criteriaBuilder.createQuery(Address.class);
Root<Address> root = criteriaQuery.from(Address.class);
criteriaQuery
.select(root)
.orderBy(criteriaBuilder.desc(root.get(Address_.id)));
TypedQuery<Address> findAllSizes = entityManager.createQuery(criteriaQuery);
return findAllSizes.getResultStream().findFirst().orElse(null);
}
All works fine. All I needed to add was add #Transactional on the method which calls the specified method

Hibernate CriteriaQuery where - ManyToOne field

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)

Avoid duplicate code in criteria builder query in spring data jpa

I am using Spring Boot (1.5.14.RELEASE) and Spring data Jpa with Java 1.8. I want to avoid duplicate code.
Below query fetches employee details. It's working fine.
Class EmployeeDAO:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<EmployeeDto> cq = cb.createQuery(EmployeeDto.class);
Root<EmployeeInfo> root = cq.from(EmployeeInfo.class);
Join<EmployeeInfo, SalaryInfo> SalaryType = root.join("SalaryInfo");
Join<EmployeeInfo, CompanyInfo> Company = root.join("CompanyInfo");
cq.select(cb.construct(EmployeeDto.class,
root.get("FirstName"),
SalaryType.get("Salary"),
Company.get("CompanyName")))
.where(specification.toPredicate(root, cq, cb))
.orderBy(cb.asc(root.get("FirstName")));
Another function in same class in also making the almost 90% same criteria builder query as shown below:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<EmployeeDto> cq = cb.createQuery(EmployeeDto.class);
Root<EmployeeInfo> root = cq.from(EmployeeInfo.class);
Join<EmployeeInfo, SalaryInfo> SalaryType = root.join("SalaryInfo");
Join<EmployeeInfo, CompanyInfo> Company = root.join("CompanyInfo");
Join<EmployeeInfo, UserInfo> User = root.join("UserInfo");
cq.select(cb.construct(EmployeeDto.class,
root.get("FirstName"),
SalaryType.get("Salary"),
Company.get("CompanyName"),
User.get("Age")))
.where(specification.toPredicate(root, cq, cb))
.orderBy(cb.asc(root.get("FirstName")));
The code in both function is same except that below code is making join with UserInfo table to get user age. All other code is duplicate. Can you tell me how can I avoid this duplicate code.
Something like this:
public class EmployeeDAO {
EntityManager em;
Specification specification;
public void get(boolean withUsers) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<EmployeeDto> cq = cb.createQuery(EmployeeDto.class);
Root<EmployeeInfo> root = cq.from(EmployeeInfo.class);
Join<EmployeeInfo, SalaryInfo> salaryType = root.join("SalaryInfo");
Join<EmployeeInfo, CompanyInfo> company = root.join("CompanyInfo");
List<Selection> sels = new ArrayList<>();
Collections.addAll(sels,
root.get("FirstName"),
salaryType.get("Salary"),
company.get("CompanyName")
);
if (withUsers) {
Join<EmployeeInfo, UserInfo> user = root.join("UserInfo");
sels.add(user.get("Age"));
}
cq.select(cb.construct(EmployeeDto.class,
sels.toArray(new Selection[0])
))
.where(specification.toPredicate(root, cq, cb))
.orderBy(cb.asc(root.get("FirstName")));
}
}

Linq in C# using IEnumerable

Appearently, I got an error if using the following code. It said:
Cannot implicity converrt type System.Linq.IQueryable<AnonymousType> to System.Collection.Generic.IEnumerable.
Please advise how I can fix this?
public IEnumerable<Session> GetAllListDetailConsumer(string refId)
{
ObjectQuery<Session> sessions = db.Sessions;
ObjectQuery<SessionsList> sessionsLists = db.SessionsList;
var query =
from s in sessions
join sList in sessionsLists on s.ReferralListID equals sList.ReferralListID
where s.ReferralListID == new Guid(refId)
select new SessionConsumerList
{
ReferralListID = s.ReferralListID,
SessionServerId = s.SessionServerID,
ApplicationID = s.ApplicationID,
// ...
ConsumerID = sList.ConsumerID,
ConsumerFirstName = sList.ConsumerFirstName,
ConsumerFamilyName = sList.ConsumerFamilyName,
// ...
};
return query.ToList();
}
You are selecting using select new, which would create an anonymous type, you need to project to class Session in your query like.
select new Session
{
....
But remember if your Session class is a representing a table in your database/data context, then you can't project to that class, instead you may have to create a temporary class and project the selection to that class.
EDIT (Since the question now has been edited)
Now you are selecting new SessionConsumerList and you are returning IEnumerable<Session>, you need to modify method signature to return IEnumerable<SessionConsumerList>
Why not separate the creation of the SessionConsumerList in another method? Makes the code a lot cleaner. Like this:
public static SessionConsumerList CreateSessionConsumerList(
Session s,
SessionsList sList)
{
return new SessionConsumerList
{
ReferralListID = s.ReferralListID,
SessionServerId = s.SessionServerID,
ApplicationID = s.ApplicationID,
// ...
ConsumerID = sList.ConsumerID,
ConsumerFirstName = sList.ConsumerFirstName,
ConsumerFamilyName = sList.ConsumerFamilyName,
// ...
};
}
And then:
var query =
from s in sessions
join sList in sessionsLists on s.ReferralListID equals sList.ReferralListID
where s.ReferralListID == new Guid(refId)
select CreateSessionConsumerList(s, sList);

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