I'm having this weird problem going on with my application when it comes to #JoinTable use.
I have 3 entities: EntityA, EntityB and EntityC.
EntityA.class
#OneToMany(fetch = FetchType.LAZY, mappedBy = "entityA")
public Set<EntityB> getEntityBSet() {
return entityBSet;
}
EntityB.class
#ManyToOne
#JoinColumn(name = "ID_ENTITY_A", nullable = false)
public EntityA getEntityA() {
return entityA;
}
#ManyToOne
#JoinTable(name = "B_AND_C",
joinColumns = { #JoinColumn(name = "ID_ENTITY_B") },
inverseJoinColumns = {#JoinColumn(name = "ID_ENTITY_C") })
public EntityC getEntityC() {
return entityC;
}
EntityC.class
#OneToMany(cascade = CascadeType.ALL, mappedBy="entityC")
public Set<EntityB> getEntityBSet() {
return entityBSet;
}
Everything works fine until I try to access entityB through entityA.getEntityBSet().
Hibernate doesnt generate the proper SQL for the relationship entityB.entityC, creating a Cartesian product:
SELECT ...
FROM ENTITY_B, B_C, ENTITY_C
WHERE B_C.ID_ENTITY_C = ENTITY_C.ID_ENTITY_C (+)
AND ENTITY_B.ID_ENTITY_A = ?
There should be the join between ENTITY_B and B_C:
AND ENTITY_B.ID_ENTITY_B = B_C.ID_ENTITY_B
On the other hand, when loading entityB through session.get(), the generated SQL is correct for that relationship (entityB.entityC).
Any help appreciated.
Related
I'm trying to map up an existing database schema using Hibernate+JPA annotations.
One of my entities are mapped like this:
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
private int department;
#Id
private int userId;
...
And another entity, Group:
#Entity
#Table(name = "groups")
public class Group implements Serializable {
#Id
private int department;
#Id
private int groupId;
...
Group and User should have a many-to-many relationship between them, but the issue is that the join table ("user_group") only has columns "DEPARTMENT, USERID, GROUPID" - i.e. the DEPARTMENT column needs to be used in both joinColumns and inverseJoinColumns:
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(
name = "user_groups",
joinColumns = { #JoinColumn(name = "department"), #JoinColumn(name = "groupid") },
inverseJoinColumns = {#JoinColumn(name = "department"), #JoinColumn(name = "userid") }
)
private List<User> groupUsers = new ArrayList<>();
which gives a mapping error - "Repeated column in mapping for entity".
However, it looks like this was/is possible using XML, because this exact example exists in the old Hibernate documentation. But I cannot find any evidence that this ever worked using annotations? I tried with #JoinFormula instead of #JoinColumn, but that does not compile. Is it possible?
Okay, I'm pretty sure it's not possible.
I found a promising workaround:
Create an #Embeddable for the "user_group" table:
#Embeddable
public class UserGroupMembership implements Serializable {
#ManyToOne
#JoinColumnsOrFormulas(
value = {
#JoinColumnOrFormula(column = #JoinColumn(referencedColumnName = "userid", name = "userid")),
#JoinColumnOrFormula(formula = #JoinFormula(referencedColumnName = "department", value = "department"))
})
private User user;
public UserGroupMembership(User user) {
this.user = user;
}
public UserGroupMembership() {
}
public User getUser() {
return user;
}
}
The trick is that #ManyToOne allows you to use #JoinColumnsOrFormulas, so one of the join conditions can be a formula, which I doesn't seem to work for #ManyToMany (the #JoinColumnsOrFormulas annotation is ignored as it expects the join columns to be part of the #JoinTable annotation).
The UserGroupMemberships are then mapped as a ElementCollection:
#ElementCollection
#CollectionTable(name = "user_group", joinColumns = {
#JoinColumn(name = "department", referencedColumnName = "department"),
#JoinColumn(name = "groupid", referencedColumnName = "groupid")
})
#OrderColumn(name = "seq", nullable = false)
private List<UserGroupMemberships> groupUsers = new ArrayList<>();
This only works right now for a unidirectional many-to-many relationship.
I have 3 JPA entities such as:
#Entity
public class Link implements {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "network", referencedColumnName = "id")
private Network network;
//...
}
#Entity
public class Network implements LinkOwner {
#OneToMany(mappedBy = "network")
#Cascade(value = { CascadeType.ALL })
private Set<Link> links;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "project", referencedColumnName = "id", nullable = false)
private Project Network.project;
//...
}
#Entity
public class Project {
#OneToMany(mappedBy = "project", orphanRemoval = true)
#Cascade(value = { CascadeType.ALL })
#Fetch(FetchMode.SELECT)
private Set<Network> networks;
}
And I do a JPA query such as:
SELECT l FROM Link l left join fetch l.subnetwork sann
where sann.project.id = :projectId
and it generates a SQL query similar to:
select * from RMT6.link, SUBNETWORK where link.subnetwork = SUBNETWORK.id
and SUBNETWORK.project=?
How can I trigger a JPQL query that selects only the fields of the first entity and exclude those of the second one?
What do I need to change in my JPQL query?
Base on your entity relationship, you don't need to use JOIN query, I think.
SELECT * FROM LINK l WHERE l.network.project.id = :projectId
I have a many-to-many relationship defined on hibernate like this:
User
public class User{
private List<UserCustomer> userCustomerList;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "id.user", cascade=CascadeType.ALL)
public List<UserCustomer> getUserCustomerList() {
return userCustomerList;
}
}
UserCustomer
#Entity
#Table(name = "RTDB_USER_CUSTOMER")
#Component("userCustomerEntity")
#AssociationOverrides({
#AssociationOverride(name = "id.user",
joinColumns = #JoinColumn(name = "ID_USER")),
#AssociationOverride(name = "id.customer",
joinColumns = #JoinColumn(name = "ID_CUSTOMER")) })
public class UserCustomer {
#EmbeddedId
public UserCustomerId getId() {
return id;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumns({ #JoinColumn(name = "ID_ROLE_CUSTOMER", referencedColumnName = "ID") }) public RoleCustomer getRoleCustomer() {
return roleCustomer;
}
}
So a user has a list of UserCustomer, that represent roles of users over customers. The problem is, that when we change a role over a customer and call update(), instead of one row updated we get all the rows updated with the same role. When we call merge() it starts to perform a lots of queries and then gives stackoverflow exception ¿Could this be a mapping problem?
Can you post the tables and updation code?
I think you are updating the role directly from UserCustomer which should be updating all the roles, as far as my understanding goes you do not want to update UserCustomer but only RoleCustomer. Try to fetch RoleCustomer and update it not the UserCustomer.
I have quite big problem with hibernate annotation mapping. Heres my diagram:
As for mapping object, few are simple cause its 1...N for:
TASK_DEF -> REPORT_DATA
REPORT_DATA -> REPORT_DATA_COLUMN
REPORT_DATA -> REPORT_DATA_VALUE
right?
Now problem is when I'm trying to map Collection of REPORT_DATA_VALUE to REPORT_DATA_COLUMN. I've tried this way:
#OneToMany(fetch = FetchType.LAZY)
#ForeignKey(name = "FK_REPORT_DATA_VALUE_REPORT_DA", inverseName = "PK_REPORT_DATA_COLUMN")
#JoinTable(name = "REPORT_DATA_VALUE", joinColumns = {
#JoinColumn(name = "REPORT_DATA_ID"),
#JoinColumn(name = "COLUMN_NM")
}, inverseJoinColumns = {
#JoinColumn(name = "REPORT_DATA_ID"),
#JoinColumn(name = "COLUMN_NM")
})
List<ReportDataValue> reportDataValueList;
But hibernate selects wrong identifies, therefore couldnt execute query. Could someone help me with this?
There is a problem in your code. Try this instead:
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "REPORT_DATA_VALUE", joinColumns = {
#JoinColumn(name = "REPORT_DATA_ID"),
}, inverseJoinColumns = {
#JoinColumn(name = "COLUMN_NM")
})
List<ReportDataValue> reportDataValueList;
By the way, If I were you, I wouldn't create a table for One To Many. I'd do this:
public class A{
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="B_ID")
B b;
}
public class B{
#OneToMany(mappedBy="b",fetch=FetchType.EAGER)
Set<A> as;
}
I would like to create JPA query based on this tables
**category**
(pk)CategoryID int (10)
category VARCHAR (45)
**templatecat**
(pk/fk)templateId int(10)
(pk/fk)categoryId int (10)
**template**
(pk)templateId int (10)
template madiumtext
I also have a "tempaltecat" table that holds foreign keys for category and template table.I`d like to create query that finds all templates in template table based on category and vice versa.
Here is my table mapping
#Entity
#Table(name = "category")
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "templatecat", joinColumns = { #JoinColumn(name = "categoryId", unique = true) }, inverseJoinColumns = { #JoinColumn(name = "templateId") })
private Set<Template> template;
#Entity
#Table(name = "template")
#ManyToOne(optional = true)
#JoinTable(name = "templatecat", joinColumns = { #JoinColumn(name = "templateId") }, inverseJoinColumns = { #JoinColumn(name = "categoryId") })
private Category category;
Thanks in advance
It looks like a #ManyToMany relationship,
Instead of using #OneToMany and #ManyToMany, you can use the following configuration:
In Category class:
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "templatecat", joinColumns = { #JoinColumn(name = "categoryId", unique = true) }, inverseJoinColumns = { #JoinColumn(name = "templateId") })
private Set<Template> templates;
In Template class:
#Entity
#Table(name = "template")
#ManyMany(optional = true, mappedBy="templates");
private Set<Category> categories;
If you want to see all Templates of a given Category, the query would be:
select o.templates from Category o where o.id = ?
The reverse works as well (all Categories from a Template)
select o.categories from Template o where o.id = ?
Hope it has helped you.