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
Related
I am using soft delete in my project. I need to unload a lot of related entities. I am using #EntityGraph with multiple entities at the same time - entityA.EntityB.EntityC, this does not work on related entities.
Each entity has the #Where annotation. I've tried using #Where and #WhereJoinColumn entities on the field. It gave no results
#Entity
#Where(clause = "field is null")
public class EntityA {
#OneToOne(mappedBy = "entityA", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
#PrimaryKeyJoinColumn
private EntityB entityB;
#Entity
#Where(clause = "field is null")
public class EntityB {
#MapsId
#OneToOne(fetch = FetchType.LAZY)
private EntityA entityA;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id", referencedColumnName = "id", nullable = false)
private EntityC entityC;
#Entity
#Where(clause = "field is null")
public class EntityC {
#OneToMany(mappedBy = "EntityB")
#Audited(targetAuditMode = NOT_AUDITED)
private Set<EntityB> set = new HashSet<>();
public interface EntityARepository extends JpaRepository<EntityA, Integer> {
#EntityGraph(attributePaths = {"entityB.entityC"})
Optional<EntityA> findById(Integer id);
I want Hibernate to return me a query like
SELECT * FROM EntityA
LEFT JOIN EntityB ON EntityA.id = EntityB.id
AND EntityB.field is null
LEFT JOIN EntityC ON EntityB.id = EntityC.id
AND EntityC.field is null
WHERE EntityA.id = ? AND EntityA.field is null
But it comes back to me
SELECT * FROM EntityA
LEFT JOIN EntityB ON EntityA.id = EntityB.id
LEFT JOIN EntityC ON EntityB.id = EntityC.id
AND EntityC.field is null
WHERE EntityA.id = ? AND EntityA.field is null
How do I get Hibernate to see all my #Where annotations when unloading many related entities. I wrote a query in hql, but it is too cumbersome.
Thanks
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 want to use Hibernate annotations to represent a unidirectional one-to-many relationship using a join. I want an added condition on the join so it only happens when a column in the source table (the "one") is equal to a constant value. For example.
SELECT *
FROM buildings b
LEFT JOIN building_floors bf on bf.building_id = b.id AND b.type = 'OFFICE'
I want to represent the b.type = 'OFFICE' part of that query.
My question is quite similar to this one, except I have a condition on the source table. JPA/Hibernate Join On Constant Value
The Java entities look like this:
#Entity
#Table(name = "buildings")
public class Building {
#Id
#Column(name = "id")
private int id;
#Column(name = "type")
private String type;
#OneToMany(mappedBy = "buildingId",
fetch = FetchType.EAGER,
cascade = {CascadeType.ALL},
orphanRemoval = true)
#Fetch(FetchMode.JOIN)
// buildings.type = 'OFFICE' ????
private Set<BuildingFloors> buildingFloors;
// getters/setters
}
#Entity
#Table(name = "building_floors")
public class BuildingFloor {
#Id
#Column(name = "building_id")
private int buildingId;
#Id
#Column(name = "floor_id")
private int floorId;
#Column(name = "description")
private String description;
// getters/setters
}
I've tried a few things where I have that placeholder comment:
#Where annotation
This doesn't work since that applies to the target entity.
#JoinColumns annotation
#JoinColumns({
#JoinColumn(name = "building_id", referencedColumnName = "id"),
#JoinColumn(name = "'OFFICE'", referencedColumnName = "type")
})
This doesn't work because I get the following error (simplified for clarity): Syntax error in SQL statement "SELECT * FROM buildings b JOIN building_floors bf on bf.building_id = b.id AND bf.'OFFICE' = b.type"
A different #JoinColumns annotation
#JoinColumns({
#JoinColumn(name = "building_id", referencedColumnName = "id"),
#JoinColumn(name = "buildings.type", referencedColumnName = "'OFFICE'")
})
This doesn't work because when using a unidirectional OneToMany relationship, the referencedColumnName is from the source table. So I get the error: org.hibernate.MappingException: Unable to find column with logical name: 'OFFICE' in buildings
Thanks in advance!
Why not use inheritance ? (I use it with JPA, I never use hibernate directly)
#Entity
#Inheritance
#Table(name = "buildings")
#DiscriminatorColumn(name="type")
public class Building {
#Id
#Column(name = "id")
private int id;
#Column(name = "type")
private String type;
}
And :
#Entity
#DiscriminatorValue("OFFICE")
public class Office extends Building {
#OneToMany(mappedBy = "buildingId",
fetch = FetchType.EAGER,
cascade = {CascadeType.ALL},
orphanRemoval = true)
private Set<BuildingFloors> buildingFloors;
}
Create database View with the following select:
SELECT bf.* FROM building_floors bf JOIN buildings b on bf.building_id = b.id AND b.type = 'OFFICE'
Map it to a class OfficeBuildingFloors as an ordinary entity and then use #OneToMany for it in Building class.
Of course, you won't be able to modify such collection and to avoid any exception you can use #Immutable on OfficeBuildingFloors.
In my opinion you should create a specific query to achieve your goals, rather than put specific annotations with constant parameter. I'm not see you mention another frameworks besides Hibernate so I would give some example with Hibernate. In your Building class your unidirectional mappings look like this:
#OneToMany(fetch = FetchType.Lazy, cascade = {CascadeType.ALL}, orphanRemoval = true)
#JoinTable(name = "building_floors", joinColumns = #JoinColumn(name = "id"), inverseJoinColumns = #JoinColumn(name = "building_id")
private Set<BuildingFloor> buildingFloors;
Then you can fetch your data using TypedQuery like this.
TypedQuery<Customer> query = getEntityManager().createNamedQuery("select b from building b inner join fetch b.buildingFloors where b.type = 'OFFICE'", Building.class);
List<Building> result = query.getResultList();
My solutions is not Hibernate specific, actually you could perform this with simple JPA. Hope this can help you to achieve your goals.
As you want filter source table you could use #Loader annotation
#Entity
#Table(name = "buildings")
#Loader(namedQuery = "building")
#NamedNativeQuery(name="building",
query="SELECT * FROM buildings b"
+ " LEFT JOIN building_floors bf on bf.building_id = b.id"
+ " WHERE b.type = 'OFFICE' AND b.id = ?",
resultClass = Building.class)
class Building
Approach with view in DB would be better and more clearly, if it could be used inside DB also. Otherwise rename Building to something which explicitly represent filtering.
Another approaches to mention: #Filter, #FilterDef.
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.
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.