OneToOne | Bi directional | Optional - gauravsk89/hibernate GitHub Wiki
Scenario: OneToOne Bi-directional Optional
we have a JOIN Table named like ITEM_BUYER with two columns ITEM_ID FK TO ITEM Table UNIQUE [ an item can be bought only once] BUYER_ID FK TO USER Table
Item entity will have reference to User entity (buyer). User entity will have a reference back to Item entity (bought item).
since it's a bi-directional association, I have kept the Item.buyer end of association responsible for synchronizing with DB and the other side i.e. User.boughtItem is the inverse side.
That means if we only call user.setBoughtItem(item) it wont trigger any DB call. To make DB call we need to call item.setBuyer(user);
During persisting this relation it does not affect the operation/sql generation whether I call user.setBoughtItem(item); or not. I guess the reason is, since this association is not FK based (in that case we need to set that key otherwise CAN NOT SET NULL exception will be thrown). While in this case the association is stored in Join Table and since Item.buyer end of association is responsible for DB synchronization so only calling item.setBuyer(user); provides both the required values 1. ITEM_ID from PK of Item entity (it is available since OneToOne association is defined inside Item entity so it picks up the PK of Item entity). 2. BUYER_ID is picked up from the PK of user entity (buyer) reference given as part of OneToOne association in the Item entity
due to this is works fine and does not throw any exception like CAN NOT SET NULL, even if user.setBoughtItem(item); is not called.
@Entity @Table(name = "ITEMS") @Data @NoArgsConstructor @AllArgsConstructor @Builder @EqualsAndHashCode(of = {"itemId", "name"}, callSuper = false) @ToString public class Item {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ITEM_ID_SEQ")
@SequenceGenerator(name= "ITEM_ID_SEQ",
sequenceName="ITEM_ID_SEQ",
allocationSize = 1)
@Column(name = "ITEM_ID")
@JsonIgnore
private Long itemId;
@Column(name = "NAME")
private String name;
@Column(name = "INITIAL_AMOUNT")
private BigDecimal initialAmount;
// oneToOne uni-directional (non-optional)
@OneToOne
@JoinColumn(name = "SELLER_ID")
private User seller;
// oneToOne uni-directional (optional)
@OneToOne
@JoinTable(name = "ITEM_BUYER",
joinColumns = @JoinColumn(name = "ITEM_ID_PK"/*, referencedColumnName = "itemId"*/),
inverseJoinColumns = @JoinColumn(name = "BUYER_ID"/*, referencedColumnName = "id"*/))
private User buyer;
// oneToOne bi-directional (using FK)
@OneToOne(mappedBy = "item")
private ShippingDetails shippingDetails;
}
@Data @NoArgsConstructor @AllArgsConstructor @ToString//(exclude = {"address", "phoneSet"}) @EqualsAndHashCode(exclude = {"phoneSet", "address"}) public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USR_ID_SEQ")
@SequenceGenerator(name = "USR_ID_SEQ",
sequenceName="USR_ID_SEQ",
allocationSize = 1)
@Column(name = "ID")
@JsonIgnore
private Long id;
@Column(name = "FIRST_NAME")
private String firstName;
@Column(name = "LAST_NAME")
private String lastName;
@Column(name = "EMAIL_ID")
private String emailId;
// one to one
// unidirectional ( user -> address)
// parent/child relationship with cascade all (existence of address object does not make sense without its user object)
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "ADDR_ID")
private Address address;
// one to many (a user can have one or more phone numbers)
// bi-directional
// parent/child (and Phone side is responsible to update the relationship)
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Phone> phoneSet = new HashSet<>();
@OneToOne(mappedBy = "buyer")
@JoinTable(name = "ITEM_BUYER",
joinColumns = @JoinColumn(name = "BUYER_ID"),
inverseJoinColumns = @JoinColumn(name = "ITEM_ID_PK"))
private Item boughtItem;
}
@Override @Transactional public void addBuyerToItem(ItemDTO itemDto) {
Item item = itemsRepository.findOne(itemDto.getItemId());
User user = userRepository.findOne(itemDto.getBuyerId());
item.setBuyer(user);
user.setBoughtItem(item);
itemsRepository.save(item);
}