me1111
me1111

Reputation: 1157

Updating entity with onetomany connection without ids in children entities

I have the following structure: tables "parents" and "children", which are binded by parents.id = children.parent_id

Application reads data from these tables into the object of the following class:

public class Parent {
    private int id;
    private Map<String, String> children;
    ....
}

Then application changes data in it. What I need to do is to apply changes to existing records. I am not allowed to change Parent class or database structure. And I am trying to do this by using jpa repository by the following way:

My entities

@Repository
public interface ParentsDao extends JpaRepository<ParentEntity, Integer> {
}

@Entity
@Table(name = "parents")
public class ParentEntity {
    @Id
    @GeneratedValue
    @Column(name = "id", updatable = false, insertable = false)
    private Integer id;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name="parent_id")
    private Set<ChildEntity> children;
    ...
}

@Entity
@Table(name = "children")
public class ChildEntity {
    @Id
    @GeneratedValue
    @Column(name = "id", updatable = false, insertable = false)
    private Integer id;

    @ManyToOne
    @JoinColumn(name="parent_id", updatable = false)
    private ScheduledReportsEntity scheduledReport;

    @Column(name = "child_name")
    private String name;

    @Column(name = "child_value")
    private String value;
    ...
}

The following is my sevice, where action() starts the whole thing: converts Parent to ParentEntity and saves it

public class ParentServiceImpl {
        @Autowired
        ParentsDao parentsDao;

        public 

void action(Parent parent) {
            ParentEntity entity = convert(parent);  // moving data from Parent to ParentEntity object
            parentsDao.save(entity); //here I receive exception
        }
        private ParentEntity convert(Parent parent) {

        Set<ChildEntity> children = new HashSet<ChildEntity>();
        for(Map.Entry<String, String> entry : simpleObj.getChildren().entrySet()){
            ChildEntity childEntity = new ChildEntity();
            childEntity.setName(entry.getKey());
            childEntity.setValue(entry.getValue());
            children.add(entity);
        }

        ParentEntity entity = new ParentEntity();
        entity.setId(parent.getId());
        entity.setChildren(children);

        return entity;
    }
}

But I am receiving the following error:

com.app.impl.ParentServiceImpl: org.springframework.dao.DataIntegrityViolationException: 
could not insert: [com.app.entities.ChildEntity]; 
SQL [insert into scheduled_report_params (child_name, child_value, parent_id) values (?, ?, ?)]; 
constraint [null]; 
nested exception is org.hibernate.exception.ConstraintViolationException: could not insert: [com.app.entities.ChildEntity]

I have 2 questions: 1. I think this is connected to missing ids in children, am I right? 2. If the reason is missing ids, then are there any way to get around it somehow and provide save by other fields? Will be greatfull for any advice. Thanks

Upvotes: 0

Views: 3834

Answers (1)

Serge Ballesta
Serge Ballesta

Reputation: 148880

You have a design problem.

You are right, if id is the primary key for child entity, it must be present in all pre-existent (not new) ChildEntity elements from the children Set of a ParentEntity element if you modify it. If not, Hibernate will try to insert them.

So you must choose between two solutions :

  • use a generated primary key in ChildEntity and find a way to keep it in your application. For example, you could use ParentEntity as the real model class with Parent being a view on it :

    class Parent {
        private ParentEntity inner;
        public Parent(ParentEntity entity) {
            inner = entity;
            // initialize children or have getChildren to dynamically create it
            // from inner.childre
            ...
        }
    ...
    }
    
  • use child name as the primay key. The ChildEntity would look like :

    @Entity
    @Table(name = "children")
    public class ChildEntity {
        @Id
        @Column(name = "child_name")
        private String name;
    
        @Column(name = "child_value")
        private String value;
    
        @ManyToOne
        @JoinColumn(name="parent_id", updatable = false)
        private ScheduledReportsEntity scheduledReport;
        ...
    }
    

But you cannot simply forget a primary key in your application.

Upvotes: 2

Related Questions