I am working on a small app where people can like posts they like, but I can't manage to do it. The backend sent a "Not-null property references a transient value" error which I tried to solve by adding "cascade = CascadeType.All" in the entities causing the issue, but when the data is saved, it also saves new items in child tables. To be more precise, I have this Thumbsup item to save, which has 3 columns (and all 3 are foreign keys) : fk_account, fk_merch and fk_post. When saving the Thumbsup, it creates a Merch item and then saves its id in fk_merch, which I do not want here.
Here is my thumbsup.java :
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Thumbsup {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int thumbsupId;
@ManyToOne
@JoinColumn(name = "fk_post", referencedColumnName = "postId")
private Post post;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "fk_merch", nullable = true, referencedColumnName = "merchId")
private Merch merch;
@ManyToOne
@JoinColumn(name = "fk_account", nullable = false, referencedColumnName = "accountId")
private Account account;
public static class Builder {
int thumbsupId;
Post post;
Merch merch;
Account account;
public Builder setThumbsupId(int thumbsupId) {
this.thumbsupId = thumbsupId;
return this;
}
public Builder setPost(Post post) {
this.post = post;
return this;
}
public Builder setMerch(Merch merch) {
this.merch = merch;
return this;
}
public Builder setAccount(Account account) {
this.account = account;
return this;
}
public Thumbsup build() {
return new Thumbsup(thumbsupId, post, merch, account);
}
}
}
merch.java :
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Merch {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int merchId;
@NotNull
private String itemname;
@NotNull
private double itemprice;
private boolean active;
private Date creation_date;
private Date update_date;
private Date deletion_date;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "fk_artist", nullable = false, referencedColumnName = "artistId")
private Artist artist;
public static class Builder {
int merchId;
String itemname;
double itemprice;
boolean active;
Date creation_date = new Date();
Date update_date;
Date deletion_date;
Artist artist;
public Builder setMerchId(int merchId) {
this.merchId = merchId;
return this;
}
public Builder setItemname(String itemname) {
this.itemname = itemname;
return this;
}
public Builder setItemprice(double itemprice) {
this.itemprice = itemprice;
return this;
}
public Builder setActive(boolean active) {
this.active = active;
return this;
}
public Builder setCreation_date(Date creation_date) {
this.creation_date = creation_date;
return this;
}
public Builder setUpdate_date(Date update_date) {
this.update_date = update_date;
return this;
}
public Builder setDeletion_date(Date deletion_date) {
this.deletion_date = deletion_date;
return this;
}
public Builder setArtist(Artist artist) {
this.artist = artist;
return this;
}
public Merch build() {
return new Merch(merchId, itemname, itemprice ,active, creation_date, update_date, deletion_date, artist);
}
}
}
post-structure.component.ts :
export class PostStructureComponent implements OnInit {
@Input() post!: Post;
credential!: Credential;
accountDetail!: Account;
artist!: Artist;
selectedPost!: Post;
thumbsup!: Thumbsup;
thumbsups!: Thumbsup[];
postForm!: FormGroup;
constructor(public artistService: ArtistService,
public postService: PostService,
public thumbsupService: ThumbsupService,
public accountService: AccountService,
public auth: AuthService) {
}
ngOnInit(): void {
this.auth.me().subscribe((response: ApiResponse) => {
this.credential = CredentialHelper.credentialFromDto(response.data as CredentialDto);
this.accountService.getDetail(this.credential.account.accountId.toString()).subscribe((account: Account) => {
this.accountDetail = account;
this.artistService.getDetail(this.accountDetail.accountId.toString()).subscribe((artist: Artist) => {
this.artist = artist;
this.thumbsupService.getListByPost(this.post.postId.toString()).subscribe((thumbsups: Thumbsup[]) => {
this.thumbsups = thumbsups;
this.thumbsups.forEach(thumbsup => {
if (thumbsup.account.accountId == this.accountDetail.accountId){
this.thumbsup = thumbsup;
}
})
})
})
})
})
}
onLike():void {
let newThumbsup = {
thumbsupId: 0,
account: this.accountDetail,
merch: MerchHelper.empty(),
post: this.post
}
if (this.thumbsup == undefined ||this.thumbsup.thumbsupId == 0){
this.thumbsupService.create(ThumbsupHelper.returnCreatePayload(newThumbsup)).subscribe();
} else {
this.thumbsupService.deleteThumbsup(this.thumbsup.thumbsupId.toString()).subscribe();
}
}
}
thumbsup.helper.ts :
export class ThumbsupHelper {
public static empty(): Thumbsup {
return {
thumbsupId: 0,
account: AccountHelper.empty(),
merch: MerchHelper.empty(),
post: PostHelper.empty()
}
}
public static returnCreatePayload(thumbsup: Thumbsup): ThumbsupCreatePayload {
return {
account: thumbsup.account,
merch: thumbsup.merch,
post: thumbsup.post
}
}
I know that there are several and serious issues in my Angular code, such as getting credential, accountDetail and artist this way but I plan to modify this later, as well as . Also, I think that I should use Observables to manage data changes on thumbsup and evaluate if it exists or not to decide which method to call, but I have difficulties with it.
If anything is missing, I'll add it as soon as possible.
CodePudding user response:
If you want to refer to entities that already exist, you will have to load them via EntityManager.find(Class entityClass, Object id)
or get a proxy reference to the object via EntityManager.getReference(Class entityClass, Object id)
.
So instead of using CascadeType.ALL
in Thumbsup
, you will have to set the fields of the associations merch
, post
and account
with the object returned by find
or getReference
.