I have a scenario like below.
Lets say EntityA has three nested entities EntityB, EntityC, EntityD. And all of EntityB, EntityC, EntityD has several nested entities inside them.
But while selecting for EntityA it selects the whole tree of nested entities. Whereas I want to fetch a specific branch. Lets say only EntityA, EntityB and all sub entities of EntityB are to be fetched leaving EntityC and EntityD back then I am not sure how to do that. As spring jpa brings all the nested objects back to me.
I am using below collection mapping.
#Entity
#Table(name = "customer_party_mapping")
#Data
public class CustomerPartyMappingEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "customer_id")
private Integer custmerId;
#Column(name = "orgtype_id")
private Integer orgTypeId;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL )
#JoinColumn(name = "customer_party_mapping_id")
#Fetch(value = FetchMode.SUBSELECT)
private List<CustomerPartyBookingLocationEntity> customerPartyBookingLocation=new ArrayList<CustomerPartyBookingLocationEntity>();
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL )
#JoinColumn(name = "customer_party_mapping_id")
#Fetch(value = FetchMode.SUBSELECT)
private List<CustomerPartyFieldMappingEntity> customerPartyFieldMappingEntity=new ArrayList<CustomerPartyFieldMappingEntity>();
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL )
#JoinColumn(name = "customer_party_mapping_id",referencedColumnName="id")
#Fetch(value = FetchMode.SUBSELECT)
private List<CustomerPartyOtherDocumentEntity> otherDocumentsList=new
ArrayList<>();
#OneToOne( cascade={ CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "customer_name_screening_id", referencedColumnName="id")
private CustomerNameScreeningEntity customerNameScreeningEntity;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL )
#JoinColumn(name = "customer_party_mapping_id")
#Fetch(value = FetchMode.SUBSELECT)
private List<CustomerDocInfoTrackingEntity> customerDocInfoTrackingList=new
ArrayList<CustomerDocInfoTrackingEntity>();
}
And I am calling
List<CustomerPartyMappingEntity> customerPartyMappingEntityList = customerPartyMappingRepository.findByCustmerId(customerid);
It gets all the nested mapped list of entities wheras I need only CustomerPartyMappingEntity and its list of customerPartyFieldMappingEntity nested object.
Any help will be appreciated.
First use FetchType.LAZY for nested entity.
Then you can use #EntityGraph to fetch nested entity by name and their nested entity using their name with . in the repository. You use to just specify the nested property in attributePaths like
#EntityGraph(attributePaths = {"customerPartyBookingLocation"})
And the nested property of customerPartyBookingLocation like
#EntityGraph(attributePaths = {"customerPartyFieldMappingEntity.subField"})
Example:
#EntityGraph(attributePaths = {"customerPartyBookingLocation", "customerPartyFieldMappingEntity.subField"})
List<CustomerPartyMappingEntity> findByCustmerId(Integer customerid);
Note: You can't use #EntityGraph with #Query annotation
If your entities are really setup correctly, see for instance the subselect example here and remove your EAGER (you are currently instructing hibernate to fetch all these fields upon entity initialization). It should work.
Related
I have the following mapping. It works fine. the problem is that Hibernate created 3 tables for that: request, research and request_researches.
request_researches is not needed.
I want the foreign key to be inside the research table. (research.request_id)
#Entity
public class Request{
#Id
private Long id;
#OneToMany(targetEntity = Research.class,cascade= CascadeType.ALL, fetch = FetchType.EAGER)
private List<ResearchEntity> researches = new ArrayList<>();
//... getters setters
}
Is this possible?
Thanks
Just add a #JoinColumn annotation where you specify the column from the related entity:
#OneToMany(targetEntity = Research.class,cascade= CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "request_id")
private List<ResearchEntity> researches = new ArrayList<>();
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 made some domains below.
#Entity
public class Conference {
...
#OneToMany(
targetEntity = ProgramDate.class,
mappedBy = "conference",
cascade = CascadeType.REMOVE,
fetch = FetchType.EAGER
)
#JsonBackReference
private List<ProgramDate> programDateList;
}
#Entity
public class Program {
...
#ManyToOne
#JoinColumn(name = "program_date_id")
#JsonBackReference
private ProgramDate date;
#ManyToOne
#JoinColumn(name = "room_id")
private Room room;
...
}
#Entity
public class ProgramDate {
...
#OneToMany(
targetEntity = Program.class,
mappedBy = "date",
fetch = FetchType.EAGER
)
#JsonBackReference
private List<Program> programList;
#ManyToOne
#JoinColumn(name = "conference_id")
private Conference conference;
}
#Entity
public class Room {
...
#OneToMany(
targetEntity = Program.class,
mappedBy = "room",
fetch = FetchType.EAGER
)
#JsonBackReference
private List<Program> programList;
}
And I made freemarker like below code.
<#list conference.programDateList as date>
...
</#list>
I meet a problem that is infinite recursion reference with JPA OneToMany, ManyToOne Relationship. I try to add #JsonBackReference, but it only resolved about the json recursive problem.
Use json ignore annotation :
#OneToMany(
targetEntity = Program.class,
mappedBy = "date",
fetch = FetchType.EAGER
)
#JsonIgnore
private List<Program> programList;
Why do you include Room & Programdate in Conference, then in Program add a Conference along a Room & a Programdate which should already be contained in Conference ? Then in ProgramDate you have another reference to... conference and a list of program...
Basically you shouldn't try to "hack" out of these loops with some fancy annotations, but you should work on your data model. While Conference looks ok, Program could be a list of conferences only, and a Programdate should be... a date.
First of all, sorry for my English.
So, I'm working with MS SQL Server with hibernate and i faced with a problem.
I have next mapping of one of the tables in my DB:
#Entity(name = " ... ")
public class Entity extends BaseEntity implements Comparable {
#Id
#Column(name = "...")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "parent_entity_id", insertable = false, updatable = false)
private Integer parentId;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST})
#JoinColumn(name = "parent_entity_id")
private Entity parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = {CascadeType.REMOVE}, orphanRemoval = true)
private Set<Entity> children;
//other fields, setters, getters
}
That means, that my Entity objects can have children, which are also an Entity objects.
So, my problems is that I can't correctly delete parent with all his children. When I try to remove parent, i get an SQL error:
The DELETE statement conflicted with the SAME TABLE REFERENCE
So, any ideas, how to solve this problem?
You have a foreign key defined between parent_entity_id and id. Set it to allow cascading deletes: deleting a parent will delete all it's children, and all their children et cetera.
Be sure you actually want this to happen!
I am working with JPA and use Hibernate as a provider to my SQL Server database.
I need a many-to-many self referencing relation that has an additional column or even more additional columns.
That is my current code. I am getting exceptions by Hibernate:
#Entity
public class Person {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person", fetch = FetchType.EAGER)
private Set<Relation> relations;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "relPerson", fetch = FetchType.EAGER)
private Set<Relation> inverseRelations;
}
#Entity
public class Relation implements Serializable {
#Id
#ManyToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.EAGER)
#PrimaryKeyJoinColumn(name = "PersonID", referencedColumnName = "id")
private Person person;
#Id
#ManyToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.EAGER)
#PrimaryKeyJoinColumn(name = "RelPersonId", referencedColumnName = "id")
private Person relPerson;
}
During runtime i get an exception from hibernate:
org.hibernate.TransientObjectException: object references an unsaved transient instance
Is there any way to implement this a little bit more intelligent and nicely?? Without getting that exception.
Thanks,
ihrigb
If an object not associated with a Hibernate Session, the object will be Transient.
An instance of Relation list may be Transient(Normally, There is no identifier value for that instance) when you save Person.
Here is better way to understand object state.