Entity mapping


Spring Boot এর সাথে JPA (Java Persistence API) ব্যবহার করলে আমরা Entity ক্লাসের মাধ্যমে ডাটাবেজ টেবিলের সাথে কাজ করি। এই Entity ক্লাসগুলোর মধ্যে সম্পর্ক (Relationship) স্থাপন করাই মূলত Entity Mapping। Entity mapping আমাদের কোডকে আরও object-oriented, reusable, এবং database-independent করে তোলে।


Entity কী?

Entity হলো এমন একটি জাভা ক্লাস, যা ডাটাবেজের একটি টেবিলকে রিপ্রেজেন্ট করে। প্রতিটি Entity এর অবজেক্ট টেবিলের একটি row (record) এর সমান।

উদাহরণ:

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;
}

এখানে User ক্লাসটি ডাটাবেজের users টেবিলের সাথে ম্যাপ হয়েছে।


Entity Mapping এর ধরন

Entity mapping মূলত চার ধরনের সম্পর্ক (Relationship) নিয়ে কাজ করে 

- One-to-One

- One-to-Many

- Many-to-One

- Many-to-Many

চলো একে একে দেখি।


One-to-One Relationship

একটি টেবিলের একটি রেকর্ড অন্য একটি টেবিলের একটি রেকর্ডের সাথে সম্পর্কিত।

উদাহরণ:
প্রতিটি ইউজারের একটি করে Address থাকে।

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;
}
@Entity
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String city;
    private String country;
}

এখানে @OneToOne সম্পর্ক তৈরি করছে, এবং @JoinColumn এর মাধ্যমে foreign key নির্ধারণ করছে।


One-to-Many Relationship

একজন ইউজারের একাধিক অর্ডার থাকতে পারে।

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Order> orders = new ArrayList<>();
}
@Entity
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String product;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
}

এখানে User এর একাধিক Order থাকতে পারে,
কিন্তু প্রতিটি Order শুধুমাত্র একটি User এর অন্তর্ভুক্ত।


Many-to-One Relationship

উপরের উদাহরণেই দেখা যাচ্ছে,
Order entity থেকে দেখলে, এটি একটি Many-to-One সম্পর্ক —
কারণ অনেক অর্ডার এক ইউজারের হতে পারে।

@ManyToOne
@JoinColumn(name = "user_id")
private User user;

এটি bidirectional সম্পর্কের দ্বিতীয় দিক।


Many-to-Many Relationship

একজন ছাত্র অনেক কোর্সে ভর্তি হতে পারে,
আবার একটি কোর্সে অনেক ছাত্র থাকতে পারে।

@Entity
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToMany
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private List<Course> courses = new ArrayList<>();
}
@Entity
public class Course {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;

    @ManyToMany(mappedBy = "courses")
    private List<Student> students = new ArrayList<>();
}

Hibernate এখানে স্বয়ংক্রিয়ভাবে student_course নামে একটি join table তৈরি করবে।


CascadeType Explained

cascade attribute বলে দেয়, parent entity এর অপারেশন child entity তে propagate হবে কি না।

Cascade Type ব্যাখ্যা
PERSIST Parent save হলে child ও save হবে
MERGE Parent merge হলে child ও merge হবে
REMOVE Parent delete হলে child ও delete হবে
REFRESH Parent refresh হলে child refresh হবে
ALL উপরের সবগুলো প্রযোজ্য হবে


FetchType Explained

FetchType বলে দেয় সম্পর্কিত entity কবে লোড হবে 👇

Fetch Type ব্যাখ্যা
EAGER Parent লোড হলে সাথে child entity লোড হয়
LAZY Child entity লোড হয় প্রয়োজন হলে (on-demand)

ডিফল্টভাবে: 

@OneToOne এবং @ManyToOneEAGER

@OneToMany এবং @ManyToManyLAZY


উদাহরণ: Bi-directional Relationship

@Entity
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "author")
    private List<Book> books = new ArrayList<>();
}
@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;

    @ManyToOne
    @JoinColumn(name = "author_id")
    private Author author;
}

এখন Author থেকে Book ও Book থেকে Author — দুই দিক দিয়েই relationship অ্যাক্সেস করা যাবে।


Entity Mapping Best Practices

-  প্রতিটি entity তে অবশ্যই @Id এবং unique identifier থাকতে হবে।
-  সবসময় LAZY fetch ব্যবহার করো performance এর জন্য।
- Cascading শুধুমাত্র যেখানে প্রয়োজন সেখানে প্রয়োগ করো।
- Bidirectional mapping করলে দুই দিকেই reference রাখতে ভুলো না।
- equals() এবং hashCode() entity তে implement করার সময় শুধুমাত্র ID ব্যবহার করো।
- @ToString (Lombok) ব্যবহার করলে relationship field exclude করো, না হলে recursive loop হতে পারে।


Practical Example: User ↔ Role Mapping

@Entity
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
        name = "user_role",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();
}

এখানে এক ইউজারের একাধিক রোল থাকতে পারে,
আবার একটি রোল অনেক ইউজারের হতে পারে।