Chapter 27:Exploring Associations in Java: One-to-One, One-to-Many, Many-to-One, and Many-to-Many Relationships
Table of contents
- Understanding Has-A Relationships in Java: Associations and Dependency Injection
- 1. The Concept of Has-A Relationship
- 2. Introduction to Dependency Injection
- 1. Has-A Relationship (Composition or Aggregation)
- 2. IS-A Relationship (Inheritance)
- 3. USE-A Relationship (Dependency)
- Summary
- 3. Constructor Dependency Injection
- 4. Setter Dependency Injection
- 5. Difference Between IS-A and Has-A Relationship
- 6. Understanding Associations in Java
- Code Reusability: Understanding the toString() Method in Java
- 1.One-to-One Association
- 2.One-to-Many Association in Java
- 3.Many-To-One Association in Java:
- 4.Many-to-Many Association in Java
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:
Constructor Dependency Injection
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 anAddress
, but anAddress
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 anAnimal
).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.
Criteria | IS-A (Inheritance) | Has-A (Composition) |
Usage | When one class is a subtype of another class. | When one class is part of another class. |
Syntax | class B extends A {} | class A { B b; } |
Type of Relationship | Parent-Child | Part-Whole |
Coupling | Tightly Coupled | Loosely Coupled |
Code Reusability | High, as child class inherits all properties | Moderate, 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:
One-to-One Association
One-to-Many Association
Many-to-One Association
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 thePassport
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 multipleBook
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 theDepartment
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 multipleCourse
objects, and eachCourse
is associated with multipleStudent
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 classEngine
, 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:
Class Structure: The
Student
class has three fields:name
,age
, andsid
. These fields are initialized through the constructor.Overriding
toString()
: ThetoString()
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}
.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:
Improved Logging and Debugging: When logging or debugging your application, meaningful output from
toString()
helps you quickly understand the state of an object.Cleaner Output: Instead of cryptic outputs like class names and hashcodes, you can print an object’s actual data.
Readable Code: Code that involves printing or logging objects becomes much easier to read and maintain when the
toString()
method is properly overridden.Use in Collections: When objects are used in collections such as lists, sets, or maps, overriding
toString()
ensures that the collection’stoString()
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-A | Dependent Object (Account) |
Employee (eid, ename, eAdd) | ← | Account (accNo, accName, accType) |
Explanation in Table
Field Name (Employee) | Field Name (Account) | Relationship Type | Explanation |
eid | accNo | One-to-One | Each employee has one account |
ename | accName | Account name is linked with employee | |
eAdd | accType | Account 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 theAccount
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 theAccount
class. This is demonstrated in thegetEmployeeDetail()
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-A | Dependent 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
Department Class:
It has fields
d_id
(department ID) anddname
(department name).The
Department
class also has a reference to an array ofEmployee
objects. This array represents the multiple employees associated with the department.
Employee Class:
Each
Employee
object has fieldseid
(employee ID),ename
(employee name), andeaddr
(employee address).These fields store the individual details of each employee.
HAS-A Relationship:
The
Department
class has a HAS-A relationship with theEmployee
class via an array ofEmployee
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 Type | Explanation |
d_id | eid | One-to-Many | A department has multiple employees |
dname | ename | Employees belong to one department | |
Employee[] | eaddr | Employee details are stored in an array in the department |
Key Points in One-to-Many Association:
Dynamic Associations:
- Employees can be added to the department dynamically, and we use an array of
Employee
objects to store these associations.
- Employees can be added to the department dynamically, and we use an array of
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 likeArrayList
.
- This approach is scalable since the
Modularity:
- The modular nature of OOP allows us to maintain each entity separately. Each
Employee
object is independent but can be easily associated with anyDepartment
object, making the design reusable.
- The modular nature of OOP allows us to maintain each entity separately. Each
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.
- 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
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 multipleStudent
objects.Each
Student
object has a reference to a singleBranch
.
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:
Branch Class:
The
Branch
class represents the "one" side of the Many-to-One association.It contains attributes like
name
,id
, andcost
.
Student Class:
The
Student
class represents the "many" side.Each
Student
instance has a reference to aBranch
object.The
getDetails
method prints both student and branch details.
Test Application:
An instance of
Branch
is created.Multiple
Student
instances are created, each associated with the sameBranch
.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:
Branch Initialization: The
Branch
object is created first and its properties are set using setter methods.Student Initialization: Each
Student
object is created and initialized using setter methods to assign its ID, Name, Address, and the previously createdBranch
object.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 ID | Student Name | Student Address | Branch ID | Branch Name | Branch Cost |
45 | Rohit | MI | CT | CSE | 115000 |
18 | Virat | RCB | CT | CSE | 115000 |
4 | Shikhar | SRH | CT | CSE | 115000 |
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
Course Class: Represents the course with attributes like
name
,id
, andcost
.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; } }
Student Class: Represents the student with attributes like
sid
,sname
,saddr
, and an array ofCourse
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("----------------"); } } }
TestApp Class: Demonstrates the creation of
Student
andCourse
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
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
).
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)
Arrows:
From
Student
toCourse
: Each student (s1
,s2
,s3
) is associated with every course (c1
,c2
,c3
), as each student is enrolled in all courses.From
Course
toStudent
: 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 ID | Student Name | Student Address | Courses Enrolled |
10 | Rohit | MI | ID: Al, Name: JAVA, Cost: 1000 |
ID: B1, Name: Full Stack Web Development, Cost: 2000 | |||
ID: C1, Name: DSA, Cost: 3000 | |||
18 | Kohli | RCB | ID: Al, Name: JAVA, Cost: 1000 |
ID: B1, Name: Full Stack Web Development, Cost: 2000 | |||
ID: C1, Name: DSA, Cost: 3000 | |||
5 | Shikhar | SRH | ID: 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
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! 😊