0

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?

aFku
  • 37
  • 2
  • 7

0 Answers0