In my use-case, I would like to #Embedded a class C in an entity.
Another entity refers to C with #OneToMany association and therefore C is annotated with #Entity.
I am aware that this seems like bad design, yet I believe that it makes perfect sense in my case.
Is it possible to force Hibernate to embed an Entity? If I try it, Hibernate complains about a missing setter for the id property of C.
I think the problem comes from this:
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
Why not just create the entity that you want, and in that entity, embed C as well. That way you have C in both classes, one as embedded and another as embedded of the new entity.
#Embeddable
public class Contact {
private String firstname;
private String lastname;
// getters and setters removed.
}
and here is your embedding class:
#Entity
public class Student {
#Embedded
private Contact contact;
}
and here is the new entity that embeds contact also
#Entity
public class FirmContact {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int contactId;
#Embedded
private Contact contact;
}
And finally the class that insists the contact must be an entity:
#Entity
public class Business {
#OneToOne(cascade=CascadeType.ALL)
private FirmContact contacts;
}
It'll just be a couple of extra steps in java to populate the object, but it should do the mapping you want. I hope this helps.
Hibernate doesn't allow you to treat an Embeddable as an Entity or to embed an Entity. According to Hibernate types:
an Embeddable, doesn't have an identifier, since it's state is part of an owning Entity.
an Entity cannot be embedded, because each Entity has a distinct life-cycle.
Since another class already has a #OneToMany association to class C, it's obvious you cannot turn it into an Embeddable.
More, a bidirectional #OneToMany association will perform better than an embeddable collection.
What you can do, is to use it as a #OneToOne association in the entity where you wanted to embed the C entity. You can make that target entity be the owning side of the association so that the C association is bound to the target entity life-cycle.
Related
I'm trying to model a business entity, where said business can have several parent businesses and several child businesses. I'm not sure which relationships are suited, or even what mapping is appropriate. I'm familiar with SQL but new to ORM in Java.
My thinking is that a business can have many or none children, and a business can have many or none parents. Therefore I've tried setting both as OneToMany but also as OneToMany, both resulting in this error: Illegal use of mappedBy on both sides of the relationship.
The implementation:
#Entity
public class Business{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "parentOrgs")
private Collection<Business> chlidOrgs;
#OneToMany(mappedBy = "chlidOrgs")
private Collection<Business> parentOrgs;
// --- Getters and setters below ---
What am I not understanding here? Any and all help much appreciated.
Your current mapping is syntactically incorrect, because only one side of the relationship can be owning side. Owning side is the field defined by value of mappedBy attribute. More detailed explanation can be found from here.
Also removing mappedBy from the one side does not solve the problem, because counterpart of OneToMany must be ManyToOne. Removing it from the both sides leaves us with two unirectional associations, which is also not what is needed.
Because each Business can have multiple parents and it seems to be preferred to be able to navigate directly to the childrens as well, solution is to use bidirectional ManyToMany:
#Entity
public class Business {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(mappedBy = "parents")
private Collection<Business> childrens;
#ManyToMany
private Collection<Business> parents;
}
From database point of view this means following tables:
Business(id)
Business_Business(childrens_id, parents_id)
When necessary, name of the join table and columns can be controlled via JoinTable.
I have Spring Boot project with DB. I've already get table, let's call it "People". I need to add a second table "People_Strings" with two columns: People_id and String. I need to include many strings for every row from People.
How can I map it in my People entity in project?
Edit: I need to do this without creating separete class for String or for People_Strings
If you only need that, you can add the following property to the People entity class:
#ElementCollection
public List<String> strings;
What you need is #OneToMany relation between people and strings. Something like the following will work for you.
#Entity
#Table(name = "People")
public class People{
#Id
#GeneratedValue
private Long id;
#OneToMany(fetch= FetchType.EAGER, cascade=CascadeType.ALL, mappedBy = "peopleId")
private List<PeopleStrings> PeopleStrings;
#Entity
#Table(name = "People_Strings")
public class PeopleStrings{
#Id
#GeneratedValue
private Long peopleId;
#ManyToOne
#JoinColumn(name="peopleId")
private String string;
You'll want a #OneToMany relationship on the Person object (please don't use plurals, eg People). And possibly a #ManyToOne relationship on the PersonString object (again no plurals)
https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/OneToMany.html
https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/ManyToOne.html
This is kinda basic question and I suggest you to read Hibernate "Get Started" first. (one-to-many / many-to-one relations especially)
I have two entities
#Entity
#Table(name="parent")
public class Parent {
#Id
String uuid;
#ElementCollection(fetch=FetchType.EAGER)
#CollectionTable(
name="child",
joinColumns=#JoinColumn(name="parent_uuid", insertable=false, updatable=false)
)
#Column(name="uuid")
private Set<String> childrenUuids = new HashSet<String>();
}
#Entity
#Table(name="child")
public class Child {
#Id
String uuid;
#Column(name="parent_uuid")
String parentUuid;
}
now when I persist Parent, the children in childrenUuids are automatically persisted because the ManyToOne relationship. I want to prevent all operations to Parent(e.g. persist, remove ...) being cascaded to Child, is it possible for JPA? I have been researching for a few days, but could not find the answer. thank you.
You should use #OneToMany instead of #ElementCollection. A #OneToMany does not cascade by default. A #ElementCollection always cascades, as far as I know, which kind of makes sense, since "#ElementCollection defines a collection of instances of a basic type or embeddable class", and basic types/embeddables are considered an integral part of their parent.
#ElementCollection does always cascade. I finally resolve this by implementing my solution for #ElementCollection. I still use JPA annotations, instead, I add #Transient above #ElementCollection to make JPA ignore it. then I put my implementation as a JPA post-load listener to each entity, which will fill up each collection after the entity is loaded.
I have an Entity that holds the last instance of a Component:
#Entity
public class Customer {
...
#Embedded
public ServiceCall getLastServiceCall() {...}
...
}
#Embeddable
public class ServiceCall() {
public Date getDate();
public Status getStatus();
}
The ServiceCall is embedded in order to call customer.getLastServiceCall().getDate() without requiring another table join.
I want to make that association one-to-many, and start saving all ServiceCalls, while holding one embedded in the customer.
Hibernate's Docs (v3.6, which I'm using) states:
You can also use association annotations in an embeddable object (ie #OneToOne, #ManyToOne, #OneToMany or #ManyToMany). To override the association columns you can use #AssociationOverride.
and it seem that all I should do is add #OneToMany to the LastServiceCall association.
Will that work for me? If not, what are my alternatives? If yes, how will that affect 2nd level cache, and is there a limitation on updating that embedded instance (I can live with an immutable objects)?
#Embeded types are not supposed to have their own identity in the database, so I don't think you can add #OneToMany to the Customer class on the ServiceCall.
#OneToMany
#Embedded
public ServiceCall getLastServiceCall() {...}
However you can add an association to the #Embeded Service call element like so.
#Entity
pubic class HistoricalServiceCall extends ServiceCall
{
#Id
private String id;
}
#Embeddable
public class ServiceCall {
#OneToMany(fetch=FetchType.LAZY)
#JoinColumn(name="join_column_defined_on_customer_table")
List<HistoricalServiceCall> getServiceCallHistory();
}
Update: putting FetchType.LAZY on the getServiceCallHistory() is a hint to the JPA provider to wait until you call getServiceCallHistory before it does another select to pull in that association.
So with the setup I am describing if you do customer.getLastServiceCall().getDate() it will not pull
in the ServiceCallHistory before the relationship is lazy.
What you need is the following:
A Customer entity
An embeddable ServiceCall
A HistoricalServiceCall entity.
The Customer should contain an embedded ServiceCall field (the last service call).
The HistoricalServiceCall entity should have an ID, an embedded ServiceCall field (the data of the HistoricalServiceCall), and, potentially, a ManyToOne association to Customer if you want the OneToMany to be bidirectional.
The Customer should have a OneToMany association to HistoricalServiceCall.
Given the following example (departments - projects):
A department has the following properties (composite primary key):
#Entity
#IdClass(DeptId.class)
public class Department
{
#Id
#Column(name="number")
private Integer number;
#Id
#Column(name="country")
private String country;
#Column(name="name")
private String name;
#OneToMany(mappedBy="dept")
private Collection<Project> projects;
...
}
Here the PK class:
public class DeptId implements Serializable
{
private Integer number;
private String country;
...
}
The relationship between projects and departments is many-to-one, that is a deptartment can have many projects. The Project class is itself using a composite key referencing Department's composite key. Important note: it's only about the implementation with #IdClass not #EmbeddedId.
Then the (problematic) JPA 1.0 #IdClass implementation would have to look something like that (redundant deptNum and deptCtry properties): -> it's just a unique name within a department
#Entity
#IdClass(ProjectId.class)
public class Project
{
#Id
#Column(name="dept_number")
private Integer deptNumber;
#Id
#Column(name="dept_country")
private String deptCountry;
#Id
#Column(name="name")
private String name;
#ManyToOne
#JoinColumns({
#JoinColumn(name="dept_number", referencedColumnName="number"),
#JoinColumn(name="dept_country", referencedColumnName="country")
})
private Department dept;
...
}
The ProjectId is:
public class ProjectId implements Serializable
{
private String name;
private DeptId dept;
...
}
The problem with this is that neither Hibernate nor EclipseLink know how to map the two redundant properties deptNum and deptCtry in Project to the dept property in DeptId (or the properies within it). -> MappingException etc.
My question is:
Is this a limitation of JPA 1.0, that tables with composite keys referencing other composite keys with #IdClass implementations generally WON'T work, because the JPA implementation simply can't know how to map these fields?
As a workaround, you'd have to use #EmbeddedId for these classes or use JPA 2.0 syntax to annotate the #XToX associations with #Id. I just want to make sure my view on this is right.
Thanks
Yes, this is a limitation of JPA 1.0, corrected in JPA 2.0. In the new JPA 2.0, you can put the ID annotation on your dept relationship and completely avoid having the redundent deptCountry and deptNumber attributes, with the key class using nesting. In JPA 1.0, only basic mappings can be marked as apart of the ID, requiring redundent mappings and some code to ensure that the values/relationships get put into the cache correctly when persisting. Because of the redundancy, as mentioned in other answers, one of the mappings for a field needs to be marked read-only via the insertable/updatable=false. Doing so though means that value is not merged into the cache - so changes (such as on insert, since you can't change an objects ID once it exists) will not be reflected unless the object is refreshed from the database. If you mark the JoinColumns as read-only, you will need to get the values from the referenced dept and put them into the correspoinding basic id attributes manually when you want to persist a Project. But, you can also mark the basic attributes as read-only. Eclipselink anyway will not have any problems and will correctly set the field values using the associated dept entity (as long as it is set before persist is called on the Project). Notice though that the basic attributes may or may not be populated when you read back the project in a different context- this will depend on if the entity is refreshed from the database or not. If they are read-only, they do not get merged into the shared cache since they, being read only, should not have changed. So they can be just ignored, or if they must be populated, the entity refreshed or the values set from the dept in an event.
This same model can be reused by using the JPA2.0 #MapsId, which will also maintain the basic mappings using the values from the relationship for you. Only benifit I see is that you don't need to access the relationship (potentially causing unneccessary joins or database access on lazy relationships) to get the foreign key/id field values.
As for the ZipArea EclipseLink exceptions, they are due to ZipAreaId having a ZipId zip attribute instead it being flattened out. JPA 1.0 requires the key class to have an attribute of the same type and name for each #ID attribute in the Entity.
The problem with this is that neither Hibernate nor EclipseLink know how to map the two redundant properties deptNum and deptCtry in Project to the dept property in DeptId
This is why you need to define the ManyToOne foreign key(s) as read-only with this kind of mapping. This is done by setting the JoinColumn attributes insertable and updatable to false.
So try the following:
#Entity
#IdClass(ProjectId.class)
public class Project
{
#Id
#Column(name="dept_number")
private Integer deptNumber;
#Id
#Column(name="dept_country")
private String deptCountry;
#Id
#Column(name="name")
private String name;
#ManyToOne
#JoinColumns({
#JoinColumn(name="dept_number", referencedColumnName="number", insertable=false, updatable=false),
#JoinColumn(name="dept_country", referencedColumnName="country", insertable=false, updatable=false)
})
private Department dept;
...
}
The problem with the posted code is, that JPA 1.0 really doesn't allow nesting of composite primary key classes. This ProjectId is invalid:
public class ProjectId implements Serializable
{
private String name;
private DeptId dept;
...
}
DeptId has to be flattened, like:
public class ProjectId implements Serializable
{
private Integer deptNumber;
private String deptCountry;
private String name;
...
}
I just got an EclipseLink version to go, but Hibernate has problems with that. I wonder how to tell Hibernate that JPA 1.0 is assumed.