Hello and welcome to my spring boot tutorial. In this tutorial we are going to learn about spring boot and how we can use it to create APIs, web services, and web applications. Spring boot is very easy to set up and to use for development.
This tutorial is for you if you are:
1. Learning the spring framework for the first time.
2. Learning spring boot so you can move from spring.
3. Coming from another stack like LAMP.
We expect you to know basic java for this tutorial. If you are not familiar with java please go through our java tutorial first.
We are going to develop an ecommerce site that we can use to show how spring boot or spring works in a real application.
Our expectation for you once you are through with this tutorial is:
1. Know how to create a spring boot application.
2. Know and understand why spring is so popular.
3. Know and understand how spring boot accelerates software development.
4. Know and understand how spring boot receives and responds to HTTP requests.
5. Know and understand how to upload and download files.
6. Know and understand spring dependency injection.
7. Know and understand the spring bean life cycle and definition.
8. Know and understand how spring uses an ORM such as Hibernate and JPA to perform database operations.
9. Know and understand how to use spring security to secure API endpoints.
10. Know and understand how to use AOP.
*** At the end of this tutorial, you should know how to develop a full web API ***
In this tutorial we will learn about how many to many works and what it does.
Many to many represents two entities that can have multiple instances on the other. For example here a user can have many roles(user,manager,admin,etc) and a role can have many users.
Note that one entity can have a many-to-many relationship with itself. A many-to-many relationship does not have to be with two separate entities. For example, a child can have two parents and a parent can have many children.
We now have to choose the parent and the child sides. In our
One the parent side we have a Set of roles annotated with the @ManyToMany annotation. We also use the @JoinTable annotation to
@ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL) @JoinTable( name = "user_roles", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = { @JoinColumn(name = "role_id") }) private Set<Role> roles;
package com.lovemesomecoding.user; import java.io.Serializable; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.lovemesomecoding.address.Address; import com.lovemesomecoding.laptop.Laptop; import com.lovemesomecoding.order.Order; import com.lovemesomecoding.role.Role; @JsonInclude(value = Include.NON_NULL) @Entity @Table(name = "user") public class User implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, updatable = false, unique = true) private Long id; @Column(name = "uid", unique = true, nullable=false, updatable=false) private String uid; @Column(name = "name") private String name; @Column(name = "email") private String email; @Column(name = "age") private int age; @JsonIgnoreProperties(value= {"users"}) @ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL) @JoinTable( name = "user_roles", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = { @JoinColumn(name = "role_id") }) private Set<Role> roles; public User() { super(); // TODO Auto-generated constructor stub } // setters and getters
On the child side of the relationship, we use the @ManyToMany annotation and also the mappedBy attribute.
@ManyToMany(mappedBy="roles", fetch=FetchType.EAGER) private Set<User> users;
package com.lovemesomecoding.role; import java.io.Serializable; import java.util.HashSet; import java.util.Set; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.Table; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.lovemesomecoding.user.User; @JsonInclude(value = Include.NON_NULL) @Entity @Table(name = "role") public class Role implements Serializable { private static final long serialVersionUID = 1L; public static final String USER = "USER"; public static final String ADMIN = "ADMIN"; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, updatable = false, unique = true) private Long id; @Column(name = "authority") private String authority; @ManyToMany(mappedBy="roles", fetch=FetchType.EAGER) private Set<User> users; public Role() { super(); // TODO Auto-generated constructor stub } // setters and getters
Many-To-Many with a more complex associative table.
March 18, 2019The many to one mapping is from the one-to-many relationship. It is from the child side of the one-to-many relationship. Check out the one to many relationship here.
Many to one relationship mean that many instances of some entity can have a relationship with only one instance of another entity. For example, many cars can belong to only one person or user.
Many to One Bidirectional
package com.lovemesomecoding.car; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.lovemesomecoding.user.User; @JsonInclude(value = Include.NON_NULL) @Entity @Table(name = "car") public class Car implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, updatable = false, unique = true) private Long id; @Column(name = "name") private String name; @Column(name = "brand") private String brand; @Column(name = "model") private String model; @Column(name = "price") private Double price; @JsonIgnoreProperties(value= {"cars"}) @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "user_id", nullable=false) private User user; public Car() { super(); // TODO Auto-generated constructor stub } // setters and getters
package com.lovemesomecoding.user; import java.io.Serializable; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.lovemesomecoding.car.Car; @JsonInclude(value = Include.NON_NULL) @Entity @Table(name = "user") public class User implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, updatable = false, unique = true) private Long id; @Column(name = "uid", unique = true, nullable=false, updatable=false) private String uid; @Column(name = "name") private String name; @Column(name = "email") private String email; @Column(name = "age") private int age; @JsonIgnoreProperties(value= {"user"}) @OneToMany(mappedBy = "user", cascade=CascadeType.ALL, fetch=FetchType.EAGER) private Set<Car> cars; public User() { super(); // TODO Auto-generated constructor stub } // getters and setters
Many to One Unidirectional
It’s very useful to map an entity to another entity which does not map back. This is a perfect solution in a situation where you are not using the child class very often. For example, you have a
package com.lovemesomecoding.paymentmethod; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.lovemesomecoding.user.User; @JsonInclude(value = Include.NON_NULL) @Entity @Table(name = "payment_method") public class PaymentMethod implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, updatable = false, unique = true) private Long id; @Column(name = "name") private String name; @Column(name = "brand") private String brand; @Column(name = "last4") private String last4; @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "user_id", nullable=false) private User user; public PaymentMethod() { super(); // TODO Auto-generated constructor stub } // getters and settersMarch 18, 2019
One-To-Many relationship is used widely in database operations. For exampel a user can have multiple cars or a user can have multiple computer screens to work with.
Our example is a user can have multiple orders as in a restaurant order. Let’s suppose you are asked to develop a pizza restaurant application where customers can order online. a User can have multiple orders.
One-To-Many Bidirectional Relationship
// On the parent side which is the User class. @OneToMany(mappedBy="user",cascade=CascadeType.ALL,fetch=FetchType.EAGER) private Set<Order> orders;
// On the child side which is the Order class. @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "user_id", nullable=false) private User user;
package com.lovemesomecoding.user; import java.io.Serializable; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.lovemesomecoding.order.Order; @JsonInclude(value = Include.NON_NULL) @Entity @Table(name = "user") public class User implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, updatable = false, unique = true) private Long id; @Column(name = "uid", unique = true, nullable=false, updatable=false) private String uid; @Column(name = "name") private String name; @Column(name = "email") private String email; @Column(name = "age") private int age; @JsonIgnoreProperties(value= {"user"}) @OneToMany(mappedBy = "user", cascade=CascadeType.ALL, fetch=FetchType.EAGER) private Set<Order> orders; public User() { super(); // TODO Auto-generated constructor stub } // getters and setters public void addOrder(Order order) { if(this.orders == null){ this.orders = new HashSet<>(); } this.orders.add(order); } }
package com.lovemesomecoding.order; import java.io.Serializable; import java.time.LocalDateTime; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hibernate.annotations.CreationTimestamp; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.lovemesomecoding.user.User; @JsonInclude(value = Include.NON_NULL) @Entity @Table(name = "user_order") public class Order implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, updatable = false, unique = true) private Long id; @Column(name = "uid", unique = true, nullable=false, updatable=false) private String uid; @Column(name = "coupon_code") private String couponCode; @Column(name = "discount") private Double discount; @Column(name = "total_amount") private Double totalAmount; @Column(name = "payment_method_id") private Long paymentMethodId; @CreationTimestamp @Column(name = "created_at") private Date createdAt; @JsonIgnoreProperties(value= {"orders"}) @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "user_id", nullable=false) private User user; public Order() { super(); // TODO Auto-generated constructor stub } // setters and getters }
One-To-Many Unidirectional Relationship
It depends on the needs you might have. If you need to access orders from the User class then you use this below:
// On the parent side(User class) @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER) private Set<Order> orders;
or if you need to access a user from the Order class then your use this below:
// On the child side(Order class) @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "user_id", nullable=false) private User user;
My preference and what I recommend is to use bidirectional. Using unidirectional will slow down your system as it creates an extra join for the
Use mappedBy on the @OneToMany otherwise, it will create a reference or associative table.
@JoinColumn is always on the owing side or the child side of the relationship.
March 18, 2019