Hi i am learning Spring JPA using OneToMany and ManyToOne bidirectional relationship, in some example i see OneToMany and ManyToOne relationship when i written in two side, the JPA add a new column as the foreign key column and insert the key value from the Parent table. But when i try mine, the column is always blank. Here is how my code looked like :
Here is my Account.java model :
@Entity
@Table(name = "msAccount")
public class Account {
@Id
@NotBlank(message = "Not Blank")
@Size(min = 0, max = 20)
public String accountId;
@NotBlank(message = "Not Blank")
public String accountName;
@NotBlank(message = "Not Blank")
@Email(message = "Should be the right email")
public String accountEmail;
@NotBlank(message = "Not Blank")
@Size(min = 5, message = "Minimal 5 char")
public String accountAddress;
@NotBlank(message = "Not Blank")
public String town;
@NotBlank(message = "Not Blank")
public String npwp;
@NotBlank(message = "Not Blank")
public String phoneNumber;
public String fax;
public String remarks;
@NotNull
public Date entryTime;
@NotNull
public Boolean active;
@OneToMany(mappedBy="account", cascade = CascadeType.ALL, orphanRemoval = true)
public List<Dealer> dealer;
//getter setter skipped
}
and here is my Dealer.java model :
@Entity
@Table(name = "msDealer")
public class Dealer {
@Id
@NotBlank(message = "Tidak Boleh Kosong")
@Size(min = 0, max = 20)
public String dealerId;
@NotBlank(message = "Tidak Boleh Kosong")
public String dealerName;
@NotBlank(message = "Tidak Boleh Kosong")
@Email(message = "Masukkan Email yang bener")
public String dealerEmail;
@NotBlank(message = "Tidak Boleh Kosong")
@Size(min = 5, message = "Minimal 5 karakter")
public String dealerAddress;
@ManyToOne(fetch = FetchType.LAZY)
public Account account;
//getter setter skipped
}
and here is my Repository :
@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
}
and here is my Service :
@Service
public class AccountService {
@Autowired
private AccountRepository accountRepository;
public Account save(Account account) {
return accountRepository.save(account);
}
}
and here is my controller :
@RestController
@RequestMapping("/api/account")
public class AccountController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final int ROW_PER_PAGE = 10;
@Autowired
private AccountService accountService;
@PostMapping("/new")
public ResponseEntity<Account> addAccount(@Valid @RequestBody Account account) {
try {
Account newAccount = accountService.save(account);
return ResponseEntity.created(new URI("/api/account/" + newAccount.getAccountId()))
.body(account);
} catch(Exception ex) {
logger.error(ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
}
}
then i post the JSON into my save endpoint :
{
"accountId": "USA001",
"accountName": "string",
"accountEmail": "string",
"accountAddress": "string",
"town": "string",
"npwp": "string",
"phoneNumber": "string",
"fax": "string",
"remarks": "string",
"entryTime": "2020-04-07T15:01:29.404Z",
"active": true,
"dealer": [
{
"dealerId": "MMO001",
"dealerName": "string",
"dealerEmail": "string",
"dealerAddress": "string"
}
]
}
and when i save it the hibernate that showed up in my terminal looked inserting query into that 2 table, but when i check my database table (which is postgresql) i found there is a field "account_account_id" that is null, what did i miss here?
i want the Hibernate run sql like this :
insert into account (account_id, account_name, ...etc)
values ('USA001', 1)
insert into dealer (account_account_id, dealer_name, dealer_id, ...etc)
values ('USA001', 'New dealer 1', 'MMO001')
Here is my UPDATED Model after some try :
my Account.java I delete cascade = CascadeType.ALL, orphanRemoval = true
@Entity
@Table(name = "msAccount")
public class Account {
@Id
@NotBlank(message = "Tidak Boleh Kosong")
@Size(min = 0, max = 20)
public String accountId;
@NotBlank(message = "Tidak Boleh Kosong")
public String accountName;
@NotBlank(message = "Tidak Boleh Kosong")
@Email(message = "Masukkan Email yang bener")
public String accountEmail;
@NotBlank(message = "Tidak Boleh Kosong")
@Size(min = 5, message = "Minimal 5 karakter")
public String accountAddress;
@NotBlank(message = "Tidak Boleh Kosong")
public String town;
@NotBlank(message = "Tidak Boleh Kosong")
public String npwp;
@NotBlank(message = "Tidak Boleh Kosong")
public String phoneNumber;
public String fax;
public String remarks;
@NotNull
public Date entryTime;
@NotNull
public Boolean active;
@OneToMany(mappedBy="account")
// @JoinColumn(name = "accountId")
public List<Dealer> dealer;
//getter setter skipped
}
and here is my Dealer.java. Added @JoinColumn :
@Entity
@Table(name = "msDealer")
public class Dealer {
@Id
@NotBlank(message = "Tidak Boleh Kosong")
@Size(min = 0, max = 20)
public String dealerId;
@NotBlank(message = "Tidak Boleh Kosong")
public String dealerName;
@NotBlank(message = "Tidak Boleh Kosong")
@Email(message = "Masukkan Email yang bener")
public String dealerEmail;
@NotBlank(message = "Tidak Boleh Kosong")
@Size(min = 5, message = "Minimal 5 karakter")
public String dealerAddress;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "account_id")
public Account account;
//getter setter skipped
}
now the error is getting weird, i got this error when i save the JSON data
> "Unable to find com.api.b2b.Model.Dealer with id MMO001; nested
> exception is javax.persistence.EntityNotFoundException: Unable to find
> com.api.b2b.Model.Dealer with id MMO001"
in some tutorial it worked, but mine is not, what did i do wrong?
here is my github repo : https://github.com/Fly-Away/LearningSpring
You're missing the @JoinColumn
on the child side:
@Entity
@Table(name = "ms_dealer")
public class Dealer {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "account_account_id")
public Account account;
// other fields
}
You have used mappedBy
on the parent side, but there is no mapping on the child side. You need to indicate, that the Dealer
is the relationship owner - it has the foreign key.
Edit: if you're persisting (not merging) the Account
entity, together with its children, you should not pass ids of child entities. (Actually passing any ids upon persist is a code smell and most probably a performance killer.) The json used should look like:
{
"accountName": "string",
"accountEmail": "string",
"accountAddress": "string",
"town": "string",
"npwp": "string",
"phoneNumber": "string",
"fax": "string",
"remarks": "string",
"entryTime": "2020-04-07T15:01:29.404Z",
"active": true,
"dealer": [
{
"dealerName": "string",
"dealerEmail": "string",
"dealerAddress": "string"
}
]
}
Before saving both-side synchronization might also be needed:
account.getDealer().forEach(d -> d.setAccount(account));
Edit:
From Author
edits must cascade to child:
@OneToMany(mappedBy = "account", cascade = CascadeType.ALL, orphanRemoval = true)
public List<Dealer> dealer;
You might also add @JsonIgnore
over Action
or List<Dealer>
to avoid stackoverflow on serialization to json.