3

I'm using Spring Boot, Spring Data REST, Hibernate, Spring JPA. I've a model like this:

 @TypeDefs({ @TypeDef(name = "json", typeClass = JsonStringType.class),
        @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) })
@EntityListeners({ AuditingEntityListener.class })
@MappedSuperclass
@Audited
public abstract class AbstractEntity extends AbstractPersistable<Long> {
    private static final long serialVersionUID = 1L;

    /* "UUID" and "UID" are Oracle reserved keywords -> "sid" */
    @Column(name = "sid", unique = true, nullable = false, updatable = false, length = 36)
    private String sid;

    @CreatedBy
    private String createdBy;

    @CreatedDate
    @Column(updatable = false)
    private Instant createdDate;

    @LastModifiedDate
    private Instant lastModifiedDate;

    @LastModifiedBy
    private String lastModifiedBy;

    // Trick to start version counting from 1 instead of 0
    @Version
    private long version = 1;

    public AbstractEntity() {
    }

    @PrePersist
    public void initializeUUID() {
        if (sid == null) {
            sid = UUID.randomUUID().toString();
        }
    }

    @Override
    @JsonIgnore
    @ApiModelProperty(hidden = true)
    public Long getId() {
        return super.getId();
    }

    @Override
    @JsonIgnore
    @ApiModelProperty(hidden = true)
    protected void setId(Long id) {
        super.setId(id);
    }

    public String getSid() {
        return sid;
    }

    public Instant getCreatedDate() {
        return createdDate;
    }

    public Instant getLastModifiedDate() {
        return lastModifiedDate;
    }

    public String getLastModifiedBy() {
        return lastModifiedBy;
    }

    public long getVersion() {
        return version;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (!super.equals(obj))
            return false;
        if (getClass() != obj.getClass())
            return false;
        AbstractEntity other = (AbstractEntity) obj;
        if (sid == null) {
            if (other.sid != null)
                return false;
        } else if (!sid.equals(other.sid)) {
            if (getId() == null) {
                if (other.getId() != null)
                    return false;
            } else {
                if (!getId().equals(other.getId()))
                    return false;
            }
        }

        return true;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = super.hashCode();
        result = prime * result + ((sid == null) ? 0 : sid.hashCode());
        return result;
    }

}



 @Entity
public class ParentEntity extends AbstractEntity {
    private String name;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "parentEntity")
    @OnDelete(action = OnDeleteAction.CASCADE)
    private List<NestedEntity> rows = new ArrayList<>();

}

@Entity
public class NestedEntity extends AbstractEntity {

    private String name;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private ParentEntity parentEntity;  
}

I'm trying to use the default POST method provided from SDR for my ParentEntity class. My repository is:

@Transactional
@PreAuthorize("isAuthenticated()")
public interface ParentEntityRepository extends PagingAndSortingRepository<ParentEntity, Long> {
}

I want to make a POST from the client (Angular, but I tried first using Swagger) sending both the ParentEntity and the nested object NestedEntity because I want the save happens in the same transaction.

So I send this Json:

    {   
  "name": "Test",  
  "_embedded": {
    "rows": [
      {
        "name": "Nested object"        
      }
    ]
  }
}

Unfortunately just the parent entity is saved on the database. I tried - just for test - to override the method save() in ParentEntityRepository in order to debug and see what is received. I see the rows list is empty. What's wrong with my code? Do you have some advice to understand where's my data are lost?

14
  • There is a good example of persisting nested objects here: stackoverflow.com/a/12642129/7992769 Commented Sep 28, 2017 at 16:23
  • @Kirsteen I'm already using cascade =CascadeType.ALL I forgot also to say my test case server side works fine. So it's not just a problem of JPA but rather - I guess - of Spring Data REST Commented Sep 28, 2017 at 16:30
  • Why do you use _embedded in your payload?.. Commented Sep 28, 2017 at 16:38
  • 1
    I can assume that it's happening due to a bidirectional association between your entities. Try to remove reference to ParentEntity from the child entity. Commented Sep 28, 2017 at 17:54
  • 1
    Yes )) I think it's enough to implement a setChildren method like this: public void setChildren(List<Child> children) { if (this.children != null) { this.children.forEach(child -> child.setParent(null)); } if (children != null) { children.forEach(child -> child.setParent(this)); } this.children = children; } Commented Sep 28, 2017 at 18:08

1 Answer 1

3

Just turn off exporting of the NestedEntityRepository (or remove it):

@RepositoryRestResource(exported = false)
public interface NestedEntityRepository extends JpaRepository<NestedEntity, UUID> {
}

And provide synchronization of your bidirectional associations between two entities. For example like this:

public class ParentEntity {

    // ...

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "parentEntity")
    private List<NestedEntity> rows;

    public void setRows(List<NestedEntity> rows) {
        if (this.rows != null) {
            this.rows.forEach(row -> row.setParentEntity(null));
        }
        if (rows != null) {
            rows.forEach(row -> row.setParentEntity(this));
        }
        this.rows = rows;
    }  
}

Or just turn you association to unidirectional.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.