Home > front end >  Hibernate association mapping
Hibernate association mapping

Time:12-20

I am working on a bike rental system project. I am able to do CRUD operations via Rest API on Bike, and Customer and create a Contract object. For this, I have exposed CRUD endpoints(s) for

  • /bike
  • /customer
  • /contract

The POST /contract/final endpoint should return a complete contract overview with contractid, bike details, and customer

The association I am using is as below

class Contract {

@Id
@GeneratedValue(strategy= GenerationType.SEQUENCE)
private id;
@ManyToOne(fetch=FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name="customerId")
private Customer customer;
@ManyToOne(fetch=FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name="bikeId")
private Bike bike; 
}

class Customer{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "customer_id")
private Long customerId;
private String firstName;
}

class Bike{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "bike_id")
private long bikeId;
private String model;
}

So I just want to be able to create/modify the bike|customer records and create a contract with the already created customer(s) and bike(s).

As of now, the contract request will create the customer and bike records apart from records created separately using /customer, /bike endpoints - which I do not want.

Is the association mapping I have used correct for this requirement? What should ideally be the relationship between these entities given the below contraints?

  • One customer can have many contracts but one contract will have only one customer

  • One bike can have many contracts but one contract will have only one bike

    How can I make use of the existing customer/bike records while creating a contract?

CodePudding user response:

  1. Do you want to have the ability to change a Bike model when you create a new Contract? Obviously no! cascade = CascadeType.ALL is about such changes. So throw it away.

  2. Do you want to load all the Contract data, like a Bike, when you load a Contract? It is not so obvious, but my advice don't do this! Let's change fetch=FetchType.EAGER to fetch=FetchType.LAZY.

  3. You use two strategies to generate an idstrategy= GenerationType.SEQUENCE, strategy = GenerationType.IDENTITY. Use only one.

  4. Don't use a primitive type for an id. Change long bikeId to Long bikeId.

  5. @JoinColumn specifies a database column name, not a class property. Change @JoinColumn(name="customerId") to @JoinColumn(name="CUSTOMER_ID"). Use uppercase for column names.

class Contract {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "CUSTOMER_ID")
    private Customer customer;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "BIKE_ID")
    private Bike bike;
    
}

Assign existing Bike and Customer to the Contract, using getReferenceById() method. It doesn't hit a database at all.

Customer customer = customerRepository.getReferenceById(customerId);
contract.setCustomer(customer);

Another way is to use a transient customer.

Customer customer = new Customer();
customer.setId(customerId);
contract.setCustomer(customer);

But standard repository save() method uses merge(). So it will be an additional SQL request to load a customer. You can use custom repository with pure save() method to avoid this https://vladmihalcea.com/best-spring-data-jparepository/

  • Related