Prelude

In the previous post, I navigated the basic settings for testing JPA (https://tlonist-sang.github.io/jpa-1/)[https://tlonist-sang.github.io/jpa-1/]. In this post, I will explain how the relations Java objects have are translated to Database tables and more.

1. Relations

  • It should be made clear once again that ORM is a trial to navigate persisted data as if one manipulates the Collection API of Java.
  • For RDBMS, setting up a primary key for one table and referencing it through a foreign key makes two tables bi-directional. Whereas for objects, you have to make each of the object to contain the other, like below.
class A{ B b;}
class B{ A a;}
  • For objects in java, the bi-direnctional referencing (A.b or B.a) is actually double mono-directional referencing. Hence to transplant this to the database, one has to have the foreign key of the other, and from here the concept of owning side of a relationship is created.

1-0. Owning Side of a relationship

  • Suppose a project has a class Account, and a class Team that contains multiple accounts.
  @Entity
  public class Account extends BaseEntity{
      @Id @GeneratedValue
      @Column(name="ACCOUNT_ID")
      private Long id;

      @ManyToOne
      @JoinColumn(name="TEAM_ID")
      private Team team;
  }

  @Entity
  public class Team extends BaseEntity{
      @Id @GeneratedValue
      @Column(name="TEAM_ID")
      private Long id;

  //    @OneToMany (mappedBy="team")
  //    List<Account> accounts = new ArrayList<>();
  }
  • Thinking how this structure should be stored in database, you can think of two options.

1) Account contains the foreign key of team.

Account Team
primary key account_id; foreign key team_id; primary key team_id;

2) Team contains the foreign keys of members

Account Team
primary key account_id; primary key team_id; foreign key account_id;
  • If you think just for a moment, you will find out that 2) method is very undesirable, because if team contains 10,000 accounts, it will have 10,000 primary keys of account_id. For 1), no matter how big a team is, one account will have one team(let’s say for commonsense’s sake), hence it is logical for Account to have the foreign key of the team.

  • We call the entity which holds the foreign key to be owning side of a relationship. In this case, it is Account.team. The other side has to be noted with mappedBy option for bi-direnctional referencing.

@Entity
public class Team extends BaseEntity{
    @Id @GeneratedValue
    @Column(name="TEAM_ID")
    private Long id;

   @OneToMany (mappedBy="team")
   List<Account> accounts = new ArrayList<>();
}
  • From the example above, class Team is read only when it comes to dealing with accounts array. Common mistakes are,
    Team team = new Team();
    team.setName("Team1");
    entityManager.persist(team);
    
    Account account = new Account();
    account.setName("eric");
    
    team.getAccounts().add(account);
    entityManager.persist(account);
    
  • The above code will bring the result like below. img

When mapping a bi-directional relation, the insert has to be made on the owning side of the relationship. ```java Team team = new Team(); team.setName(“Team1”); entityManager.persist(team);

Account account = new Account(); account.setName(“eric”); team.getAccounts().add(account);

account.setTeam(team); entityManager.persist(account);

[![img](https://tlonist-sang.github.io/assets/img/jpa2-2.png)](https://tlonist-sang.github.io/assets/img/jpa2-2.png)

- It is generally recommended to modify the getter & setter for Team to include these aspects.
```java
@Entity
public class Team extends BaseEntity{
   //...
   
   @OneToMany (mappedBy="team")
   List<Account> accounts = new ArrayList<>();

   public void addAccount(Account account){
      accounts.add(account);
      account.setTeam(this);
   }
}

1-1. @ManyToOne, @OneToMany

  • @ManyToOne is the most frequently used relation mapping. As noted eariler, the one with FK(foreign key) is the owning side of a relationship.

1-2. @OneToOne

  • @OneToOne is quite similar to @ManyToOne in that the one with FK is the owning side of the relationship. Rest is plain.

1-3. @ManyToMany

  • Unless really absolutely needed, @ManyToMany is not recommended in most cases. (https://stackoverflow.com/questions/7339143/why-no-many-to-many-relationships)[https://stackoverflow.com/questions/7339143/why-no-many-to-many-relationships]
  • @ManyToMany automatically generates an interfacing table linking the two tables of interest.
 List<Team> teamList = new ArrayList<>();
            List<Flag> flagList = new ArrayList<>();

            Team team1 = new Team("A");
            Team team2 = new Team("B");

            Flag flag1 = new Flag("Blue");
            Flag flag2 = new Flag("Green");

            teamList.add(team1);
            teamList.add(team2);

            flagList.add(flag1);
            flagList.add(flag2);

            team1.setFlags(flagList);
            team2.setFlags(flagList);

            flag1.setTeams(teamList);
            flag2.setTeams(teamList);

            em.persist(team1);
            em.persist(team2);
            em.persist(flag1);
            em.persist(flag2);

            em.flush();
            em.clear();
  • Executing above code results in the status below. img

2. Inheritance

  • Objects can be inherited to and from other objects. In JPA, the concept of Inheritance is achieved using SuperType and SubType. There is no concept that directly matches with the idea of inheritance in Database. There are three main strategies in mimicking inhertance.

2-1. @Inheritance types

@Inheritance(strategy=InheritanceType.JOINED)
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
  • Let’s say there is ITEM class which serves as a parent of classes Album, Book and Movie.
@Entity
@Inheritance(strategy = InheritanceType.)
@Table(name="items")
public abstract class Item {
    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
    private String country;
}

@Entity
public class Album extends Item {
    private String artist;
}
@Entity
public class Book extends Item {
    private String product;
    private String isbn;
}
@Entity
public class Movie extends Item {
    private String director;
    private String actor;
}

JOINED Strategy: this strategy creates parent class table AND child class tables all separately.

  • The parent class created with JOINED has a special column called DTYPE, which is used to join subclasses. ```java @Entity @Inheritance(strategy = InheritanceType.JOINED) public a
  Hibernate: 
      create table Album (
        artist varchar(255),
          id bigint not null,
          primary key (id)
      )
  Hibernate:   
      create table Book (
        isbn varchar(255),
          product varchar(255),
          id bigint not null,
          primary key (id)
      )
  Hibernate:     
      create table Movie (
        actor varchar(255),
          director varchar(255),
          id bigint not null,
          primary key (id)
      )
  Hibernate: 
      create table items (
        DTYPE varchar(31) not null,
          id bigint not null,
          country varchar(255),
          name varchar(255),
          price integer not null,
          primary key (id)
      )
  • When inserting a child object, you can see that the query is inserted twice from the console below.
      Movie movie = new Movie();
      movie.setDirector("aa");
      movie.setActor("bb");
      movie.setName("GoneWithTheWind");
      movie.setPrice(10000);
    
      em.persist(movie);
      em.flush();
      em.clear();
    
Hibernate: 
    /* insert Test.products.Movie
        */ insert 
        into
            items
            (country, name, price, DTYPE, id) 
        values
            (?, ?, ?, 'M', ?)
Hibernate: 
    /* insert Test.products.Movie
        */ insert 
        into
            Movie
            (actor, director, id) 
        values
            (?, ?, ?)
  • @DiscriminatorColumn and @DiscrimitatorValue can be used to denote customized name for child class tables.
    @DiscriminatorValue("M")
    public class Movie extends Item {}
    

    img

  • You can see that the DTYPE for Movie is marked as “M”

SINGLE_TABLE Strategy: This creates ONLY ONE table for parent and child classes.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Item {}
  • Executing above Movie insertion query,
    Hibernate: 
          create table items (
          DTYPE varchar(31) not null,
            id bigint not null,
            country varchar(255),
            name varchar(255),
            price integer not null,
            actor varchar(255),
            director varchar(255),
            artist varchar(255),
            isbn varchar(255),
            product varchar(255),
            primary key (id)
        )
    Hibernate: 
        /* insert Test.products.Movie
            */ insert 
            into
                items
                (country, name, price, actor, director, DTYPE, id) 
            values
                (?, ?, ?, ?, ?, 'M', ?)
    
  • Only one insert query is executed.
  • Unused child table columns are filled with NULL values.

img

TABLE_PER_CLASS Strategy: There are only Child class tables (no parent class table!)

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {}
  • Likewise, inserting a Movie object, you get ```console Hibernate: create table Album ( id bigint not null, country varchar(255), name varchar(255), price integer not null, artist varchar(255), primary key (id) ) Hibernate: create table Book ( id bigint not null, country varchar(255), name varchar(255), price integer not null, isbn varchar(255), product varchar(255), primary key (id) ) Hibernate: create table Movie ( id bigint not null, country varchar(255), name varchar(255), price integer not null, actor varchar(255), director varchar(255), primary key (id) )

Hibernate: /* insert Test.products.Movie */ insert into Movie (country, name, price, actor, director, id) values (?, ?, ?, ?, ?, ?)



- Though this seems quite ideal at first sight, the queries from this strategy can be quite hideous. Suppose you are retrieving a Move entity using Item object, as can be naturally done in Object world.

```java
 Item item = em.find(Item.class, movie.getId());
 System.out.println("Name:"+item.getName()+" Price:"+item.getPrice());
  • And below is the result..!
Hibernate: 
    select
        item0_.id as id1_3_0_,
        item0_.country as country2_3_0_,
        item0_.name as name3_3_0_,
        item0_.price as price4_3_0_,
        item0_.actor as actor1_4_0_,
        item0_.director as director2_4_0_,
        item0_.artist as artist1_1_0_,
        item0_.isbn as isbn1_2_0_,
        item0_.product as product2_2_0_,
        item0_.clazz_ as clazz_0_ 
    from
        ( select
            id,
            country,
            name,
            price,
            actor,
            director,
            null as artist,
            null as isbn,
            null as product,
            1 as clazz_ 
        from
            Movie 
        union
        all select
            id,
            country,
            name,
            price,
            null as actor,
            null as director,
            artist,
            null as isbn,
            null as product,
            2 as clazz_ 
        from
            Album 
        union
        all select
            id,
            country,
            name,
            price,
            null as actor,
            null as director,
            null as artist,
            isbn,
            product,
            3 as clazz_ 
        from
            Book 
    ) item0_ 
where
    item0_.id=?
Name:GoneWithTheWind Price:10000
  • Likewise, there can be some complications if you want to query by parent entity. There are some other disadvantages regarding this strategy that seniors don’t recommend, but I am yet to clarify them. I will just not use it!
  • For convenience’s purpose, it is recommended to make parent class abstract, so that it does not instantiate by itself.

Here is a brief recap of PROs and CONs of each strategy.

Account JOINED SINGLE_TABLE TABLE_PER_CLASS(Just NOT recommended)
PROs Regularized tables/ Save space used effectively No need for joining tables/ Simple select query Does not allow null
CONs Uses lots of join query/ Complicated selection query/ Calls insert query Allows null values to child columns/ Table size can get large Uses lots of union queries when selecting multiple child tables

2-2. @MappedSuperclass

  • @MappedSuperclass is NOT about Inheritance; rather, it is a convenient hack for supplying mapping information to child class.
  • The class annotated with @MappedSuperclass does NOT create a new table, nor does it map to any other table in the database.
  • It does not provide select or search functionalities.
@MappedSuperclass
public class BaseEntity {

    private String createdBy;
    private LocalDateTime createdDate;
    private String lastModifedBy;
    private LocalDateTime lastModifiedDate;

@Entity
public class Account extends BaseEntity{
  //....
}

```java
 public static void main(String[] args) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpaMain1");
    EntityManager em = emf.createEntityManager();
    EntityTransaction transaction = em.getTransaction();
    transaction.begin();
    try{
        Account account = new Account();
        account.setCreatedBy("tlonist");
        account.setAccountName("harry");
        account.setCreatedDate(LocalDateTime.now());

        em.persist(account);
        em.flush();
        em.clear();
        transaction.commit();
    }catch(Exception e){
        transaction.rollback();
    }finally{
        em.close();
    }
 }
}
Hibernate: 
    /* insert Test.Account
        */ insert 
        into
            Account
            (createdBy, createdDate, lastModifedBy, lastModifiedDate, name, cover, createDate, roleType, TEAM_ID, ACCOUNT_ID) 
        values
            (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

Okay, this was a rather long post. To sum up what I had covered,

  1. What an owning side of a relationship is. With examples of sample code and console results.
  2. How Inheritance can be made with JPA with its strategies.