Annotation Examples @OneToMany @OneToOne @ManyToMany @ManyToOne [Hibernate]

Question:

Interested in how to create entities with annotations:

  • @OneToOne
  • @OneToMany
  • @ManyToOne
  • @ManyToMany

Also variations with bidirectional and unidirectional

It would also be nice to add explanations like Cascade.**

Answer:

This answer will be updated as needed.

The answer is based on examples from this resource – How to synchronize bidirectional entity associations with JPA and Hibernate . This resource was updated on January 19, 2020, so if you have questions/problems with the answers below, please check out this website

Update#3 05/26/2020

  1. Don't use @Data , @EqualsAndHashcode from Lombok!! These issues are discussed in the following sources:
    Item 45: Why Avoid Lombok @EqualsAndHashCode in Entities
    The best way to implement equals, hashCode, and toString with JPA and Hibernate
    Ultimate Guide to Implementing equals() and hashCode() with Hibernate
    You can use @Setter and @Getter

  2. Don't use GenerationStrategy AUTO for ID. Read here

Update №2 02.02.2020

  1. IMPORTANT It is better not to use Set<..> instead of List<..> when dealing with ..Many.. . A lot of problems arise, especially when working with forms. Also read this article by one of the main contributors to the Hibernate project – The best way to fix the Hibernate MultipleBagFetchException (This is an addition to Update # 1 (point 1) )

Update №1 11.09.2019:

  1. Instead of List<> , use Set<> where possible . (No, better not. Read more about it in the update below)

    Set<..> better to use instead of List<...> otherwise, when working with join fetch (custom hql query), you will get an error: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple Bags:

    More details about the error are written here.

  2. Don't use CascadeType.ALL

    In short, it's better to use only with @OneToOne , because when used with multiple relationships, unnecessary entries from the database may be deleted.

    This point is described in detail in this source.


GITHUB Demo (Spring 5)

I show all the examples below in detail in my mini-project, which can be downloaded from GitHub

You can read more about cascading here .

Project Lombok

In my answers I use Project Lombok annotations:
@Data – Annotation that adds Getters/Setters, Equals, ToString, HashCode to your project
@AllArgsConstructor – Constructor containing all global variables written in this class
@NoArgsConstructor – An empty constructor. If we do not want to write data in the constructor itself, but use Setter
@ToString(exclude – "VariableName") – When working with bidirectional , we will loop objects. To prevent this, you need to get rid of them.
Other Project Lombok annotations
Installing the Project Lombok Plugin in IntelliJ and Eclipse


@OneToOne

unidirectional option:

From the User to the Buyer, but not from the Buyer to the User

User.java:

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table
public class User {

    @Id
    @GeneratedValue
    private long id;

    @Column
    private String username;

    //Some code

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "customer_id", unique = true)
    private Customer customer;
}

Customer.java

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table
public class Customer {

    @Id
    @GeneratedValue
    @Column(name = "country_id")
    private long id;

    //Some code

    @Column(name = "customer_name")
    private String customerName;


}

bidirectional variant:

From User to Buyer and from Buyer to User


@OneToMany

unidirectional option:

A Post has multiple Comments, but we don't need to search for a Post from a Comment

Post.java:

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table
public class Post {

    @Id
    @GeneratedValue
    @Column(name = "post_id")
    private Long id;

    @Column
    private String postHeader;

    @OneToMany(
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    private List<Comment> comments = new ArrayList<>();

    public void addComment(Comment comment) {
        comments.add(comment);
    }

    public void removeComment(Comment comment) {
        comments.remove(comment);
    }
}

Comment.java:

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table
public class Comment {

    @Id
    @GeneratedValue
    @Column(name = "postcom_id")
    private Long id;

    @Column
    private String text;
}

bidirectional variant:

*Professor on the course can get information about students, at the same time students can get information about Professor*

Professor.java:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table
public class Professor {

    @Id
    @GeneratedValue
    @Column(name = "professor_id")
    private Long id;

    @Column
    private String name;

    @OneToMany(
            mappedBy = "professor",
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    List<Student> students = new ArrayList<>();

    /*
    As you see we need to do something like "recursion" below
     */
    public void addStudent(Student student) {
        students.add(student);
        student.setProfessor(this);
    }

    public void removeStudent(Student student) {
        students.remove(student);
        student.setProfessor(null);
    }
}

Student.java

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table
public class Student {

    @Id
    @GeneratedValue
    @Column(name = "student_id")
    private Long id;

    @Column
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="professor_id")
    private Professor professor;

}


@ManyToOne


@ManyToMany

unidirectional option:

A User can have multiple Roles, a Role can be assigned to multiple Users

User.java

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table
public class User {

    @Id
    @GeneratedValue
    @Column(name = "user_id")
    private long id;

    ...

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
    private List<Role> roles = new ArrayList<>();

    public void addRoles(Role role) {
        roles.add(role);
    }

    public void removeRoles(Role role) {
        roles.remove(role);
    }
}

Role.java

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table
public class Role {

    @Id
    @GeneratedValue
    @Column(name = "role_id")
    private int id;

    @Column(name = "role")
    private String role;
}

bidirectional variant:

A trader can trade on several Exchanges, Exchanges can be visited by several Traders

Trader.java:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table
public class Trader {

    @Id
    @GeneratedValue
    @Column(name = "trader_id")
    private Long id;

    @Column(name = "trader_name")
    private String traderName;

    @ManyToMany(fetch = FetchType.LAZY,
            cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE
    })
    @JoinTable(name = "trader_stockmarket",
            joinColumns = { @JoinColumn(name = "trader_id") },
            inverseJoinColumns = { @JoinColumn(name = "stockmarket_id") })
    private List<Stockmarket> stockmarkets = new ArrayList<>();


    /*
    We need to add methods below to make everything work correctly
     */

    public void addStockmarket(Stockmarket stockmarket) {
        stockmarkets.add(stockmarket);
        stockmarket.getTraders().add(this);
    }

    public void removeStockmarket(Stockmarket stockmarket) {
        stockmarkets.remove(stockmarket);
        stockmarket.getTraders().remove(this);
    }

}

Stockmarket.java

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table
public class Stockmarket{

    @Id
    @GeneratedValue
    @Column(name = "stockmarket_id")
    private Long id;

    @Column(name = "stockmarket_name")
    private String stockmarketName;

    @ManyToMany(mappedBy="stockmarkets")
    private List<Trader> traders = new ArrayList<>();

    /*
    We need to add methods below to make everything work correctly
     */

    public void addTrader(Trader trader) {
        traders.add(trader);
        trader.getStockmarkets().add(this);
    }

    public void removeTrader(Trader trader) {
        traders.remove(trader);
        trader.getStockmarkets().remove(this);
    }

}

Scroll to Top