JPA many to one/one to many query - java

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.

Related

Hibernate annotations: Many-to-many with shared composite key attribute

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.

Hibernate #JoinTable not working properly

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.

Filter Rows in Join Table - Many to Many in Hibernate

I'm trying to implement a soft delete across my entire platform. Each table has a deleted_on column that will be used to filter out deleted rows.
This includes the join table that I have that's between my files and users. I haven't had any luck finding anything that could help. Right now my classes look like this
#Entity
public class File {
#ManyToMany
#JoinTable(
name = "file_user",
joinColumns = #JoinColumn(name = "file_id", referencedColumnName = "file_id"),
inverseJoinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id")
)
private List<User> users;
// Constructor and other fields
}
User class:
#Entity
public class User {
#ManyToMany(mappedBy = "users")
private List<FileData> files = new ArrayList<>();
// Constructor and other fields
}
If it's possible I would like to be able to run the following code without filtering it in the business code
File file = fileRepository.findOne(fileKey);
file.getUsers().remove(user);
fileRepository.save(file);
if you are Hibernate as your JPA provider, then you can use the #Where annotation
#Entity
#Where(clause="deleted_on=1")
public class User {
#ManyToMany(mappedBy = "users")
private List<FileData> files = new ArrayList<>();
// Constructor and other fields
}
this will apply the where clause on all your queries with User Class
Additional Note:
you need to specify the #where clause again in all your relationships
#ManyToMany
#JoinTable(
name = "file_user",
joinColumns = #JoinColumn(name = "file_id", referencedColumnName = "file_id"),
inverseJoinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id")
)
#Where(clause="deleted_on=1")
private List<User> users;

create entity from database wizard jpa 2.1 #ManyToMany, fix list not needed

i have some join tables like this multimedia_feature.
The wizard will create in Multimedia class a List attribute:
...
#JoinTable(name = "multimedia_feature", joinColumns = {
#JoinColumn(name = "feature_oid", referencedColumnName = "oid")}, inverseJoinColumns = {
#JoinColumn(name = "multimedia_oid", referencedColumnName = "oid")})
#ManyToMany
private List<Feature> featureList;
...
since i just need a ManyToOne relationship (one feature can have many multimedia file), i flagged multimedia_oid as UNIQUE.
After this the wizard creates other 2 tables (i think redoundant)
#Entity
#Table(name = "multimedia_feature")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "MultimediaFeature.findAll", query = "SELECT m FROM MultimediaFeature m"),
#NamedQuery(name = "MultimediaFeature.findByMultimediaOid", query = "SELECT m FROM MultimediaFeature m WHERE m.multimediaFeaturePK.multimediaOid = :multimediaOid"),
#NamedQuery(name = "MultimediaFeature.findByFeatureOid", query = "SELECT m FROM MultimediaFeature m WHERE m.multimediaFeaturePK.featureOid = :featureOid")})
public class MultimediaFeature implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected MultimediaFeaturePK multimediaFeaturePK;
#JoinColumn(name = "multimedia_oid", referencedColumnName = "oid", insertable = false, updatable = false)
#OneToOne(optional = false)
private Multimedia multimedia;
#JoinColumn(name = "feature_oid", referencedColumnName = "oid", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Feature feature;
...
...
and
#Embeddable
public class MultimediaFeaturePK implements Serializable {
#Basic(optional = false)
#NotNull
#Column(name = "multimedia_oid")
private int multimediaOid;
#Basic(optional = false)
#NotNull
#Column(name = "feature_oid")
private int featureOid;
...
...
finally it added an attribute in Multimedia class:
....
#OneToOne(cascade = CascadeType.ALL, mappedBy = "multimedia")
private MultimediaFeature multimediaFeature;
....
since I have really many join classes, I would avoid creating all of these classes.
Can i manually create the attributes, such as:
#JoinTable(name = "multimedia_feature",
#JoinColumn(name"feature_oid", referencedColumnName = "oid")
)
#OneToOne(optional = false)
private Feature feature;
or this precludes the correct persistence?
It looks the feature attribute in Multimedia class should be a #ManyToOne relationship.
By default, join tables are created for the mapping of many-to-many relationships and unidirectional one-to-many relationships.
If you want to avoid the join class, I think you can map the multimedia attribute in Feature class by using the #JoinTable like that:
#OneToMany
#JoinTable(name = "multimedia_feature",
joinColumns = #JoinColumn(name = "feature_oid"),
inverseJoinColumns = #JoinColumn(name = "multimedia_oid") )
private List<Multimedia> multimediaList;
If you do need bidirectional relationship with join table, the mapping will be like this:
public class Feature implements Serializable {
#OneToMany(mappedBy="feature")
private List<Multimedia> multimediaList;
...
}
public class Multimedia implements Serializable {
#ManyToOne
#JoinTable(name = "multimedia_feature",
joinColumns = #JoinColumn(name = "multimedia_oid") ,
inverseJoinColumns = #JoinColumn(name = "feature_oid"))
private Feature feature;
...
}
Or you can totally remove the join table with a bidirectional association by introducing a join column in multimedia table, like feture_oid. So that you can easily map the feature attribute in Multimedia class:
#ManyToOne
#JoinColumn(name = "feature_oid")
private Feature feature;
And in the Feature class will be like:
#OneToMany(mappedBy="feature")
private List<Multimedia> multimediaList;

JPA query left join without loading the joined entity field

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

Categories

Resources