Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Save nested objects with Spring Data REST

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?

like image 837
drenda Avatar asked Sep 14 '26 18:09

drenda


1 Answers

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.

like image 188
Cepr0 Avatar answered Sep 16 '26 17:09

Cepr0



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!