Please help! It has already taken me a day, and I am no closer to resolving the issue. My set up is as follows:
Eclipse Indigo
Eclipselink 2.3
Apache Tomcat 6
What I am doing is I am persisting an entity that has an #OneToOne mapping with a child entity. Once the record is in the database, I am running a select and successfully selecting the newly inserted record. However, the child entity is null.
My goal is to only insert the parent record, because the child record is already in the DB.
Question: what do I need to change to cause the child record to be populated?
I have an entity that I persist:
EntityManager em = getEntityManager();
try {
em.getTransaction().begin();
em.persist(timeSheet);
em.getTransaction().commit();
}
The entity contains #OneToOne:
#Entity
#Cache(expiry=-1)
#Table(name="TIME_SHEET")
public class TimeSheet implements Serializable {
private static final long serialVersionUID = 1L;
#OneToOne(fetch=FetchType.EAGER)
#JoinColumns({
#JoinColumn(name="PROJECT_ID", referencedColumnName="PROJECT_ID", nullable = false, insertable=false, updatable=false),
#JoinColumn(name="COMPANY_ID", referencedColumnName="COMPANY_ID", nullable = false, insertable=false, updatable=false)
})
private Project project;
public Project getProject() {
return project;
}
The child entity is:
#Entity
#Cache(expiry=-1)
#Table(name="PROJECT")
public class Project implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private ProjectPK id;
After "TimeSheet" is persisted and selected, "Project" is null. What should I do to ensure that "Project" is populated?
Thank you!
Well, in the code shown, you persist a TimeSheet without first setting a Project in it, so why would you expect it to have a Project when you load it? You should never expect your object model to look different after you load it than it did before you saved it. If it does look different, then you have a problem.
You should set the cascade policy correctly.
#OneToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#entity-hibspec-cascade
Cascade policy dictates what to do with associated entities when you perform a persistence action on the parent entity.
As Ryan points out, you need to maintain the reference in TimeSheet to the Project. This is because the TimeSheet is cached. So you can either set the relationship by reading in the existing project when creating the TimeSheet, or by refreshing the TimeSheet after the transaction commits so that it gets built.
The code you have shown sets the TimeSheet->Project relationship fields are read only and doesn't show how the foriegn keys are populated. Make sure that these are actually set when inserting the TimeSheet through other mappings or it maybe that the TimeSheet legitimately doesn't have a Project - though the fields seem to have a not-null constraint.
Related
I have an hibernate managed entity:
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// (omitted useless details)
#OneToOne
#JoinColumn(name = "child_id", updatable = false)
private Child child;
// (omitted useless details)
}
--
#Entity
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// (omitted rest...this is a POJO)
}
Since the responsibility of the update for Child is managed elsewhere, my goal is to simply not update child when the parent is saved. Unfortunately, this updatable = false on the #JoinColumn appears to be getting ignored, saving any changes made to both parent and child.
How can I achieve only saving the parent in a one to one relationship?
Any help is appreciated.
EDIT: Clarification, the Child class is an #Entity as well, but a simple POJO. The relationship between Parent and Child is unidirectional from the parent.
I looked into this for quite a while and looking at the documentation for #JoinColumn annotation and the updatable/insertable properties it says...
"(Optional) Whether the column is included in SQL UPDATE statements generated by the persistence provider."
What this means is setting updatable = false will only prevent the foreign key from being updated (column is included). When you save the parent entity it will also save all child entities attached to the object.
This means that any child properties that are attached to the parent entity when you save will also be persisted. This brings another issue to light if someone else may have updated this child entity, but because you are saving the parent entity with the child entity attached to it, you will un-knowingly overwrite any changes made to the child object.
Solution:
AFAIK the only way to resolve this is to be diligent about the object you save, unless you really need to be saving/updating the child entities don't. Keep your objects self contained, this is the downfalls of lazy/eager loaded properties and saving that same object graph. You could also do like another poster said and set the child entity to null before the save to prevent any updates, but can easily be missed.
If you are OK with saving the entire object tree (parent and children entities) you could add a property that utilizes the #Version attribute to introduce optimistic concurrency. This way if any entity was updated by someone else and you are trying to save an old version it will fail.
#Version
#Column(name=”version”)
private long version;
public long getVersion() { return version; }
I'm working with JPA 2 + Hibernate 4 and I'm implementing some CRUD operations on model entities.
Now I need to prevent a certain entity (EntityB) to be deleted when a related entity (EntityA) exists in database:
#Entity
public class EntityA {
#Id
private int id;
#OneToOne(mappedBy = "entityA", optional = false, fetch = FetchType.LAZY)
private EntityB entityB;
//...
}
#Entity
public class EntityB {
#Id
private int id;
#OneToOne
#JoinColumn(name = "id")
private EntityA entityA;
//...
}
Is there any way to achieve this using relationship options or should I check EntityA existence in my dao/repository before removing EntityB?
NOTE I need this also for #ManyToOne relationships.
If you want to prevent that in your code, than simply do not delete that entity (by checking that manually). There is no possibility to do that with annotations.
On the other side, this sounds to me rather like a need for a DB constraint. If those entities are already related, then simply add a foreign key constraint (if none is existent). If not, than think of adding one.
PS: if you already have a relationship, check the CascadeType.REMOVE setting.
I don't think you can solve this with annotations. You should manally check related-entity existence before.
I'm using JPA2 with EclipseLink implementation
![Simple table structure][1]
Here are the two tables which I try to map and the JPA annotations.
public class Story implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
Integer id;
#Temporal(TemporalType.TIMESTAMP)
#Column (name="DATE_CREATED")
Date dateCreated;
String title;
String description;
#Column(name="AUTHOR_ID")
Integer authorId;
#Column(name="COUNTRY_ID")
Integer countryId;
private String reviews;
#OneToMany(mappedBy = "story", cascade=CascadeType.ALL)
private List<Tip> tipList;
}
public class Tip implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
private String description;
private Integer vote;
#ManyToOne (cascade=CascadeType.ALL)
#JoinColumn(name="STORY_ID", referencedColumnName="ID")
private Story story;
}
As a simple example I would like to persist a story and some story related tips in the same transaction.
Here is the section of code which does that:
Story newStory = new Story(title, body, ...);
EntityTransaction transaction = em.getTransaction().begin();
boolean completed = storyService.create(newStory);
//The tips are saved as a List<String>. This methods creates the needed List<Tip> from the Strings
List<Tip> tips = TipUtil.getTipList(tipList);
newStory.setTipList(tips)
transaction.commit();
I have no errors and all the entities are persisted in the database. The problem is that in the tip table the story_id field is always NULL. I can imagine that JPA is unable to get the new id from the story table. What's the correct approach here?
LE
In the current state of the code, the Tip entities are persisted but the country ID remains null.
With JPA, it is always recommended to update the relationship on both the sides in a bi-directional relationship. This is to ensure that the data is consistent in your application layer and nothing to do with the database.
However it is mandatory that you update the owning side of the relationship in a bidirectional relationship.
So, setting/not setting
story.setTipList(tips)
is up to you. But if you want the changes to reflect properly in DB then you mush call
tip.setStory(story)
as Tip is the owning side here, as per your code.
Also your code looks incomplete to me. Reasons is,
the entity returned by storyService.create(newStory) is managed but not the newStory. So just setting newStory.setTipList(tips) will not updated the db
Because you need to update the parent link story in each of your child.
The way its is done is to create a addTip(Tip tip) method in your Story class.
This method does :
tip.setStory(this);
tipList.add(tip);
If you don't need bedirectional approach, you can remove the story field in Tip and it will resolve your problem
Remove the
#Column(name = "STORY_ID")
private Integer storyId;
You are already declaring it in #JoinColumn(name="STORY_ID", referencedColumnName="ID")
That is why you are getting the error Multiple writable mappings exist for the field [tip.STORY_ID]
You should not be using PrimaryKeyJoinColumn, just JoinColumn, but having your complete class would help giving a certain answer.
PrimaryKeyJoinColumn would only be used if the story_id was also the id of the Tip (no id in Tip) and there was a duplicate basic mapping for it. It should rarely be used, and is not required in JPA 2.0 anymore as duplicate id mappings are no longer required.
I have two tables: t_promo_program and t_promo_program_param.
They are represented by the following JPA entities:
#Entity
#Table(name = "t_promo_program")
public class PromoProgram {
#Id
#Column(name = "promo_program_id")
private Long id;
#OneToMany(cascade = {CascadeType.REMOVE})
#JoinColumn(name = "promo_program_id")
private List<PromoProgramParam> params;
}
#Entity
#Table(name = "t_promo_program_param")
public class PromoProgramParam {
#Id
#Column(name = "promo_program_param_id")
private Long id;
//#NotNull // This is a Hibernate annotation so that my test db gets created with the NOT NULL attribute, I'm not married to this annotation.
#ManyToOne
#JoinColumn(name = "PROMO_PROGRAM_ID", referencedColumnName = "promo_program_id")
private PromoProgram promoProgram;
}
When I delete a PromoProgram, Hibernate hits my database with:
update
T_PROMO_PROGRAM_PARAM
set
promo_program_id=null
where
promo_program_id=?
delete
from
t_promo_program
where
promo_program_id=?
and last_change=?
I'm at a loss for where to start looking for the source of the problem.
Oh crud, it was a missing "mappedBy" field in PromoProgram.
Double-check whether you're maintaining bidirectional association consistency. That is; make sure that all PromoProgramParam entities that link to a PromoProgram as its parent are also contained in said parent's params list. It's a good idea to make sure this happens regardless of which side "initiates" the association if you will; if setPromoProgram is called on a PromoProgramParam, have the setter automatically add itself to the PromoProgram's params list. Vice versa, when calling addPromoProgramParam on a PromoProgram, have it set itself as the param's parent.
I've encountered this problem before as well, and it was due to not maintaining bidirectional consistency. I debugged around into Hibernate and found that it was unable to cascade the delete operation to the children because they weren't in the list. However, they most certainly were present in the database, and caused FK exceptions as Hibernate tried to delete only the parent without first deleting its children (which you've likely also encountered with the #NonNull in place).
FYI, I believe the proper "EJB 3.0"-way of making the PromoProgramParam.promoProgram field (say that a 100 times) non-nullable is to set the optional=false attribute on the #ManyToOne annotation.
When trying to save an ID from my parent class into a child class, I keep getting the error
"ERROR - Field 'parent_id' doesn't have a default value"
I have tried all types of mappings. I am using annotations.
Any help on this would be appreciated
Parent:
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="description")
private String description;
#OneToMany
#Cascade(value= {org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE})
#JoinColumn(name="parent_id")
private List<Child> children;
Child:
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="description")
private String description;
Thanks.
A late addition in case anyone ever runs into the same issue.
This entity here, when persisted using Hibernate 4.1.8, will cascade the FieldChangeentities, but will not fill the join column:
#Entity
public class Event {
//ID and other fields here
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "event_id")
private List<FieldChange<?>> fields = new ArrayList<FieldChange<?>>();
}
Neither does the insert statement set the event_id column, nor does it update the inserted entity after the fact - the event_id remains null and the relation is lost.
If, however, the #JoinColumn definition is changed like this:
#JoinColumn(name = "event_id", nullable = false)
, then the insert statement includes the event_id column like it should, and all is well.
This may only be a regression in this particular version of Hibernate, but maybe it helps someone.
In your case JPA provider to persist child object with its parent perform at least three queries on db. First two persist the objects by its own. The last one
update child object with the foreign key referencing parent. The second query fail because you have a NOT NULL constraint on the foreign key column. You have three options:
Remove NOT NULL constraint on foreign key in the child entity
Use bidirectional relationship
Change JPA provider to one which supports such cases.
You must have something wrong somewhere else because those mappings will work the way they are. They could be better, but they'll work. Specifically, all the #Column annotations are redundant and unnecessary, and as non sequitor noted, you should use the cascade property of JPA's #OneToMany instead of Hibernate's #Cascade. I've created a runnable example with the cleaned-up version of what you posted. If you have git and maven, you can run it with:
git clone git://github.com/zzantozz/testbed tmp
cd tmp
mvn -q compile exec:java \
-Dexec.mainClass=rds.hibernate.UnidirectionalManyToOneJoinColumn \
-pl hibernate-unidirectional-one-to-many-with-join-column
It creates a parent with two children, saves them, and then loads them and prints out the graph. The output is:
Creating parent with two children
Loading saved parent
Parent{description='parent', children=[Child{description='child 2'}, Child{description='child 1'}]}
Change your #OneToMany to #OneToMany(cascade=CascadeType.ALL) use JPA rather than the Hibernate extensions
My guess is that the #JoinColumn annotation needs a referencedColumnName assigned.
#JoinColumn(name = "parent_id", referencedColumnName = "id")