I am creating a JPA entity with a ManyToOne relationship. Why does having child.setParent(parent); cause the following fail during flush():
org.apache.openjpa.persistence.ArgumentException: Missing field for property "parent_id" in type "class test.ChildPrimaryKey".
The test code that fails:
Parent parent = new Parent();
Child child = new Child();
ChildPrimaryKey childPrimaryKey = new ChildPrimaryKey();
childPrimaryKey.setLookupId(1);
child.setId(childPrimaryKey);
child.setParent(parent); // <-- FAIL because of this
// Begin transaction
entityManager.clear();
entityManager.getTransaction().begin();
LOGGER.info("Persisting parent without child.");
entityManager.persist(parent);
LOGGER.info("Updating parent with child.");
childPrimaryKey.setParentId(parent.getId());
parent.getChildren().add(child);
entityManager.merge(parent);
// Fail happens at flush
entityManager.flush();
Entities:
#Embeddable
public class ChildPrimaryKey {
#Column(name = "lookup_id")
private int lookupId;
#Column(name = "parent_id")
private long parentId;
}
#Entity
#Table(name = "child")
public class Child {
#EmbeddedId
private ChildPrimaryKey id;
#MapsId("parent_id")
#ManyToOne
private Parent parent;
}
#Entity
#Table(name = "parent")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Child> children;
}
If I remove child.setParent(parent); statement, then my code passes. Using OpenJPA 2.2.2
I beleive your MapsId should be #MapsId("parentId") based on the class structure you've presented. The value of MapsId is an attribute, not a column name.
Related
Using hibernate, insert to child fails with "Referential integrity constraint violation" on child. Parent Id is incremented for each child.
// Parent: Composite primary key, one auto generated
#IdClass(PlanId.class)
public class PlanEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PlanIdGenerator")
#SequenceGenerator(name = "PlanIdGenerator", sequenceName = "PLAN_ID_SEQUENCE", allocationSize = 1)
private Long id;
#Id
private Long version;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "planEntity", fetch = FetchType.LAZY, orphanRemoval = true) //
private Collection<PlanGoalBucketEntity> goalBuckets = new ArrayList<>();
public void addPlanGoalBucketEntity(PlanGoalBucketEntity goalBucket) {
goalBuckets.add(goalBucket);
goalBucket.setPlanEntity(this);
}
public void removePosition(PlanGoalBucketEntity goalBucket) {
goalBuckets.remove(goalBucket);
goalBucket.setPlanEntity(null);
}
.....
}
//Child
public class PlanGoalBucketEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(optional = false)
#JoinColumns({ #JoinColumn(name = "plan_id", referencedColumnName = "id"),
#JoinColumn(name = "version", referencedColumnName = "version") })
private PlanEntity planEntity;
.....
}
Insert to planentity (parent) with one PlanGoalBucketEntity (child) is all good.
Inserting parent with multiple child, it fails with foreign key violation "Referential integrity constraint violation". Inserting first child is fine but second child fails because it increments parent id for second child.
Couldn't figure out what is going wrong.
You haven't pasted your PlanId class, but I assume it is something like this
public class PlanId implements Serializable {
public Long id;
public Long version;
}
I copied your code and the following test works, all I did was implement PlanId as above and added necessary setters in your entities. I am using an H2 DB
#Test
public void plan() {
PlanEntity planEntity = new PlanEntity();
planEntity.setVersion(1L);
planEntity.addPlanGoalBucketEntity(new PlanGoalBucketEntity());
planEntity.addPlanGoalBucketEntity(new PlanGoalBucketEntity());
planEntityRepository.save(planEntity);
// Check save of 2 plan goal buckets is successful
List<PlanEntity> planEntities =
entityManager.createQuery(
"select distinct p from PlanEntity p left join fetch p.goalBuckets", PlanEntity.class)
.getResultList();
assertEquals(1, planEntities.size());
assertEquals(2, planEntities.iterator().next().getGoalBuckets().size());
}
// JpaRepository
#Repository
public interface PlanEntityRepository extends JpaRepository<PlanEntity, PlanId> {}
I have a view which has a parent-child relationship. So I have my entity this way:
#Entity
#Table(name = "assessment_v", schema = "hlt_hrsc")
public class HrscLabellingAssessmentVEntity {
#Id
private String id;
#Column(name = "child_id")
private String childId;
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "id")
private HrscLabellingAssessmentVEntity parent;
#OneToMany(mappedBy = "parent")
private Set<HrscLabellingAssessmentVEntity> child = new HashSet<>();
}
when I start my application it throws an error :
nested exception is org.hibernate.MappingException: Could not determine type for: java.util.Set
Please find the below image of the view for parent child eg:
id child_id
120.35871 120.35872
120.35872 null
Here 120.35872 is the parent record and 120.35871 is the child record. The parentId is maintained on the child_id column. My requirement is when I try to fetch the parent it should have the child records also.
what am I doing wrong??
Please check this example
#Entity
#Table(name = "assessment_v", schema = "hlt_hrsc")
public class HrscLabellingAssessmentVEntity {
#Id
private String id;
#Column(name = "child_id")
private String childId;
#JsonBackReference
#JoinColumn(name = "parent_id", referencedColumnName = "id")
#ManyToOne(fetch = FetchType.LAZY)
#Fetch(FetchMode.JOIN)
private HrscLabellingAssessmentVEntity parentId;
#JsonManagedReference
#OneToMany(mappedBy = "parentId", cascade = CascadeType.ALL)
private Set<HrscLabellingAssessmentVEntity> child = new HashSet<>();
}
Let's say I have the following "parent" pojo...
#Entity
#Table(name = "parent")
public class Parent{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(fetch=FetchType.LAZY, mappedBy = "parent", cascade = {CascadeType.ALL})
#JsonIgnoreProperties("parent")
List<Child> children;
}
and I have the following child POJO :
#Entity
#Table(name = "child")
public class Child{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name="parent_id")
private Parent parent;
}
The result of this will be that I have two tables, and my jpa repository will perform queries using the parent_id field within the child table.
However, What if I want it be like a lookup, where by there is a third table for the relationship, where I have the child id and the parent id as a row, and that would be the relationship? can I modify my spring - jpa / hibernate setup for that? If so, some help would be appreciated!
I cannot figure out how to simply relate a child entity to and existing parent.
#Entity
#Table(name = "parent")
#Document(indexName = "parent")
public class Parent implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "name")
private String name;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(unique = true)
private Child child;
//getters, setters
}
Child
#Entity
#Table(name = "child")
#Document(indexName = "child")
public class Child implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "name")
private String name;
#OneToOne(mappedBy = "child")
private Parent parent;
//getters, setters
}
These are the two basic models.
The parent already exists within the database, and I want to add a new child in relation.
Child childEntity = childRepository.save(child);
The child is populated as follows:
child.json
{
"name": "smallChild",
"parent": { "id" : "1" }
}
I want to be able to save the child, and have it automatically have a relation to the parent.
I did some really nasty code...
Save the Child without a parent for the ID
Query the database for the parent by ID
Set the child to the parent entity
Save the Parent with the new child
Set the Parent entity TO the child entity
Resave the child with the parent.
This ended up being 6-ish database queries.
I tried watching a few course videos from lynda.com, but it didn't help.
Thanks!
Either the Parent Primary Key or the complete entity is required.
If the parent's ID available, then the extra query for fetching parent object is not required if the goal is to just save.
If you have the parent's Id available with you then you can save the child entity as:
Child child = new Child();
//...
//Setters for child
//...
//Now just create a parent object and set the id to it
Parent p = new Parent();
p.SetId(parentId); // as the parentId is already vailable
child.setParent(p);
Child childEntity = childRepository.save(child);
I have the following table structure:
parent(parentId)
child(childId, parentId fk)
Then, I have the following objects:
#Entity
#Table(name = "parent")
public class Parent {
#Id
#GeneratedValue(...)
private String id;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "parentId")
Set<Child> children
}
#Entity
#Table(name = "child")
public class Child {
#Id
#GeneratedValue(...)
private String id;
#Column(...)
private String parentId;
}
Now, I create a transient parent and child, and I add the child to the parent, then save the parent:
Parent parent = new Parent();
parent.children.add(new Child());
parentDao.save(parent);
I get the exception:
org.hibernate.PropertyValueException: not-null property references a null or transient value
My question: How can I get the parentId in the child class to automatically be set to the value generated by the insertion of the parent?
I would re-arange your class structure as follows:
#Entity
#Table(name = "parent")
public class Parent {
#Id
#GeneratedValue(...)
private String id;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "parentId")
Set<Child> children
}
#Entity
#Table(name = "child")
public class Child {
#Id
#GeneratedValue(...)
private String id;
#Column(...)
private Parent parent;
}
Then when hibernate fetches the parent class, and initializes the set of children, the child class will have a reference to the parent class. Then to get the parentId, you would call:
Child c = new Child()....
c.parent.id;
Your child shouldn't have a private String parentId, but a private Parent parent, and when you parent.children.add(child), you must also child.setParent(parent). See the prototypical parent-child relationship example in the Hibernate reference and the bidi one-to-many section of the annotation reference.