I have some Child entities that are already stored in the database. At some point, the user selects some entries adds them to a Parent and saves that Parent. The reference to the Parent in the Child does not get updated when calling the JpaReposity.save function of the Parent. Do I need to manually update/save every child entity in the database?
Parent
#Entity
public class Parent extends Base {
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH}, mappedBy = "parent")
private List<Child> children;
public List<Child> getChildren() {
return Collections.unmodifiableList(children);
}
public void addChild(Child child) {
if(this.children== null) { this.children= new ArrayList<>(); }
this.children.add(child);
child.setParent(this);
}
}
Child
#Entity
public class Child extends Base{
#ManyToOne
private Parent parent;
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent= parent
if(!parent.getChildren().contains(this)) {
parent.getChildren().add(this);
}
}
}
Repository
public interface ParentRepository extends JpaRepository<Parent, Long> {}
Service
#Transactional
Parent createAndSaveParent() {
List<Child> children = this.childRepo.findAll();
Parent parent = new Parent();
children.forEach(c -> parent.addChild(c));
return this.parentRepo.save(parent);
}
I am not getting any error, if I look at the returned Parent object after calling save, the children have the corrent parent set, but in the database the reference doesn't get updated.
Yes because of
mappedBy = "parent"
this means that the relationship is maintained by parent in the Child class and is responsible for setting the foreign key.
Related
I'm building an Asp.Net core Api project.
I have a Parent entity as Aggregate root. That parent entity has a collection of Owned types.
If instead of collection of Owned types I have collection of Entities, I would simply tell the parent to delete the entity by it's Id like this:
// Parent entity method
public void DeleteChild(int childId)
{
var existing = this._children.FirstOrDefault(x=>x.Id == childId);
...
this._children.Remove(existing);
}
How to delete the owned object in the collection of an aggregate (owned entity does not have an Id property)?
//Parent entity method
public void DeleteChild(?????????) //Don't know what to put here, a surrogate key?
{
...
}
The model is something like this:
public class Parent
{
public int Id {get;set;}
public string Name {get;set;}
public List<Child> Children {get;set;}
... //other non important props
}
public class Child
{
public string Name {get;set;}
... //other non important props
}
And the mapping:
public class ParentEntityConfiguration : IEntityTypeConfiguration<Parent>
{
public void Configure(EntityTypeBuilder<Device> builder)
{
builder.ToTable("Parents");
builder.HasKey(x => x.Id);
builder.OwnsMany<Child>("Children", cfg =>
{
cfg.ToTable("Children");
cfg.WithOwner().HasForeignKey("ParentId");
cfg.HasKey("ParentId", "Name");
cfg.HasIndex("ParentId", "Name").IsUnique();
});
}
}
So there is no "Id" propety in the Child nor the "ParentId" as they are shadowed.
The same goes for modifying the Owned object.
What is the best practice for this?
Thanks
P.S. I'm using EF Core 6.x
Edit: Given the added details in the question.
You are using a composite key for your child which is a combination between the child's name and the parentid. Since child is a owned type it will be automatically included when you retrieve the object of the parent. (If you didn't change the configuration AutoInclude)
So if you get your parent object like:
var parent = dbcontext.Parent.Where(p => p.id = idParent);
//find the one you want to eliminate by name.
var child = parent.children.First(item => item.name == "foo").value;
//remove the child from the list
parent.children.Remove(child);
//save changes
dbcontext.SaveChangesAsync();
================================================================
OLD:
You just have to find the parent first. Then you can iterate through the childrens and delete the ones that you want. Then just call the save changes and they should be removed.
Example:
// Parent entity method
public void DeleteChild(int childId, idParent)
{
var parent= dbcontext.Parent.FirstOrDefault(x=>x.Id == idparent);
for (int i = 0; i < parent.Children.length; i++)
{
if (collection[i].id = childId)
collection[i] = null;
}
dbcontext.SaveChangesAsync();
}
You need to reference the value-object by itself:
public void DeleteChild(Child child)
{
// redacted
}
I'm working with a 3rd party library provided to our team where one of the entities has a OneToMany relationship to entities of the same type of itself. I've changed the entity name to keep it anonymous.
Probably there's a better way of annotating entities with this type of relationship but as it's provided by a 3rd party I'm avoiding making to many changes so that it's compatible with future patches and updates.
It's using OpenJPA 2.4.0-ep2.0
#Entity
#Table(name = Person.TABLE_NAME)
public class Person {
private Long parentUid;
private List<Person> children = new ArrayList<>();
#OneToMany(targetEntity = Person.class, cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
#ElementJoinColumn(name = "PARENT_UID")
#ElementForeignKey
#ElementDependent
public List<Person> getChildren() {
return this.children;
}
}
When I try to persist a person with children, only the main entity gets persisted and children ignored.
However, if I change the fetch attribute to FetchType.EAGER it works (it persists both the parent and children). My understanding was that the fetch type only affects the loading, not the inserting. Any ideas why is it happening?
Also, is there a way of making it work while keeping the fetch type to FetchType.LAZY?
I've tried the following (modify the setter):
protected void setChildren(final List<Person> children) {
if (Objects.nonNull(children)) {
for (Person child : children) {
child.setParentUid(parentUid);
}
this.childItems = children;
} else {
this.childItems = new ArrayList<>();
}
}
Problem is in the child entity ,you should use #ManyToOne annotation in child entity.
add following code to Person :
public class person {
.
.
#MantToOne(fetch=FetchType.LAZY)
#JoinClolumn(name="PARENT_UID")
private Person parent;
public void setParent(Person parent){
}
.
.
}
then revise setChildren Code like this:
protected void setChildren(final List<Person> children) {
if (Objects.nonNull(children)) {
for (Person child : children) {
child.setParent(this);
}
this.childItems = children;
} else {
this.childItems = new ArrayList<>();
}
}
one important point is Řalways fetch type must be sync in parent and child.
How to write OneToMany relation in jpa entity class using javafx listproperty
I tried this:
private final ListProperty<Bill> bills = new SimpleListProperty<>();
#OneToMany(targetEntity = Bill.class, mappedBy = "invoice")
public ObservableList getBills() {
return bills.get();
}
public void setBills(ObservableList value) {
bills.set(value);
}
public ListProperty billsProperty() {
return bills;
}
getting error : " oneTomany attribute type should be [java.util.colletion, java.util.List]
Simply use List rather than ObservableList from the getter and setter functions.
private final ListProperty<Bill> bills = new SimpleListProperty<>();
#OneToMany(targetEntity = Bill.class, mappedBy = "invoice")
public List getBills() {
return bills.get();
}
public void setBills(List value) {
bills.set(FXCollections.observableArrayList(value));
}
public ListProperty billsProperty() {
return bills;
}
I'm trying to store a table of data in a two dimensional collection.
Whenever I:
#OneToMany
public List<List<Cell>> cells;
I get a JPA error:
JPA error
A JPA error occurred (Unable to build EntityManagerFactory): Use of #OneToMany or #ManyToMany targeting an unmapped class: models.Table.cells[java.util.List]
Cell is a class that I created, it's basically a String decorator. Any ideas? I just need a two dimensional matrix that i can store.
#Entity public class Table extends Model {
#OneToMany
public List<Row> rows;
public Table() {
this.rows = new ArrayList<Row>();
this.save();
}
}
#Entity public class Row extends Model {
#OneToMany
public List<Cell> cells;
public Row() {
this.cells = new ArrayList<Cell>();
this.save();
}
}
#Entity public class Cell extends Model {
public String content;
public Cell(String content) {
this.content = content;
this.save();
}
}
As far as I know, #OneToMany only works with List of Entities. You are doing a List of a List, which is not an entity, so it fails.
Try to change the model to:
Table > Row > Cell
All of them via #OneToMany, so you can have your 2-dimensional structure but with Entities.
EDIT:
I believe your model declaration is not correct. Try this one:
#Entity public class Table extends Model {
#OneToMany(mappedBy="table")
public List<Row> rows;
public Table() {
this.rows = new ArrayList<Row>();
}
public Table addRow(Row r) {
r.table = this;
r.save();
this.rows.add(r);
return this.save();
}
}
#Entity public class Row extends Model {
#OneToMany(mappedBy="row")
public List<Cell> cells;
#ManyToOne
public Table table;
public Row() {
this.cells = new ArrayList<Cell>();
}
public Row addCell(String content) {
Cell cell = new Cell(content);
cell.row = this;
cell.save();
this.cells.add(cell);
return this.save();
}
}
#Entity public class Cell extends Model {
#ManyToOne
public Row row;
public String content;
public Cell(String content) {
this.content = content;
}
}
To create:
Row row = new Row();
row.save();
row.addCell("Content");
Table table = new Table();
table.save();
table.addRow(row);
I have the following mapping in my Play! app using JPA:
#Entity
public class Contact extends Model {
public String name;
#ManyToMany(mappedBy = "contacts", fetch = FetchType.EAGER)
public Set<Category> categories = new HashSet<Category>();
public void addCategory(Category c) {
this.categories.add(c);
if (!c.contacts.contains(this)) {
c.contacts.add(this);
}
}
#PreRemove
public void preRemove() {
for (Category c : this.categories) {
c.contacts.remove(this);
}
this.categories = null;
}
}
#Entity
public class Category extends Model {
public String name;
#ManyToMany
public Set<Contact> contacts = new HashSet<Contact>();
public void addContact(Contact c) {
this.contacts.add(c);
if (!c.categories.contains(this)) {
c.categories.add(this);
}
}
#PreRemove
protected void preRemove() {
for (Contact c : this.contacts) {
c.categories.remove(this);
}
this.contacts = null;
}
}
Deleting Category works fine, the relationship is updated correctly. If I delete a Contact however, I'm getting a constraint violation:
Caused by: org.h2.jdbc.JdbcBatchUpdateException: Referential integrity constraint violation: "FK34B085DF487AF903:
PUBLIC.CATEGORY_CONTACT FOREIGN KEY(CONTACTS_ID) REFERENCES PUBLIC.CONTACT(ID)"; SQL statement:
delete from Contact where id=? [23003-149]
How can I ensure that deleting a Contact will not delete the Category but will only remove the relationship?
EDIT: Duh! The issue was I also had a User object which had references to both Contact and Category. I missed clearing that relationship. The following is the change to preRemove() method in Contact class:
#PreRemove
public void preRemove() {
for (Category c : this.categories) {
c.contacts.remove(this);
}
this.user.contacts.remove(this);
for (Category c : this.user.categories) {
c.contacts.remove(this);
}
//It's important to save the user
this.user.save();
}
An other solution is to manually remove elements from the list and save it, before deleting the main entity :
while( !contact.categories.isEmpty() ) {
contact.categories.remove(0);
}
contact.save();
contact.delete();