I have two entities: WorkoutTemplate and ConcreteExercise.
In WorkoutTemplate I have this relationship with ConcreteExercises
#OneToMany(cascade = CascadeType.ALL, mappedBy = "belongingWorkout", fetch = FetchType.LAZY)
private List<ConcreteExercise> concreteExercises;
And in ConcreteExercise I have this relationship with WorkoutTemplate
#ManyToOne
private WorkoutTemplate belongingWorkout;
I would like to insert a WorkoutTemplate into my database...
I make the request to the controller sending a Json like this:
{ "workoutName" : "My Workout",
concreteExercises: [
{
"name" : "Squat"
}
]
}
The DAO insert into my DB the WorkoutTemplate
And insert also in the table of the ConcreteExercise the name.
But not the reference to the WorkoutTemplate...
Practically, the table ConcreteExercise is made of:
id, name, belongin_workout_id
With the request above, we populate the id (auto-increment) and the name, but not the foreign key to the WorkoutTemplate.
How can I solve this ?
I would like to automatically insert the foreign key without sending it in the request or doing it manually into the service
Hi there it's because you are not using #JoinColumn which marks a column for as a join column for an entity association or an element collection.
On your WorkoutTemplate entity - you can retain this:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "belongingWorkout", fetch = FetchType.LAZY)
private List<ConcreteExercise> concreteExercises;
But on your ConcreteExercise entity - you need to have this change:
#ManyToOne
#JoinColumn(name = "workout_template_id", nullable = false)
private WorkoutTemplate belongingWorkout;
The above code will create a foreign key linking the ConcreteExercise entity with the primary key from the WorkoutTemplate entity. The name of the foreign key column in the ConcreteExercise entity is specified by name property which for this case is workout_template_id - feel free to change this.
If you have set this up but still not working, it will also be helpful to share a code snippet on how are you saving these.
Related
My Spring web application allows users to update "Employee" records to change the fields or add new "Phone" records related to this "Employee" record. However, when the "Employee" record is submitted for update after adding a new "Phone" record, it's throwing a SQL error exception.
The problem is that the "employee_id" foreign key on "Phone" table to the "Employee" table isn't set in the eventual SQL insert statement submitted to the database. However, in the "PhoneEntity" JPA entity object that is referenced by the updated/merged "EmployeeEntity" object, the property associated with the employee_id database field isn't null, it's set to the "EmployeeEnity" Object being updated/merged.
From my understanding of JPA, having the entity property associated with a database field should set it when the insert statement for the entity's record is submitted to the database, but in this case it isn't which is causing this error.
I've tried stepping through with a debugger, and I have verified that the created PhoneEntity object is a member of EmployeeEntity's phones property, and that the same PhoneEntity's employee property is set to the same EmployeeEntity object (with the same object IDs) in a bidirectional relationship.
I've also set the hibernate.show_sql=true to see the SQL statement being submitted to the database and it includes the statement (with the ellipses being more fields):
Hibernate:
insert
into
phone
(id, employee_id, ...)
values
(?, ?, ...)
Which means that it is inserting a new phone for the new PhoneEntity object.
After trying to running this insert statement it gives the SQL error "Column 'employee_id' cannot be null". However like I said before, I've checked with the debugger and the employee property is indeed set to the EmployeeEntity object.
this is a simplified example of what my code looks like:
#Entity
#Table(name = "employee")
public class EmployeeEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#OneToMany(mappedBy="employee", cascade = {CascadeType.PERSIST})
private Set<PhoneEntity> phones = new HashSet<>();
...
}
#Entity
#Table(name = "phone")
public class PhoneEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#ManyToOne
#JoinColumn(name = "employee_id", nullable = false)
private EmployeeEntity employee;
...
}
With tables that have the structure created by the following SQL statements.
CREATE TABLE employee (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
...
);
CREATE TABLE phone (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
employee_id INT NOT NULL,
...
FOREIGN KEY(employee_id) REFERENCES employee(id)
);
And the following is where it actually submits the updates to the entity manager to make updates to the database.
public void update(EmployeeDomain employee) {
EmployeeEntity entity = employeeDomainToEntity.transform(employee)
getEntityManager().merge(entity);
}
The EmployeeEntity and PhoneEntity objects are created by converting similar domain objects that were in turn deserialized from a http request. I'd include more of this section of the code but, as I've mentioned, I've already confirmed with my debugger that the actual entity objects being submitted to the merge are already in the form that we expected with the phones fields and employee fields being set correctly, so the end entities should be correct.
In the official JPA specification document (version 2.1) in section "3.2.7.1 Merging Detached Entity State" (page 85) we find:
For all entities Y referenced by relationships from X having the cascade element value cascade=MERGE or cascade=ALL, Y is merged recursively as Y'. For all such Y referenced by X, X' is set to reference Y'. (Note that if X is managed then X is the same object as X'.)
This explains that you are lacking cascade=MERGE for the annotation of the phones field.
As proposed in thanh ngo's answer, the aforementioned definition (or: explanation) thus translates to:
#OneToMany(mappedBy="employee", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<PhoneEntity> phones = new HashSet<>();
Alternatively, you could also make use of cascade=CascadeType.ALL. However, this would also include operations such as CascadeType.REMOVE which might not always be intended.
Hope it helps.
I think the problem is that you are using merge.
The cascade type setting for the entity should be:
#OneToMany(mappedBy="employee", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<PhoneEntity> phones = new HashSet<>();
Okay, so within the database we have a table called distributionCompanies, created like so:
CREATE TABLE `distributionCompanies` (
`distributionCompanyID` INT(11) NOT NULL,
`distributionCompanyName` VARCHAR(255) NOT NULL,
PRIMARY KEY (distributionCompanyID)
);
I'm trying to map this table to a class using Hibernate:
#Entity
#Table(name = "distributionCompanies")
public class DistributionCompany implements DatabaseObject {
#Id
#GeneratedValue
#Column(name = "distributionCompanyID", length = 11, unique = true, nullable = false)
private int distributionCompanyID;
....
However, when running, I hit this issue:
Initial SessionFactory creation failedorg.hibernate.HibernateException: Missing column: distributionCompanyID_distributionCompanyID in database2.distributionCompanies
This isn't the only table in the database, and I've managed to map other classes successfully using the same method, so I'm a little stumped as to why this is causing an issue.
Thank you for your time,
Samuel Smith
EDIT: In response to Xavi's comment, I temporarily removed another mapping for the column, and the error went away, so the bad-egg probably lays in the following code:
#ManyToOne(targetEntity = DistributionCompany.class)
#JoinTable(name = "distributionCompanies", joinColumns = { #JoinColumn(name = "distributionCompanyID", nullable = false) })
private int distributionCompanyID;
Hibernate is looking for a column named distributionCompanyID_distributionCompanyID in your distributionCompanies table.
This is probably due to a ToOne association mapping towards this table without #JoinColum.
From Hibernate Documentation:
The #JoinColumn attribute is optional, the default value(s) is like in one to one, the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column in the owned side. In this example company_id because the property name is company and the column id of Company is id.
If you've got a #ManyToOne or #OneToOne association mapping in another entity, this would explain why Hibernate is looking for such a column.
EDIT Seeing the association mapping you posted, it looks like it should be:
#ManyToOne(targetEntity = DistributionCompany.class)
#JoinColumn(name = "distributionCompanyID")
private DistributionCompany distributionCompany;
The #JoinTable annotation is used to specify a join table (that means an intermediate table used to model many-to-many associations). And the point of mapping an association would be to dispose of the mapped object instance (in this case a DistributionCompany, not just a distributionCompanyId).
I need some help with a JPA self referencing relationship. I think there is something that I have not defined correctly.
I have a JPA entity bean named ItemEntity. There are two types of items. Parent items and child items. The parent item can have many child items and the child item only has one parent item. So really this is a ManyToOne / OneToMany JPA self referencing relationship.
In my database the item table looks like this...
item_no,parent_item_no,item_description
111,null,This is my parent item
222,111,This is my child item
So in my java program when I call itemEntity.getChildren on item 111, I would expect to see 222 however I am getting null.
Here is how I have defined my JPA relationship...
#Entity(name = "stg_item")
public class ItemEntity implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "itemid")
#TableGenerator(name = "itemid", table = "stg_items_sequence", allocationSize = 1)
private Long id;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "item_no", referencedColumnName = "parent_item_no")
private ItemEntity parent;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "parent")
private Collection<ItemEntity> children;
Can anyone tell me what I am doing wrong here?
The item entity has a #ManyToOne relationship with another one of my entities named stg_import_payload. Here is the named query I am using. Maybe I have to do something special with the named query?
"SELECT x "
+ " FROM stg_import_payload x "
+ " WHERE x.processedInd='N' "
+ " AND EXISTS (SELECT stg_item FROM stg_item stg_item WHERE stg_item.importPayload = x AND stg_item.processedInd='N') ";
Thanks.
Docs:
String javax.persistence.JoinColumn.referencedColumnName()
(Optional) The name of the column referenced by this foreign key column.
When used with entity relationship mappings other than the cases described here, the referenced column is in the table of the target entity.
When used with a unidirectional OneToMany foreign key mapping, the referenced column is in the table of the source entity.When used inside a JoinTable annotation, the referenced key column is in the entity table of the owning entity, or inverse entity if the join is part of the inverse join definition.
When used in a CollectionTable mapping, the referenced column is in the table of the entity containing the collection.
Default (only applies if single join column is being used): The same name as the primary key column of the referenced table.
You should change this:
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "item_no", referencedColumnName = "parent_item_no")
private ItemEntity parent;
to
#ManyToOne(fetch = FetchType.LAZY, optional = true)
private ItemEntity parent;
It works for me
hi I'm getting this "javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: ERROR: duplicate key value violates unique constraint"
I have two tables A and B,
A has id, b_id, B has id,A_id's.
A - oneToMany , B - manyToOne relationship.
on A,
#OneToOne(cascade = { CascadeType.ALL })
#JoinColumn(name = "LATEST_VERSION_ID") #Valid
#EntityProperty(type = "GuidKey", relation = "B.id")
on B,
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "A_ID") #Valid
#EntityProperty(type = "Key", relation = "A.id")
when I create Page A I was able to Do so, But when I try to update I get unique constraint violation on table 'B'.
It says the record already exist.
You have to make a bi-directional relation using the mappedBy property.
See:
one to one bidirectional hibernate mapping
http://www.mkyong.com/hibernate/hibernate-one-to-one-relationship-example-annotation
http://www.codereye.com/2009/04/hibernate-bi-directional-one-to-one.html
Also, #EntityProperty isn't required for this. The foreign key should be in one table in one-to-one.
We have two entities:
public class User {
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", insertable = true, updatable = true)
#ElementList(name = "tabledata")
public List<MyUserTableData> tableData = new ArrayList<MyUserTableData>();
}
public class MyUserTableData {
public Long user_id;
}
The action that I do is that I remove an entry from u.tableData and then call the EntityManager to merge(u).
OpenJPA will remove the entry from the User object by setting the corresponding record in the MyUserTableData with a user_id = "null".
But what I want is that if the entry is deleted from the User, it should also delete the record from the MyUserTableData and not just NULL the column user_id.
How can I force OpenJPA to delete the OneToMany related entry instead of putting a null in the column?
I will not accept answers that do asume that my database schema is bad :) The table MyUserTableData is basically a foreign key table that connects the user to another entity but holds some more information then just a foreign key, it adds some meta data to the foreign key that neither belong to the user nor to the other entity.
Thanks!
Sebastian
I was able to resolve my issue:
http://openjpa.apache.org/builds/1.0.4/apache-openjpa-1.0.4/docs/manual/manual.html#dependent
#ElementDependent => does exactly what I want.