Chapter 27:Exploring Associations in Java: One-to-One, One-to-Many, Many-to-One, and Many-to-Many Relationships

Rohit GawandeRohit Gawande
35 min read

Table of contents

Understanding Has-A Relationships in Java: Associations and Dependency Injection

Introduction:

In Java, relationships between classes and objects play a crucial role in making your code modular, reusable, and easier to maintain. One of the key concepts that every Java developer must understand is the Has-A relationship, also known as composition.

In object-oriented programming (OOP), relationships between entities are essential for building structured, efficient, and reusable code. One of the most prominent relationships is the HAS-A relationship, often referred to as composition or aggregation. This type of relationship allows one object to contain or "own" another object, enabling complex behaviors and interactions within a program.

In this blog post, we will explore how the HAS-A relationship works in Java, its importance in designing robust applications, and how it differs from other relationships like IS-A (inheritance). We will also discuss dependency injection, a key concept in implementing HAS-A relationships effectively.

1. The Concept of Has-A Relationship

A Has-A relationship in Java represents an association where one class contains a reference to another class. It implies that one object is composed of or relies on another object. This relationship contrasts with the Is-A relationship which is established through inheritance.

The HAS-A relationship defines that one class has a reference to another class as a member variable. This creates a "whole-part" relationship between objects, where one object (the target) contains or depends on another object (the dependent). The HAS-A relationship is often described as composition or aggregation depending on whether the relationship is strong or weak, but in both cases, the core idea remains that one object contains another.

Example to Understand the Misinterpretation of Is-A:

class Car {}
class Engine {}

// Incorrect usage of inheritance
class Engine extends Car {}

In the example above, we are saying "Engine IS-A Car," which doesn't make sense logically. An engine is a part of a car, not a type of car. Therefore, Has-A makes more sense for this situation.

Correct Representation of Has-A:

class Car {
    Engine engine; // Has-A relationship
}
class Engine {}

2. Introduction to Dependency Injection

In Java, the process of injecting one object into another object is known as dependency injection. This allows for loosely coupled classes and makes your application easier to test and maintain.

In a Has-A relationship, one object is a dependent object, and the other is the target object. The dependent object is injected into the target object. For example, a car depends on an engine to function, but the engine can exist independently.

There are two primary methods for dependency injection:

  1. Constructor Dependency Injection

  2. Setter Dependency Injection

In Java, relationships between entities (such as classes and objects) play a crucial role in optimizing memory utilization, code reusability, execution time, and shareability. These relationships help structure applications more efficiently and allow entities to interact seamlessly.

Here are the three main types of relationships in Java:

1. Has-A Relationship (Composition or Aggregation)

  • Definition: A "Has-A" relationship means that a class contains another class as a member. This is also known as Composition (strong association) or Aggregation (weak association), depending on the ownership and lifecycle dependency of the objects involved.

  • Usage: The "Has-A" relationship is extensively used in projects where objects are created from other objects. It promotes code reusability and better organization.

  • Example (Composition):

      class Engine {
          void start() {
              System.out.println("Engine started");
          }
      }
    
      class Car {
          private Engine engine;
    
          Car() {
              engine = new Engine();  // Car has an engine
          }
    
          void drive() {
              engine.start();
              System.out.println("Car is moving");
          }
      }
    
      public class Main {
          public static void main(String[] args) {
              Car car = new Car();
              car.drive();
          }
      }
    
  • Key Points:

    • In Composition, the composed object cannot exist independently (i.e., if the containing object is destroyed, so is the contained object).

    • In Aggregation, the contained object can exist independently (e.g., a Person may have an Address, but an Address can exist independently).

2. IS-A Relationship (Inheritance)

  • Definition: The "IS-A" relationship is based on inheritance, where one class inherits the properties and behavior of another class. It denotes a parent-child (or superclass-subclass) relationship.

  • Usage: It is used to create a hierarchy, reuse code, and implement polymorphism (e.g., a Dog is an Animal).

  • Example:

      class Animal {
          void eat() {
              System.out.println("This animal is eating");
          }
      }
    
      class Dog extends Animal {
          void bark() {
              System.out.println("The dog is barking");
          }
      }
    
      public class Main {
          public static void main(String[] args) {
              Dog dog = new Dog();
              dog.eat();  // Inherited from Animal class
              dog.bark();
          }
      }
    
  • Key Points:

    • The subclass inherits fields and methods from the superclass.

    • It supports polymorphism, which allows methods to be overridden to provide specific behavior for the subclass.

3. USE-A Relationship (Dependency)

  • Definition: A "USE-A" relationship signifies dependency, where one class uses another class temporarily (often via method parameters or local variables).

    • This relationship doesn't imply long-term association, and there is no lifecycle management involved.

Summary

  • Has-A Relationship: Used for composition and aggregation, where one class owns or is associated with another.

  • IS-A Relationship: Used for inheritance, where one class extends another.

  • USE-A Relationship: Represents a temporary dependency where one class uses another for specific functionality.

Each of these relationships helps in structuring a Java application with a focus on code reusability, memory optimization, and execution efficiency.

3. Constructor Dependency Injection

Constructor dependency injection involves injecting the dependent object through the constructor of the target class.

Example: Car and Engine (Constructor Dependency Injection)

class Engine {
    // Dependent object
}

class Car {
    Engine engine; // Target object

    // Constructor Dependency Injection
    public Car(Engine engine) {
        this.engine = engine;
    }

    public void drive() {
        System.out.println("Car is running using engine");
    }
}

In this example, the Car class depends on the Engine class, and we are injecting the Engine object through the Car constructor.

4. Setter Dependency Injection

Setter dependency injection uses a setter method to inject the dependent object into the target object.

Example: Car and Engine (Setter Dependency Injection)

class Engine {
    // Dependent object
}

class Car {
    Engine engine; // Target object

    // Setter Dependency Injection
    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public void drive() {
        System.out.println("Car is running using engine");
    }
}

5. Difference Between IS-A and Has-A Relationship

  • IS-A relationship (Inheritance) defines a parent-child relationship between two classes. It is implemented using the extends keyword.

  • Has-A relationship (Composition) defines that one class contains a reference to another class. It is implemented using instance variables.

CriteriaIS-A (Inheritance)Has-A (Composition)
UsageWhen one class is a subtype of another class.When one class is part of another class.
Syntaxclass B extends A {}class A { B b; }
Type of RelationshipParent-ChildPart-Whole
CouplingTightly CoupledLoosely Coupled
Code ReusabilityHigh, as child class inherits all propertiesModerate, as you need to compose the class

It is important to differentiate between HAS-A and IS-A relationships when designing object-oriented systems. While a HAS-A relationship implies that one class contains or owns another class, an IS-A relationship involves inheritance, where one class extends another, implying that the subclass is a type of the superclass.

Example of IS-A Relationship:

class Vehicle { 
public void drive() {
     System.out.println("Vehicle is driving.");
 }
 }

class Car extends Vehicle { 
@Override public void drive() { 
System.out.println("Car is driving.");
 } 
}

In the above example, Car extends Vehicle, forming an IS-A relationship, i.e., a Car is a Vehicle. This relationship is defined using the extends keyword.

However, it doesn’t make sense to say that an Engine is a Car, which is why we use the HAS-A relationship instead.

6. Understanding Associations in Java

An association is a relationship between two separate classes that are established through their objects.In Java, association defines the relationship between two classes or entities, allowing objects to communicate with each other. This communication is established through the concept of HAS-A relationship, which simplifies interactions between objects. Let's explore the different types of associations: There are four types of associations:

  1. One-to-One Association

  2. One-to-Many Association

  3. Many-to-One Association

  4. Many-to-Many Association


    1) One-to-One Association (1:1)

    A One-to-One association means that one instance of a class is associated with exactly one instance of another class. This type of relationship is direct and exclusive between two entities.

    Example Scenario:

    A person can have only one passport, and each passport belongs to only one person. The Person class will be associated with the Passport class, where each person "has a" passport.


    2) One-to-Many Association (1:M)

    A One-to-Many association implies that one instance of a class is associated with multiple instances of another class. This is common in scenarios where one object has control or ownership over multiple objects.

    Example Scenario:

    A library can have many books, but each book is associated with only one library. The Library class will have a relationship with multiple Book objects.


    3) Many-to-One Association (M:1)

    A Many-to-One association is the reverse of the One-to-Many relationship. Here, many instances of a class are associated with a single instance of another class.

    Example Scenario:

    Many employees can work in one department, but each employee is associated with only one department. The Employee class has a relationship with the Department class, where multiple employees "belong to" one department.


    4) Many-to-Many Association (M:M)

    A Many-to-Many association involves multiple instances of one class being associated with multiple instances of another class. This creates a bidirectional relationship between two entities.

    Example Scenario:

    A student can enroll in many courses, and each course can have many students. In this case, the Student class is associated with multiple Course objects, and each Course is associated with multiple Student objects.


    Establishing Relationships (HAS-A Relationship)

    In Java, associations are often represented through the HAS-A relationship (composition or aggregation). This is achieved by defining a reference variable of one class in another class, enabling communication between them. For example, by including an object of one class as a reference within another class, you establish a HAS-A relationship.

    For instance, when a class Car has an object of the class Engine, we say that the car "HAS-A" engine. Through this reference, the car can use the functionalities of the engine, facilitating collaboration between objects.

Code Reusability: Understanding the toString() Method in Java

Java, being an object-oriented programming language, treats everything as an object. When you create an instance of a class, you are effectively creating an object. Often, when dealing with complex applications, you may need to print out objects for logging, debugging, or just to display their data. In such scenarios, the toString() method comes into play. By default, every class in Java inherits from the Object class, which contains the toString() method. This method is responsible for converting an object into a string representation.

When we try to print an object, such as new Student(), the toString() method is automatically called. However, the default implementation of this method from the Object class prints the class name and the hashcode of the object in hexadecimal format (e.g., Rohit.Student@3feba861). In most cases, this output is not very useful. To make the output meaningful, we need to override the toString() method in our custom classes..


How toString() Works

To understand how the toString() method functions, let’s first look at the default behavior. Every class in Java inherits from the Object class, which provides several essential methods, including toString(). The toString() method in the Object class is defined as follows:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

This means that when you try to print an object without overriding the toString() method, Java will print the class name followed by the "@" symbol and the object's hashcode in hexadecimal format.

Let’s look at an example.

Code Example Without toString() Overridden

package Rohit;

public class Student {
    private String name;
    private Integer age;
    private Integer sid;

    // Constructor to set values
    public Student(String name, Integer age, Integer sid) {
        this.name = name;
        this.age = age;
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }
}

package Rohit;

public class TestApp {
    public static void main(String[] args) {
        Student s = new Student("Rohit", 20, 33);
        System.out.println(s);  // Output: Rohit.Student@3feba861
    }
}

In the above code, when we try to print the Student object, Java calls the toString() method from the Object class. As a result, we get an output like Rohit.Student@3feba861, which is the class name (Rohit.Student) followed by the object's hashcode (3feba861). While this is technically correct, it doesn’t provide any useful information about the Student object itself.


Overriding toString() Method for Meaningful Output

To display meaningful information about the object’s state (i.e., its fields), we need to override the toString() method in our Student class. By overriding this method, we can control how an object is represented as a string.

Here’s how we can override the toString() method in the Student class:

Code Example with Overridden toString()

package Rohit;

public class Student {
    private String name;
    private Integer age;
    private Integer sid;

    // Constructor to set values
    public Student(String name, Integer age, Integer sid) {
        this.name = name;
        this.age = age;
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    // Overriding the toString method
    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + ", sid=" + sid + "}";
    }
}

package Rohit;

public class TestApp {
    public static void main(String[] args) {
        Student s = new Student("Rohit", 20, 33);
        System.out.println(s);  // Output: Student{name='Rohit', age=20, sid=33}
    }
}

Explanation:

  1. Class Structure: The Student class has three fields: name, age, and sid. These fields are initialized through the constructor.

  2. Overriding toString(): The toString() method is overridden to return a string that provides a meaningful representation of the object’s state. Specifically, it returns a string in the format: Student{name='Rohit', age=20, sid=33}.

  3. Result: Now, when we print the Student object, instead of seeing the class name and hashcode, we get useful information about the object: the student's name, age, and student ID (sid).


Importance of Overriding toString()

Overriding the toString() method is crucial for improving the readability and debuggability of your code. Here are some key benefits:

  1. Improved Logging and Debugging: When logging or debugging your application, meaningful output from toString() helps you quickly understand the state of an object.

  2. Cleaner Output: Instead of cryptic outputs like class names and hashcodes, you can print an object’s actual data.

  3. Readable Code: Code that involves printing or logging objects becomes much easier to read and maintain when the toString() method is properly overridden.

  4. Use in Collections: When objects are used in collections such as lists, sets, or maps, overriding toString() ensures that the collection’s toString() method also produces meaningful output for each object in the collection.


How the toString() Method Fits in OOP

Java is an object-oriented programming language (OOP), meaning that everything is modeled as objects. To maintain the principles of OOP, such as encapsulation and code reusability, you need to ensure that your objects are easily understandable and manageable. The toString() method plays a significant role in this.

Object Representation

In OOP, an object is an instance of a class, and it contains data (fields) and behaviors (methods). When you override toString(), you give meaning to how the object is represented as a string. This makes it easier for other developers (or yourself) to work with objects in a readable manner.

Has-A Relationship and toString()

In the code example above, the Student class represents a Has-A relationship. This relationship signifies that a class contains another class or object. In our example, the Student class could have additional objects such as Address, Course, or Department. Each of these would have its own toString() method that can be called when printing the Student object.

In Java, understanding and overriding the toString() method is essential for effective object representation and communication.

By customizing the toString() method, you can make debugging, logging, and displaying information about your objects much easier. It enhances code reusability, maintainability, and readability, especially in larger projects where objects are used extensively. Furthermore, it fits well into the object-oriented nature of Java and is particularly useful in modeling relationships such as Has-A, where one class contains objects of another class.

Associations in Java

In Java, associations represent relationships between entities, helping organize and manage interactions between objects. To establish associations between different entities, we declare either a single reference or an array of reference variables of one entity class in another class. This concept helps achieve various types of relationships between objects in Java.

1.One-to-One Association

A One-to-One association is a relationship where one instance of a class (target object) is associated with exactly one instance of another class (dependent object). This is often implemented using a reference variable in the target class to hold an instance of the dependent class.

Memory Mapping of One-to-One Association

Let’s use an example where the Employee class has an association with the Account class. Here, an Employee "has a" single Account, creating a One-to-One relationship. The memory map for this association can be visualized as:

Target Object (Employee)Has-ADependent Object (Account)
Employee (eid, ename, eAdd)Account (accNo, accName, accType)
Explanation in Table
Field Name (Employee)Field Name (Account)Relationship TypeExplanation
eidaccNoOne-to-OneEach employee has one account
enameaccNameAccount name is linked with employee
eAddaccTypeAccount type belongs to the employee

Why Declare the Dependent Object First?

In this design, we declare the dependent object (Account) first before the target object (Employee) because the target object needs to "have" the dependent object. In other words, the target object (Employee) depends on the data provided by the dependent object (Account) to function properly. This sequence is essential to ensure the relationship is set up accurately when creating the target object.


Example: One-to-One Association in Java

Let’s break down a Java program that demonstrates the One-to-One association between an Employee and their Account:

Code Implementation

package Inhrtance.bean;

// Dependent Object
public class Account {
    String accNo;
    String accName;
    String accType;

    // Constructor to initialize Account details
    public Account(String accNo, String accName, String accType) {
        this.accNo = accNo;
        this.accName = accName;
        this.accType = accType;
    }
}

Here, the Account class represents the dependent object. The Employee class will be the target object:

package Inhrtance.bean;

public class Employee {
    private String eid;
    private String ename;
    private String eAdd;

    // HAS-A relationship
    Account account;

    // Constructor Injection
    public Employee(String eid, String ename, String eAdd, Account account) {
        this.eid = eid;
        this.ename = ename;
        this.eAdd = eAdd;
        this.account = account;
    }

    // Method to get employee details
    public void getEmployeeDetail() {
        System.out.println("Employee Details are:");
        System.out.println("EMPID: " + eid);
        System.out.println("EMPNAME: " + ename);
        System.out.println("EMPADD: " + eAdd);
        System.out.println();
        System.out.println("Account Details are:");
        System.out.println("ACCNO: " + account.accNo);
        System.out.println("ACCNAME: " + account.accName);
        System.out.println("ACCTYPE: " + account.accType);
    }
}

Finally, the TestApp1 class serves as the driver class, where we create instances of Account and Employee and display their details:

package Inhrtance.main;

import Inhrtance.bean.Account;
import Inhrtance.bean.Employee;

public class TestApp1 {
    public static void main(String[] args) {
        // Create Account object
        Account account = new Account("ABC123", "Rohit", "Saving");

        // Create Employee object with Account object injected
        Employee employee = new Employee("HFh1", "Rohit", "Jh", account);

        // Get employee details
        employee.getEmployeeDetail();
    }
}

Output:

Employee Details are:
EMPID: HFh1
EMPNAME: Rohit
EMPADD: Jh

Account Details are:
ACCNO: ABC123
ACCNAME: Rohit
ACCTYPE: Saving

How One-to-One Association Works

  • Constructor Injection: In the above code, the Employee object holds a reference to the Account object. The reference is passed using the constructor (constructor injection). This injects the dependency of the account into the employee.

  • Data Navigation: Once the association is set up, we can navigate through the Employee class to access data from the Account class. This is demonstrated in the getEmployeeDetail() method, where account details are accessed through the employee object.


Memory Representation of One-to-One Association

The memory representation would look something like this:


Conclusion: One-to-One Associations in Java

One-to-One associations are essential in representing simple relationships between entities in object-oriented programming. They enhance modularity, memory utilization, and data navigation. In the provided example, the Employee class holds a reference to a single Account object, creating a clear, maintainable, and well-structured association between these two entities.

2.One-to-Many Association in Java

In object-oriented programming (OOP), an association represents a relationship between two or more classes. The One-to-Many association is a specific type of association where one instance of a class (the target object) is associated with multiple instances of another class (the dependent object). This relationship is often used in real-world scenarios, such as a single department having multiple employees or a team consisting of several players.

One-to-Many Association Overview

In the context of One-to-Many relationships, a single target object is linked to multiple dependent objects. For instance, a Department (target object) can have multiple Employees (dependent objects). This relationship allows data navigation, as the target object (department) can access the details of all associated dependent objects (employees).

Memory Mapping of One-to-Many Association

Consider the following scenario:

  • Target Object: Department

  • Dependent Object: Employee

In a One-to-Many association, the target object (Department) holds a reference to an array of dependent objects (Employee[]). This means the target class has an array that can store references to multiple instances of the dependent class.

Below is the memory map for this association:

Target Object (Department)Has-ADependent Objects (Employees)
Department (d_id, dname)Employee[3] (eid, ename, eaddr)

Here, the Department holds a reference to an array of Employee objects. Each Employee object is independent but linked through the Department.

One-to-Many Association Example

Now, let's walk through a working code example of One-to-Many association in Java. The relationship involves a Department class and an Employee class.

Code Implementation

The Employee class serves as the dependent object:

package in.rohit.bean;

// Dependent Object
public class Employee {
    String eid;
    String ename;
    String eaddr;

    // Constructor to initialize Employee details
    public Employee(String eid, String ename, String eaddr) {
        super();
        this.eid = eid;
        this.ename = ename;
        this.eaddr = eaddr;
    }
}

The Department class is the target object that holds an array of Employee objects:

package in.rohit.bean;

public class Department {
    private String d_id;
    private String dname;

    // HAS-A Variable for One-to-Many association
    private Employee[] emps;

    // Constructor Injection
    public Department(String d_id, String dname, Employee[] emps) {
        super();
        this.d_id = d_id;
        this.dname = dname;
        this.emps = emps;
    }

    // Method to get Department details
    public void getDepartmentDetails() {
        System.out.println("Department Details Are:");
        System.out.println("=======================");
        System.out.println("Department ID:: " + d_id);
        System.out.println("Department NAME:: " + dname);
        System.out.println();
        System.out.println("Employee Details Are::");
        System.out.println("=======================");
        for (Employee employee : emps) {
            System.out.println("Employee ID:: " + employee.eid);
            System.out.println("Employee NAME:: " + employee.ename);
            System.out.println("Employee ADDRESS:: " + employee.eaddr);
        }
    }
}

In the TestApp class, we create instances of Employee and associate them with a Department:

package in.rohit.main;

import in.rohit.bean.Department;
import in.rohit.bean.Employee;

public class TestApp {
    public static void main(String[] args) {
        // Create Employee objects
        Employee e1 = new Employee("45", "Rohit", "MI");
        Employee e2 = new Employee("3", "Shikhar", "SRH");
        Employee e3 = new Employee("18", "Virat", "RCB");

        // Create an Employee array to hold the employees
        Employee[] emps = new Employee[3];
        emps[0] = e1;
        emps[1] = e2;
        emps[2] = e3;

        // Create a Department object and associate the Employee array with it
        Department d = new Department("IPL18", "BCCI", emps);

        // Get Department details along with the employees
        d.getDepartmentDetails();
    }
}

Output:

Department Details Are:
=======================
Department ID:: IPL18
Department NAME:: BCCI

Employee Details Are::
=======================
Employee ID:: 45
Employee NAME:: Rohit
Employee ADDRESS:: MI
Employee ID:: 3
Employee NAME:: Shikhar
Employee ADDRESS:: SRH
Employee ID:: 18
Employee NAME:: Virat
Employee ADDRESS:: RCB

Explanation

  1. Department Class:

    • It has fields d_id (department ID) and dname (department name).

    • The Department class also has a reference to an array of Employee objects. This array represents the multiple employees associated with the department.

  2. Employee Class:

    • Each Employee object has fields eid (employee ID), ename (employee name), and eaddr (employee address).

    • These fields store the individual details of each employee.

  3. HAS-A Relationship:

    • The Department class has a HAS-A relationship with the Employee class via an array of Employee objects.

    • This relationship is established during the creation of the Department object, where the array of employees is passed into the department’s constructor.

Data Navigation in One-to-Many Association

In the example, the Department class has a method getDepartmentDetails(), which allows navigation through the associated Employee[] array. This demonstrates how the target object (department) can access the details of the dependent objects (employees).

Memory Map of One-to-Many Association

The memory structure of this One-to-Many association is depicted below:

Memory Map Visualization:

       +-------------------+           +-------------------+
       |   Department       |           |   Employee Array   |
       |-------------------|           |-------------------|
       | d_id = "IPL18"     |           | Emp[0] -> e1       |
       | dname = "BCCI"     | --------> | Emp[1] -> e2       |
       | Emp[] (3 elements) |           | Emp[2] -> e3       |
       +-------------------+           +-------------------+

                                            +-------+
                                            | e1    |
                                            | "45"  |
                                            | Rohit |
                                            | MI    |
                                            +-------+

                                            +-------+
                                            | e2    |
                                            | "3"   |
                                            | Shikhar|
                                            | SRH   |
                                            +-------+

                                            +-------+
                                            | e3    |
                                            | "18"  |
                                            | Virat |
                                            | RCB   |
                                            +-------+

Table Representation of One-to-Many Association

Field Name (Department)Field Name (Employee)Relationship TypeExplanation
d_ideidOne-to-ManyA department has multiple employees
dnameenameEmployees belong to one department
Employee[]eaddrEmployee details are stored in an array in the department

Key Points in One-to-Many Association:

  1. Dynamic Associations:

    • Employees can be added to the department dynamically, and we use an array of Employee objects to store these associations.
  2. Scalability:

    • This approach is scalable since the Department class can easily manage any number of employees by simply adjusting the size of the array. If there are more employees, we can increase the array size or use more advanced data structures like ArrayList.
  3. Modularity:

    • The modular nature of OOP allows us to maintain each entity separately. Each Employee object is independent but can be easily associated with any Department object, making the design reusable.
  4. Constructor Injection:

    • In this example, we use constructor injection to inject the array of employees into the department. This ensures that the association is created at the time the Department object is instantiated.

Conclusion

The One-to-Many association is crucial in real-world scenarios, especially when managing relationships between entities such as departments and employees, teams and players, or schools and students. By using this association, we can easily navigate between related entities, store multiple related objects, and maintain a structured, scalable system. The code example clearly demonstrates how the target object (Department) can access and manage multiple dependent objects (Employees), leading to efficient data organization and retrieval.

3.Many-To-One Association in Java:

One of the fundamental types of associations is the Many-to-One (M:1) Association. This relationship allows multiple instances of one entity to be associated with a single instance of another entity. In this detailed exploration, we will dissect the Many-to-One association in Java, illustrating it through examples, code snippets, and a detailed memory map.

1. Understanding Many-to-One Association

In a Many-to-One association, one class (the "many" side) holds references to another class (the "one" side). This setup is essential in scenarios where multiple entities share a common reference or resource. For example, in an educational system, multiple students may be enrolled in a single branch or department. Here’s how this can be represented:

  • Student Class (Many side): Represents individual students.

  • Branch Class (One side): Represents a branch or department where multiple students are enrolled.

The many side will have a reference to the one side, but not the other way around. This means that each student knows which branch they belong to, but each branch does not need to know about all its students directly.

2. Memory Map

To visualize the Many-to-One association, we use a memory map showing how objects are related. Let’s use the example of students and branches.

Memory Map Diagram:

+-------------------------+
|         Branch          |
|-------------------------|
| name: "CSE"             |
| id: "CT"                |
| cost: 115000            |
+-------------------------+
          |
          | 1
          |
          v
+-------------------------+    +-------------------------+    +-------------------------+
|         Student         |    |         Student         |    |         Student         |
|-------------------------|    |-------------------------|    |-------------------------|
| id: "45"                |    | id: "18"                |    | id: "4"                 |
| name: "Rohit"           |    | name: "Virat"           |    | name: "Shikhar"         |
| address: "MI"           |    | address: "RCB"          |    | address: "SRH"          |
| branch: (CSE Branch)    |    | branch: (CSE Branch)    |    | branch: (CSE Branch)    |
+-------------------------+    +-------------------------+    +-------------------------+

In this memory map:

  • The Branch object is shared by multiple Student objects.

  • Each Student object has a reference to a single Branch.

3. Code Example: Many-to-One Association

Here’s a complete Java example illustrating a Many-to-One association where multiple Student instances are linked to a single Branch.

Constructor Injection:-

Branch Class:

package in.rohit.bean;

public class Branch {
    String name;
    String id;
    Integer cost;

    public Branch(String name, String id, int cost) {
        super();
        this.name = name;
        this.id = id;
        this.cost = cost;
    }

    // Getters and Setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Integer getCost() {
        return cost;
    }

    public void setCost(Integer cost) {
        this.cost = cost;
    }
}

Student Class:

package in.rohit.bean;

public class Student {
    private String id;
    private String name;
    private String address;
    private Branch branch; // Reference to the Branch

    public Student(String id, String name, String address, Branch branch) {
        super();
        this.id = id;
        this.name = name;
        this.address = address;
        this.branch = branch;
    }

    public void getDetails() {
        System.out.println("Branch Details Are:");
        System.out.println("=======================");
        System.out.println("Branch ID: " + branch.getId());
        System.out.println("Branch Name: " + branch.getName());
        System.out.println("Branch Cost: " + branch.getCost());
        System.out.println();
        System.out.println("Student Details Are:");
        System.out.println("=======================");
        System.out.println("Student ID: " + id);
        System.out.println("Student Name: " + name);
        System.out.println("Student Address: " + address);
    }

    // Getters and Setters
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Branch getBranch() {
        return branch;
    }

    public void setBranch(Branch branch) {
        this.branch = branch;
    }
}

Test Application:

package in.rohit.main;

import in.rohit.bean.Branch;
import in.rohit.bean.Student;

public class TestApp {
    public static void main(String[] args) {
        // Creating a Branch object
        Branch branch = new Branch("CSE", "CT", 115000);

        // Creating Student objects
        Student s1 = new Student("45", "Rohit", "MI", branch);
        Student s2 = new Student("18", "Virat", "RCB", branch);
        Student s3 = new Student("4", "Shikhar", "SRH", branch);

        // Displaying details of each student
        s1.getDetails();
        s2.getDetails();
        s3.getDetails();
    }
}

Explanation:

  1. Branch Class:

    • The Branch class represents the "one" side of the Many-to-One association.

    • It contains attributes like name, id, and cost.

  2. Student Class:

    • The Student class represents the "many" side.

    • Each Student instance has a reference to a Branch object.

    • The getDetails method prints both student and branch details.

  3. Test Application:

    • An instance of Branch is created.

    • Multiple Student instances are created, each associated with the same Branch.

    • The getDetails method is called to display the details.

Expected Output:

Student1 details are:
Student1 ID ::45
Student1 Name::Rohit
Student1 Address ::MI
Branch Details Are::
Branch ID::CT
Branch Name::CSE
Branch Cost::115000

Student2 details are:
Student2 ID ::18
Student2 Name::Virat
Student2 Address ::RCB
Branch Details Are::
Branch ID::CT
Branch Name::CSE
Branch Cost::115000

Student3 details are:
Student3 ID ::4
Student3 Name::Shikhar
Student3 Address ::SRH
Branch Details Are::
Branch ID::CT
Branch Name::CSE
Branch Cost::115000

Explanation of the Output

  • Each Student instance displays its own details, including ID, Name, and Address.

  • Following each student's details, the branch details (which are shared) are displayed.

  • As all students are associated with the same branch, the branch information is identical for each student.

This output demonstrates the Many-to-One association where multiple Student objects are related to a single Branch object.

You're right, you provided a version of the code that uses setter injection. I'll explain how this affects the output and include the necessary details about setter injection.

Explanation with Setter Injection

Setter Injection is another method to achieve dependency injection where dependencies are provided through setter methods rather than through the constructor. This allows for more flexibility in setting or updating dependencies after the object is created.

Branch Class:

package in.rohit.bean;
// Dependent Object
public class Branch {
    String name;
    String add;
    String id;

    // Getters and Setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }
}

Student Class:

package in.rohit.bean;
// Target Object
public class Student {
    private String id;
    private String name;
    private String add;
    // HAS-A variable
    private Branch branchCSE;

    // Getters and Setters
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }

    public Branch getBranchCSE() {
        return branchCSE;
    }

    public void setBranchCSE(Branch branchCSE) {
        this.branchCSE = branchCSE;
    }
}

TestApp Class:

package in.rohit.main;
import in.rohit.bean.Branch;
import in.rohit.bean.Student;

public class TestApp {
    public static void main(String[] args) {
        // Creating Branch Object
        Branch branch = new Branch();
        branch.setName("Nagpur");
        branch.setAdd("Hingna");

        // Creating Student Objects and using Setter Injection
        Student s1 = new Student(); 
        s1.setId("123");
        s1.setName("Rohit");
        s1.setAdd("Shantinagar");
        s1.setBranchCSE(branch);

        Student s2 = new Student(); 
        s2.setId("456");
        s2.setName("Nikhil");
        s2.setAdd("Burdi");
        s2.setBranchCSE(branch);

        Student s3 = new Student(); 
        s3.setId("897");
        s3.setName("Rohit2");
        s3.setAdd("Shanti");
        s3.setBranchCSE(branch);

        // Printing Student Details
        System.out.println();
        System.out.println("Student1 details are:");
        System.out.println("Student1 ID ::" + s1.getId());
        System.out.println("Student1 Name::" + s1.getName());
        System.out.println("Student1 Address ::" + s1.getAdd());
        System.out.println("Branch Details Are::");
        System.out.println("Branch ID::" + s1.getBranchCSE().getId());
        System.out.println("Branch Name::" + s1.getBranchCSE().getName());
        System.out.println("Branch Address::" + s1.getBranchCSE().getAdd());
        System.out.println();

        System.out.println("Student2 details are:");
        System.out.println("Student2 ID ::" + s2.getId());
        System.out.println("Student2 Name::" + s2.getName());
        System.out.println("Student2 Address ::" + s2.getAdd());
        System.out.println("Branch Details Are::");
        System.out.println("Branch ID::" + s2.getBranchCSE().getId());
        System.out.println("Branch Name::" + s2.getBranchCSE().getName());
        System.out.println("Branch Address::" + s2.getBranchCSE().getAdd());
        System.out.println();

        System.out.println("Student3 details are:");
        System.out.println("Student3 ID ::" + s3.getId());
        System.out.println("Student3 Name::" + s3.getName());
        System.out.println("Student3 Address ::" + s3.getAdd());
        System.out.println("Branch Details Are::");
        System.out.println("Branch ID::" + s3.getBranchCSE().getId());
        System.out.println("Branch Name::" + s3.getBranchCSE().getName());
        System.out.println("Branch Address::" + s3.getBranchCSE().getAdd());
    }
}

Expected Output with Setter Injection

Student1 details are:
Student1 ID ::123
Student1 Name::Rohit
Student1 Address ::Shantinagar
Branch Details Are::
Branch ID::Nagpur
Branch Name::Hingna
Branch Address::Hingna

Student2 details are:
Student2 ID ::456
Student2 Name::Nikhil
Student2 Address ::Burdi
Branch Details Are::
Branch ID::Nagpur
Branch Name::Hingna
Branch Address::Hingna

Student3 details are:
Student3 ID ::897
Student3 Name::Rohit2
Student3 Address ::Shanti
Branch Details Are::
Branch ID::Nagpur
Branch Name::Hingna
Branch Address::Hingna

Key Points:

  1. Branch Initialization: The Branch object is created first and its properties are set using setter methods.

  2. Student Initialization: Each Student object is created and initialized using setter methods to assign its ID, Name, Address, and the previously created Branch object.

  3. Branch Details: For each student, the branch details are fetched using the getBranchCSE() method which returns the branch object and its details are printed.

The output confirms that all students share the same branch details due to the Many-to-One relationship.

4. Memory Map and Table Representation

Memory Map:

+-------------------------+       +-------------------------+       +-------------------------+
|         Branch          |       |         Branch          |       |         Branch          |
|-------------------------|       |-------------------------|       |-------------------------|
| name: "CSE"             |       | name: "CSE"             |       | name: "CSE"             |
| id: "CT"                |       | id: "CT"                |       | id: "CT"                |
| cost: 115000            |       | cost: 115000            |       | cost: 115000            |
+-------------------------+       +-------------------------+       +-------------------------+
          ^                             ^                               ^
          |                             |                               |
          | 1                           | 1                             | 1
          |                             |                               |
+-------------------------+       +-------------------------+       +-------------------------+
|         Student         |       |         Student         |       |         Student         |
|-------------------------|       |-------------------------|       |-------------------------|
| id: "45"                |       | id: "18"                |       | id: "4"                 |
| name: "Rohit"           |       | name: "Virat"           |       | name: "Shikhar"         |
| address: "MI"           |       | address: "RCB"          |       | address: "SRH"          |
| branch: (CSE Branch)    |       | branch: (CSE Branch)    |       | branch: (CSE Branch)    |
+-------------------------+       +-------------------------+       +-------------------------+

Table Representation:

Student IDStudent NameStudent AddressBranch IDBranch NameBranch Cost
45RohitMICTCSE115000
18ViratRCBCTCSE115000
4ShikharSRHCTCSE115000

5. Conclusion

The Many-to-One association in Java effectively models scenarios where multiple instances of one entity (e.g., students) are linked to a single instance of another entity (e.g., branch). By understanding and implementing this association, you can create more efficient and organized code that mirrors real-world relationships.

4.Many-to-Many Association in Java

Many-to-Many Association is a relationship where multiple instances of one entity are associated with multiple instances of another entity. For instance, multiple students can enroll in multiple courses, and each course can have multiple students enrolled.

Key Concepts

  • Many-to-Many Relationship: Each instance of entity A can be related to multiple instances of entity B and vice versa.

  • Target Object: The entity that contains or references multiple instances of another entity.

  • Dependent Object: The entity that is referenced by or associated with multiple instances of the target object.

In this scenario, we have a Student entity associated with multiple Course entities, and each Course entity can be associated with multiple Student entities.

Implementation and Code

Classes

  1. Course Class: Represents the course with attributes like name, id, and cost.

     package in.rohit.bean;
     // Dependent Object
     public class Course {
         String name;
         String id;
         Integer cost;
    
         // Constructor
         public Course(String name, String id, int cost) {
             super();
             this.name = name;
             this.id = id;
             this.cost = cost;
         }
     }
    
  2. Student Class: Represents the student with attributes like sid, sname, saddr, and an array of Course objects.

     package in.rohit.bean;
     import in.rohit.bean.Course;
    
     // Target Object
     public class Student {
         private String sid;
         private String sname;
         private String saddr;
         // Has-A Relationship
         Course[] course;
    
         // Constructor Injection
         public Student(String sid, String sname, String saddr, Course[] course) {
             super();
             this.sid = sid;
             this.sname = sname;
             this.saddr = saddr;
             this.course = course;
         }
    
         // Method to get Student Details
         public void getStudentDetails() {
             System.out.println("SID :" + sid);
             System.out.println("SNAME :" + sname);
             System.out.println("SADDR :" + saddr);
             System.out.println("Course Details:");
             for (Course c : course) {
                 System.out.println("Course ID :" + c.id);
                 System.out.println("Course Name :" + c.name);
                 System.out.println("Course Cost :" + c.cost);
                 System.out.println("----------------");
             }
         }
     }
    
  3. TestApp Class: Demonstrates the creation of Student and Course objects and how the many-to-many relationship is represented.

     package in.rohit.main;
     import in.rohit.bean.Course;
     import in.rohit.bean.Student;
    
     public class TestApp {
         public static void main(String[] args) {
             // Creating Course Objects
             Course c1 = new Course("Al", "JAVA", 1000);
             Course c2 = new Course("B1", "Full Stack Web Development", 2000);
             Course c3 = new Course("C1", "DSA", 3000);
             Course[] courses = new Course[3];
             courses[0] = c1;
             courses[1] = c2;
             courses[2] = c3;
    
             // Creating Student Objects with Courses
             Student s1 = new Student("10", "Rohit", "MI", courses);
             Student s2 = new Student("18", "Kohli", "RCB", courses);
             Student s3 = new Student("5", "Shikhar", "SRH", courses);
    
             // Getting all student details
             s1.getStudentDetails();
             s2.getStudentDetails();
             s3.getStudentDetails();
         }
     }
    

Output

The output demonstrates how each student is associated with all courses. The getStudentDetails() method prints details of each student along with the details of all courses they are enrolled in.

Expected Output

SID :10
SNAME :Rohit
SADDR :MI
Course Details:
Course ID :Al
Course Name :JAVA
Course Cost :1000
----------------
Course ID :B1
Course Name :Full Stack Web Development
Course Cost :2000
----------------
Course ID :C1
Course Name :DSA
Course Cost :3000
----------------

SID :18
SNAME :Kohli
SADDR :RCB
Course Details:
Course ID :Al
Course Name :JAVA
Course Cost :1000
----------------
Course ID :B1
Course Name :Full Stack Web Development
Course Cost :2000
----------------
Course ID :C1
Course Name :DSA
Course Cost :3000
----------------

SID :5
SNAME :Shikhar
SADDR :SRH
Course Details:
Course ID :Al
Course Name :JAVA
Course Cost :1000
----------------
Course ID :B1
Course Name :Full Stack Web Development
Course Cost :2000
----------------
Course ID :C1
Course Name :DSA
Course Cost :3000
----------------

Memory Map Diagram

Visual Representation

+--------------------+     +--------------------+     +--------------------+
|     Student s1     |     |     Student s2     |     |     Student s3     |
+--------------------+     +--------------------+     +--------------------+
|  sid: "10"        |     |  sid: "18"        |     |  sid: "5"         |
|  sname: "Rohit"   |     |  sname: "Kohli"   |     |  sname: "Shikhar" |
|  saddr: "MI"      |     |  saddr: "RCB"     |     |  saddr: "SRH"     |
|  courses: [       |     |  courses: [       |     |  courses: [       |
|    c1, c2, c3     |     |    c1, c2, c3     |     |    c1, c2, c3     |
|  ]                 |     |  ]                 |     |  ]                 |
+--------------------+     +--------------------+     +--------------------+
        |                       |                       |
        |                       |                       |
        |                       |                       |
        |                       |                       |
        V                       V                       V
+--------------------+     +--------------------+     +--------------------+
|       Course c1    |     |       Course c2    |     |       Course c3    |
+--------------------+     +--------------------+     +--------------------+
|  id: "Al"         |     |  id: "B1"         |     |  id: "C1"         |
|  name: "JAVA"     |     |  name: "Full Stack|     |  name: "DSA"     |
|  cost: 1000       |     |    Web Development"|     |  cost: 3000      |
+--------------------+     +--------------------+     +--------------------+
        ^                       ^                       ^
        |                       |                       |
        |                       |                       |
        |                       |                       |
        +-----------------------+-----------------------+
              Many-to-Many Association

Explanation of the Memory Map

  1. Student Objects (s1, s2, s3):

    • Each student (s1, s2, s3) has a list of courses.

    • sid: Student ID (e.g., "10", "18", "5")

    • sname: Student Name (e.g., "Rohit", "Kohli", "Shikhar")

    • saddr: Student Address (e.g., "MI", "RCB", "SRH")

    • courses: Array of Course objects that the student is enrolled in. Each student has all the courses (c1, c2, c3).

  2. Course Objects (c1, c2, c3):

    • Each course (c1, c2, c3) is linked to all students.

    • id: Course ID (e.g., "Al", "B1", "C1")

    • name: Course Name (e.g., "JAVA", "Full Stack Web Development", "DSA")

    • cost: Course Cost (e.g., 1000, 2000, 3000)

  3. Arrows:

    • From Student to Course: Each student (s1, s2, s3) is associated with every course (c1, c2, c3), as each student is enrolled in all courses.

    • From Course to Student: Each course (c1, c2, c3) is associated with every student (s1, s2, s3), as each course is taken by all students.

Table Representation

Here’s a table showing the Many-to-Many relationship, with each student having all courses and each course having all students:

Student IDStudent NameStudent AddressCourses Enrolled
10RohitMIID: Al, Name: JAVA, Cost: 1000
ID: B1, Name: Full Stack Web Development, Cost: 2000
ID: C1, Name: DSA, Cost: 3000
18KohliRCBID: Al, Name: JAVA, Cost: 1000
ID: B1, Name: Full Stack Web Development, Cost: 2000
ID: C1, Name: DSA, Cost: 3000
5ShikharSRHID: Al, Name: JAVA, Cost: 1000
ID: B1, Name: Full Stack Web Development, Cost: 2000
ID: C1, Name: DSA, Cost: 3000

Summary

In this Many-to-Many relationship, every student is associated with every course, and every course is associated with every student. The memory map and table above accurately represent this comprehensive many-to-many association.

Conclusion

The Has-A relationship and dependency injection are vital for creating flexible, maintainable, and modular applications in Java. They help separate concerns and allow objects to collaborate without being tightly coupled. Mastering these concepts, along with different types of associations, will greatly enhance your ability to design and implement complex systems efficiently.


Thank You!

I appreciate you taking the time to read this post. I hope you found the information helpful and insightful. If you have any questions or feedback, feel free to reach out!

For more content on related topics, check out my other series:

  • DSA Series: Dive into the world of Data Structures and Algorithms with detailed explanations and code examples.

  • Full Stack Java Development Series: Explore the comprehensive guide on Full Stack Java Development, covering various technologies and practices.

You might also find these posts interesting:

Inner Class & Introduction to Interface – A detailed look into inner classes and interface fundamentals in Java.

Connect with me on social media for updates and more:

  • LinkedIn: Follow me for professional updates and networking opportunities.

  • GitHub: Check out my repositories for code samples and projects.

  • LeetCode: Explore my LeetCode profile for coding challenges and problem-solving skills.

    Your feedback and engagement are invaluable. Feel free to reach out with questions, comments, or suggestions. Happy coding!


Rohit Gawande
Full Stack Java Developer | Blogger | Coding Enthusiast

1
Subscribe to my newsletter

Read articles from Rohit Gawande directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rohit Gawande
Rohit Gawande

🚀 Tech Enthusiast | Full Stack Developer | System Design Explorer 💻 Passionate About Building Scalable Solutions and Sharing Knowledge Hi, I’m Rohit Gawande! 👋I am a Full Stack Java Developer with a deep interest in System Design, Data Structures & Algorithms, and building modern web applications. My goal is to empower developers with practical knowledge, best practices, and insights from real-world experiences. What I’m Currently Doing 🔹 Writing an in-depth System Design Series to help developers master complex design concepts.🔹 Sharing insights and projects from my journey in Full Stack Java Development, DSA in Java (Alpha Plus Course), and Full Stack Web Development.🔹 Exploring advanced Java concepts and modern web technologies. What You Can Expect Here ✨ Detailed technical blogs with examples, diagrams, and real-world use cases.✨ Practical guides on Java, System Design, and Full Stack Development.✨ Community-driven discussions to learn and grow together. Let’s Connect! 🌐 GitHub – Explore my projects and contributions.💼 LinkedIn – Connect for opportunities and collaborations.🏆 LeetCode – Check out my problem-solving journey. 💡 "Learning is a journey, not a destination. Let’s grow together!" Feel free to customize or add more based on your preferences! 😊