Hi I'm pretty new to Java and Spring Boot. I have an API which uses 3 entities:
@Entity
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(updatable = false, nullable = false)
int id;
// Other fields
@Column(nullable = false)
String accountId;
@OneToMany(mappedBy = "account", cascade = CascadeType.ALL)
@JsonBackReference
List<StockMarketTransaction> transactions;
@ManyToMany
@JsonBackReference
Set<StockMarketSubject> currentlyOwnedSubjects;
public int getId() {
return id;
}
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public List<StockMarketTransaction> getTransactions() {
return transactions;
}
public void setTransactions(List<StockMarketTransaction> transactions) {
this.transactions = transactions;
}
public void addTransaction(StockMarketTransaction transaction){
this.transactions.add(transaction);
}
public Set<StockMarketSubject> getCurrentlyOwnedSubjects() {
return currentlyOwnedSubjects;
}
public void setCurrentlyOwnedSubjects(Set<StockMarketSubject> currentlyOwnedSubjects) {
this.currentlyOwnedSubjects = currentlyOwnedSubjects;
}
}
@Entity
public class StockMarketSubject{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
// Other fields
@OneToMany(mappedBy = "subject")
@JsonBackReference
List<StockMarketTransaction> transactions;
@ManyToMany
@JsonBackReference
Set<Account> holdingAccounts;
public int getId() {
return id;
}
public List<StockMarketTransaction> getTransactions() {
return transactions;
}
public void setTransactions(List<StockMarketTransaction> transactions) {
this.transactions = transactions;
}
public void addTransaction(StockMarketTransaction transaction){
this.transactions.add(transaction);
}
public Set<Account> getHoldingAccounts() {
return holdingAccounts;
}
public void setHoldingAccounts(Set<Account> holdingAccounts) {
this.holdingAccounts = holdingAccounts;
}
}
@Entity
public class StockMarketTransaction{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
// Other fields
@ManyToOne
@JsonManagedReference
StockMarketSubject subject;
@ManyToOne(cascade = CascadeType.ALL)
@JsonManagedReference
Account account;
public Long getId() {
return id;
}
public StockMarketSubject getSubject() {
return subject;
}
public void setSubject(StockMarketSubject subject) {
this.subject = subject;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
I'm also using DTO to transfer data between service layer (fetch/create/update) and controllers. GET endpoints return DTO structure with data and POST endpoints use DTO as @RequestBody
in order to capture user data sent via request.
For Transaction entity I use this DTO with MapStruct Mapper:
public class StockMarketTransactionDTO {
@Schema(accessMode = Schema.AccessMode.READ_ONLY)
Long id;
// Other fields
@NotEmpty
@PositiveOrZero
int subjectId;
@NotEmpty
@PositiveOrZero
int accountId;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public int getSubjectId() {
return subjectId;
}
public void setSubjectId(int subjectId) {
this.subjectId = subjectId;
}
public int getAccountId() {
return accountId;
}
public void setAccountId(int accountId) {
this.accountId = accountId;
}
public StockMarketTransactionDTO(){}
}
@Mapper
public interface StockMarketTransactionMapper {
StockMarketTransactionMapper INSTANCE = Mappers.getMapper(StockMarketTransactionMapper.class);
@Mapping(source = "subject.id", target = "subjectId")
@Mapping(source = "account.id", target = "accountId")
StockMarketTransactionDTO stockMarketTransactionToStockMarketTransactionDTO(StockMarketTransaction transaction);
@InheritInverseConfiguration
StockMarketTransaction stockMarketTransactionDTOToStockMarketTransaction(StockMarketTransactionDTO dto);
}
Insted of exchanging whole objects related to transaction I want to only attach their ID (PK) to DTO. Now when I use this mapper to create transaction DTO from entity and return it with response it works perfectly fine. It gives users a valid ID of related subject and account. But the issue I have is creating entity from DTO filled with user data. With this payload:
{
"transactionDate": "2022-12-13T17:15:47.023Z",
"pricePerOne": 20,
"volume": 20,
"operationType": "BUY",
"description": {
"advantages": "string",
"disadvantages": "string",
"reason": "string"
}, // Other fields
"subjectId": 1,
"accountId": 1
}
I get internal error:
org.hibernate.PropertyValueException: not-null property references a null or transient value : com.rcbg.afku.investmentdiary.brokeraccounts.entities.Account.accountId
I tried find any efficient and proper way to map object like this with MapStruct but I failed. Is there any possibility to handle this with @Mapping annotation, or should I create my own implementation of mapping method?