Relationships between tables
Next, we create a new table called owner that has a one-to-many relationship with the car table. The owner can own multiple cars, but a car can have only one owner. The following UML diagram shows the relationship between the tables:
The following are the steps for creating a new table:
- First, we create the Owner entity and repository in the domain package. The Owner entity and repository are created similarly to what we did with the Car class. The following is the source code of the Owner entity class and OwnerRepository:
// Owner.java
package com.packt.cardatabase.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Owner {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private long ownerid;
private String firstname, lastname;
public Owner() {}
public Owner(String firstname, String lastname) {
super();
this.firstname = firstname;
this.lastname = lastname;
}
public long getOwnerid() {
return ownerid;
}
public void setOwnerid(long ownerid) {
this.ownerid = ownerid;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
// OwnerRepository.java
package com.packt.cardatabase.domain;
import org.springframework.data.repository.CrudRepository;
public interface OwnerRepository extends CrudRepository<Owner, Long>
{
}
- In this phase, it is good to check that everything is done correctly. Run the project and check that both database tables are created and that there are no errors in the console. The following screenshot shows the console messages when tables are created:
Now, our domain package contains two entity classes and repositories:
- The one-to-many relationship can be added by using the @ManyToOne and @OneToMany annotations. In the car entity class, which contains a foreign key, you will define the relationship with the @ManyToOne annotation. Also, add the getter and setter for the owner field. It is recommended using FetchType.LAZY for all associations. For toMany relationships, that is the default value, but for toOne relationships, you should define it. FetchType defines the strategy for fetching data from the database. The value can be either EAGER or LAZY. In our case, the lazy strategy means that when the owner is fetched from the database, all the cars associated with the owner will be fetched when needed. Eager means that the cars will be fetched immediately with the owner. The following source code shows how to define a one-to-many relationship in the Car class:
// Car.java
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "owner")
private Owner owner;
//Getter and setter
public Owner getOwner() {
return owner;
}
public void setOwner(Owner owner) {
this.owner = owner;
}
In the owner entity site, the relationship is defined with the @OneToMany annotation. The type of the field is List<Car> because the owner may have multiple cars. Add also the getter and setter for that:
// Owner.java
@OneToMany(cascade = CascadeType.ALL, mappedBy="owner")
private List<Car> cars;
//Getter and setter
public List<Car> getCars() {
return cars;
}
public void setCars(List<Car> cars) {
this.cars = cars;
}
The @OneToMany annotation has two attributes that we are using. The cascade attribute defines how cascading affects the entities. The attribute setting ALL means that if the owner is deleted, the cars linked to that owner are deleted as well. The mappedBy="owner" attribute setting tells us that the Car class has the owner field, which is the foreign key for this relationship.
When you run the project, you can see from the console that the relationship is now created:
- Now, we can add some owners to the database with CommandLineRunner. Let's also modify the Car entity classes constructor and add an owner there:
// Car.java constructor
public Car(String brand, String model, String color,
String registerNumber, int year, int price, Owner owner) {
super();
this.brand = brand;
this.model = model;
this.color = color;
this.registerNumber = registerNumber;
this.year = year;
this.price = price;
this.owner = owner;
}
- We first create two owner objects and save these to the database. For saving the owners, we also have to inject the OwnerRepository into the main class. Then we connect the owners to the cars by using the Car constructor. The following is the source code of the application main class CardatabaseApplication:
@SpringBootApplication
public class CardatabaseApplication {
// Inject repositories
@Autowired
private CarRepository repository;
@Autowired
private OwnerRepository orepository;
public static void main(String[] args) {
SpringApplication.run(CardatabaseApplication.class, args);
}
@Bean
CommandLineRunner runner() {
return args -> {
// Add owner objects and save these to db
Owner owner1 = new Owner("John" , "Johnson");
Owner owner2 = new Owner("Mary" , "Robinson");
orepository.save(owner1);
orepository.save(owner2);
// Add car object with link to owners and save these to db.
Car car = new Car("Ford", "Mustang", "Red",
"ADF-1121", 2017, 59000, owner1);
repository.save(car);
car = new Car("Nissan", "Leaf", "White",
"SSJ-3002", 2014, 29000, owner2);
repository.save(car);
car = new Car("Toyota", "Prius", "Silver",
"KKO-0212", 2018, 39000, owner2);
repository.save(car);
};
}
}
If you now run the application and fetch cars from the database, you can see that the owners are now linked to the cars:
If you want to create many-to-many relationship instead, which means, in practice, that an owner can have multiple cars and a car can have multiple owners, you should use the @ManyToMany annotation. In our example application, we will use a one-to-many relationship, but there follows an example of how to change the relationship to many-to-many. In a many-to-many relationship, it is recommended using Set instead of List with hibernate:
- In the Car entity class many-to-many relationship, define the getters and setters in the following way:
@ManyToMany(mappedBy = "cars")
private Set<Owner> owners;
public Set<Owner> getOwners() {
return owners;
}
public void setOwners(Set<Owner> owners) {
this.owners = owners;
}
In the owner entity, the definition as follows:
@ManyToMany(cascade = CascadeType.MERGE)
@JoinTable(name = "car_owner", joinColumns = { @JoinColumn(name =
"ownerid") }, inverseJoinColumns = { @JoinColumn(name = "id") })
private Set<Car> cars = new HashSet<Car>(0);
public Set<Car> getCars() {
return cars;
}
public void setCars(Set<Car> cars) {
this.cars = cars;
}
- Now, if you run the application, there will be a new join table that is created between the car and owner tables. The join table is defined by using the @JoinTable annotation. With the annotation, we can set the name of the join table and join columns. The following is a screenshot of the database structure when using a many-to-many relationship: