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?
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.