I'm trying to add #WhereJoinTable to a property, unfortunately the field is a foreign key, and I need to look up by a property on the other table (called 'type').
I have the following tables:
business:
id
name
...
appuser:
id
email
...
role:
id
type
user_busines_role_permission:
id
appuser_id
business_id
role_id
I can do the following and it works:
#OneToMany()
#JoinTable(name = "user_business_role_permission",
joinColumns = #JoinColumn(name = "business_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "appuser_id", referencedColumnName = "id")
)
#WhereJoinTable(clause = "role_id ='3'")
private Set<AppUser> managedBy;
But I need something like:
#WhereJoinTable(clause = "role.type ='BUSINESS_MANAGER'")
or a nested select so I can select the role id and return it, is this doable?
Related
class Employee {
#EmbeddedId
private EmployeeId id; // composite key with two fileds companyId and employeeNumber
#OneToMany(mappedBy = "employee", cascade = CascadeType.ALL)
private List<Phone> phoneList;
}
class Phone {
#EmbeddedId
private PhoneId phoneId; // composite key with three fileds companyId , employeeNumber and phoneNumber
#ManyToOne
#JoinColumns({
#JoinColumn(name = "COMPANY_ID", referencedColumnName = "COMPANY_ID", insertable = false,
updatable = false),
#JoinColumn(name = "EMPLOYEE_NUMBER_NEW", referencedColumnName = "EMPLOYEE_NUMBER",
insertable = false, updatable = false)})
private Employee employee;
}
In above example, Employee table gets generated with column employeeId, employeeNumber. And Phone table gets generated with employeeId, employeeNumber, phoneNumber, employee_nuber_new.
When I am creating employee object and phone object, at the end employeeId and employeeNumber values in Phone table gets populated from Employee object ( as part of FK ) but employee_number_new column still have "null" value. I want to updated employee_number_new column with employeeNumber from Employee table.
Tried lot of ways including JoinFormula and all but no success. Can anybody throw some light on this ?
Thanks...
The reason you are getting a null value in the Phone table because you are using insertable = false in your JoinColumn definition. If you could set this to true then your problem would be solved.
Hibernate doesn't support the mixing of insertable and non-insertable columns. So you have to make companyId insertable too. But that leads to the possibility of your primary key being changed in the Phone table which you don't want.
So, we are left with two options, if the EMPLOYEE_NUMBER is the same in both tables don't create the new column EMPLOYEE_NUMBER_NEW column and use EMPLOYEE_NUMBER.
But if EMPLOYEE_NUMBER is not the same in both tables then and you need to save the employee number from employee object, create another insertable column it can be COMPANY_ID_EMP and make updateable false. That way when you save a phone object both EMPLOYEE_NUMBER_NEW and COMPANY_ID_EMP will be saved. But in practice COMPANY_ID_EMP and COMPANY_ID will be the same value.
The following code worked for me
#ManyToOne
#JoinColumns({
#JoinColumn(name = "companyIdEmp", referencedColumnName = "companyId", updatable = false),
#JoinColumn(name = "employeeNumberNew", referencedColumnName = "employeeNumber", updatable = false)})
private Employee employee;
I'm trying to write a JPQL to fetch Users with some Roles by filtering by Role.Name.
public class User
#ManyToMany
#JoinTable(
name = "UsersRoles",
joinColumns = {#JoinColumn(name = "UsersId", referencedColumnName = "UsersId")},
inverseJoinColumns = {#JoinColumn(name = "RolesName", referencedColumnName = "Name")})
#BatchSize(size = 20)
private Set<Role> roles = new HashSet<>();
}
public class Role {
#Id
#Column(name = "Name", length = 50)
private String name;
}
I tried the following on #Query in the jpa repository
SELECT U FROM User U INNER JOIN Role R WHERE R.name = ?1
But that is not working and it generates sql like the following.
select
...
from Users user0_
inner join Roles role1_ on
where role1_.Name = ?
The joining condition is not added.
As the title indicates I'm trying to select both username and role name by using the following query.
select u.username, r.name
from users u, role r
inner join users_roles ur
on ur.user_id = u.id
where username = ?;
However I'm getting the below error
[2017-04-05 21:34:49] [42P01] ERROR: invalid reference to FROM-clause entry for table "u"
[2017-04-05 21:34:49] Hint: There is an entry for table "u", but it cannot be referenced from this part of the query.
[2017-04-05 21:34:49] Position: 79
My user entity is as follows
#Entity(name = "users") // Postgres doesn't like the table name "user"
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String username;
...
#ManyToMany
#JoinTable(
name = "users_roles",
joinColumns = #JoinColumn(
name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(
name = "role_id", referencedColumnName = "id"))
private Collection<Role> roles;
...
And my role entity is as follows
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#ManyToMany(mappedBy = "roles")
private Collection<User> users;
...
Any clues about what I'm doing wrong?
SELECT users.username, role.name
FROM users
LEFT OUTER JOIN users_roles
ON users.id = users_roles.user_id
LEFT OUTER JOIN role
ON users_roles.role_id = role.id
WHERE username = ?
How do I create an efficient JPA Criteria query to select a list of entities only if they exist in a join table? For example take the following three tables:
create table user (user_id int, lastname varchar(64));
create table workgroup (workgroup_id int, name varchar(64));
create table user_workgroup (user_id int, workgroup_id int); -- Join Table
The query in question (what I want JPA to produce) is:
select * from user where user_id in (select user_id from user_workgroup where workgroup_id = ?);
The following Criteria query will produce a similar result, but with two joins:
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
cq.select(root);
Subquery<Long> subquery = cq.subquery(Long.class);
Root<User> subroot = subquery.from(User.class);
subquery.select(subroot.<Long>get("userId"));
Join<User, Workgroup> workgroupList = subroot.join("workgroupList");
subquery.where(cb.equal(workgroupList.get("workgroupId"), ?));
cq.where(cb.in(root.get("userId")).value(subquery));
getEntityManager().createQuery(cq).getResultList();
The fundamental problem seems to be that I'm using the #JoinTable annotation for the USER_WORKGROUP join table instead of a separate #Entity for the join table so it doesn't seem I can use USER_WORKGROUP as a Root in a criteria query.
Here are the entity classes:
#Entity
public class User {
#Id
#Column(name = "USER_ID")
private Long userId;
#Column(name = "LASTNAME")
private String lastname;
#ManyToMany(mappedBy = "userList")
private List<Workgroup> workgroupList;
}
#Entity
public class Workgroup {
#Id
#Column(name = "WORKGROUP_ID")
private Long workgroupId;
#Column(name = "NAME")
private String name;
#JoinTable(name = "USER_WORKGROUP", joinColumns = {
#JoinColumn(name = "WORKGROUP_ID", referencedColumnName = "WORKGROUP_ID", nullable = false)}, inverseJoinColumns = {
#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID", nullable = false)})
#ManyToMany
private List<User> userList;
}
As far as I know, JPA essentially ignores the join table. The JPQL that you do would be
select distinct u from user u join u.workgroupList wg where wg.name = :wgName
for the Criteria query, you should be able to do:
Criteria c = session.createCriteria(User.class, "u");
c.createAlias("u.workgroupList", "wg");
c.add(Restrictions.eq("wg.name", groupName));
c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
there's no need to worry about the middle join table.
I generated entities using NetBeans (EclipseLink JPA) based on the following diagram:
The relevant generated code is:
Course
#JoinTable(name = "course_software", joinColumns = {
#JoinColumn(name = "course_id", referencedColumnName = "id", nullable = false)}, inverseJoinColumns = {
#JoinColumn(name = "software_id", referencedColumnName = "id", nullable = false)})
#ManyToMany
private List<Software> softwareList;
Software
#ManyToMany(mappedBy = "softwareList")
private List<Course> courseList;
The problem is with Course entity. When I try to persist it as follows
getFacade().create(currentCourse);
currentCourse.setSoftwareList(selectedSoftware);
then a record is created in course, but nothing is created in course_software join table.
How is this caused and how can I solve it?
It seems you don't need to specify the referencedColumnName attributes of #JoinColumn annotations as long as the columns referenced in the joined table are already primary keys.