Hibernate এ ইনহেরিটেন্স (Inheritance) মডেলিং তিনটি প্রধান টেকনিক ব্যবহার করে করা যায়। এগুলি হল Table Per Class Hierarchy, Table Per Subclass, এবং Table Per Concrete Class। এই টেকনিকগুলি ইনহেরিটেন্স হায়ারার্কি কিভাবে ডেটাবেস টেবিলে মানচিত্রিত হবে তা নির্ধারণ করে। এই তিনটি পদ্ধতির মধ্যে পার্থক্য রয়েছে ডেটাবেসে কিভাবে ডেটা সঞ্চিত হবে এবং কিভাবে সম্পর্কিত ডেটা অনুরোধ করা হবে।
এই পদ্ধতিতে, সমস্ত সাবক্লাসের জন্য একটি মাত্র টেবিল ব্যবহৃত হয়। এই টেবিলের মধ্যে সকল বৈশিষ্ট্য (প্রোপার্টি) রাখা হয়, এমনকি বেস ক্লাসেরও। Discriminator কলাম ব্যবহার করে প্রতিটি রেকর্ডের ধরন (কোন ক্লাসের অন্তর্গত) চিহ্নিত করা হয়।
সুবিধা:
অসুবিধা:
এই পদ্ধতিতে, প্রতিটি সাবক্লাসের জন্য একটি আলাদা টেবিল তৈরি করা হয়। বেস ক্লাসের জন্য একটি টেবিল থাকবে এবং প্রতিটি সাবক্লাসের জন্য একটি আলাদা টেবিল থাকবে, যেগুলো শুধুমাত্র তাদের নিজস্ব বৈশিষ্ট্যগুলোই ধারণ করবে।
সুবিধা:
অসুবিধা:
এই পদ্ধতিতে, প্রতিটি কনক্রিট (অথবা বাস্তবায়িত) ক্লাসের জন্য আলাদা টেবিল তৈরি হয়। এই টেবিলগুলো শুধুমাত্র তাদের নিজস্ব ডেটা ধারণ করে এবং অন্য ক্লাসের ডেটার জন্য কোন রেফারেন্স রাখে না।
সুবিধা:
অসুবিধা:
এই তিনটি পদ্ধতির প্রতিটিরই নিজের নিজস্ব ব্যবহারিক ক্ষেত্র রয়েছে, এবং সঠিক পদ্ধতি নির্বাচন ডেটাবেসের কাঠামো এবং প্রয়োজনে নির্ভর করে।
Table Per Class Hierarchy (TPC) হল একটি হাইব্রিড ইনহেরিটেন্স মডেল যেখানে পুরো ক্লাস হায়ারার্কি একটিমাত্র টেবিলের মধ্যে সংরক্ষিত হয়। এই মডেলটি সাধারণত ছোট অ্যাপ্লিকেশনগুলির জন্য ব্যবহৃত হয় যেখানে একাধিক ক্লাসের ডেটা একই টেবিলের মধ্যে সংরক্ষিত থাকে।
NHibernate এ Table Per Class Hierarchy মডেলটি সাধারণত Discriminator কলাম ব্যবহার করে বাস্তবায়িত করা হয়, যা ক্লাসের ধরন বা টেবিলের বিভিন্ন সাবক্লাসের মধ্যে পার্থক্য তৈরি করে।
এখানে Employee এবং তার FullTimeEmployee এবং PartTimeEmployee এর উদাহরণ ব্যবহার করা হবে।
প্রথমে, তিনটি ক্লাস তৈরি করা হবে — Employee, FullTimeEmployee, এবং PartTimeEmployee।
public class Employee
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class FullTimeEmployee : Employee
{
public virtual double Salary { get; set; }
}
public class PartTimeEmployee : Employee
{
public virtual double HourlyRate { get; set; }
}
এখানে:
NHibernate এ Table Per Class Hierarchy ম্যাপিং করতে, আপনাকে Discriminator কলাম কনফিগার করতে হবে যা প্রতিটি সাবক্লাসের মধ্যে পার্থক্য করবে। এই উদাহরণে, Employee
টেবিলটি তিনটি ক্লাসের জন্য একটি টেবিল হবে এবং Discriminator কলামটি ঠিক করবে কোন সাবক্লাসের ডেটা।
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="hibernate.dialect">NHibernate.Dialect.MsSql2008Dialect</property>
<property name="hibernate.connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="hibernate.connection.connection_string">Server=yourserver;Database=yourdb;Integrated Security=True;</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- Discriminator সেটিংস -->
<mapping class="FullTimeEmployee">
<join table="Employee">
<key column="Id"/>
<discriminator column="EmployeeType" type="string"/>
</join>
</mapping>
<mapping class="PartTimeEmployee">
<join table="Employee">
<key column="Id"/>
<discriminator column="EmployeeType" type="string"/>
</join>
</mapping>
</session-factory>
</hibernate-configuration>
এখানে:
update
হিসেবে সেট করা হয়েছে, যাতে NHibernate স্বয়ংক্রিয়ভাবে ডেটাবেসের স্কিমা আপডেট করে।এই মডেলে, Employee টেবিলটি তৈরি হবে যা তিনটি ক্লাসের তথ্য ধারণ করবে। টেবিলটি এমন হবে:
CREATE TABLE Employee (
Id INT PRIMARY KEY,
Name VARCHAR(255),
Salary FLOAT, -- FullTimeEmployee এর জন্য
HourlyRate FLOAT, -- PartTimeEmployee এর জন্য
EmployeeType VARCHAR(50) -- Discriminator কলাম
);
এই টেবিলটি তিনটি সাবক্লাসের (FullTimeEmployee, PartTimeEmployee, Employee) তথ্য সংরক্ষণ করবে। EmployeeType কলামটি ব্যবহার করা হবে বিভিন্ন সাবক্লাসের মধ্যে পার্থক্য করতে।
FullTimeEmployee এবং PartTimeEmployee এর জন্য ডেটা সন্নিবেশ ও পড়া খুবই সহজ হবে, কারণ সমস্ত ডেটা Employee টেবিলে একত্রিত হয়ে থাকবে।
public void InsertEmployee()
{
using (ISession session = NHibernateHelper.SessionFactory.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
try
{
var fullTimeEmployee = new FullTimeEmployee
{
Name = "John Doe",
Salary = 50000
};
session.Save(fullTimeEmployee);
var partTimeEmployee = new PartTimeEmployee
{
Name = "Jane Smith",
HourlyRate = 20
};
session.Save(partTimeEmployee);
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
}
}
public Employee GetEmployeeById(int id)
{
using (ISession session = NHibernateHelper.SessionFactory.OpenSession())
{
return session.Get<Employee>(id); // সকল Employee সন্নিবেশ করা যাবে
}
}
Table Per Class Hierarchy Mapping হলো এমন একটি পদ্ধতি যা ছোট অ্যাপ্লিকেশন বা সহজ ক্লাস হায়ারার্কির জন্য বেশ কার্যকর। তবে বড় এবং জটিল ডেটাবেসের জন্য এই পদ্ধতি কিছু সীমাবদ্ধতা তৈরি করতে পারে, এবং এমন পরিস্থিতিতে অন্যান্য ইনহেরিটেন্স ম্যাপিং পদ্ধতি (যেমন Table Per Subclass বা Table Per Concrete Class) ব্যবহার করা হতে পারে।
Table Per Subclass (TPS) একটি Inheritance Mapping Strategy যেখানে এক্সটেনডেড ক্লাসের জন্য আলাদা আলাদা টেবিল তৈরি করা হয়, কিন্তু সব টেবিলের মধ্যে সম্পর্ক স্থাপিত থাকে। এই কৌশলে, মূল ক্লাসের জন্য একটি টেবিল তৈরি হয় এবং সাবক্লাসের জন্য আলাদা টেবিল তৈরি হয়, যেখানে মূল ক্লাসের ফিল্ডগুলি সাবক্লাসে ইনহেরিট করা হয় এবং প্রত্যেক সাবক্লাসের জন্য নিজের ফিল্ড থাকে।
NHibernate এ Table Per Subclass Mapping এর মাধ্যমে আপনি সহজেই ইনহেরিটেন্স স্ট্রাকচার তৈরি করতে পারেন যেখানে সব সাবক্লাসের ডেটা তাদের নিজস্ব টেবিলে সেভ করা হয়, তবে সাধারণ তথ্য মূল টেবিলে থাকে।
ধরা যাক, আমাদের একটি Employee
ক্লাস এবং এর দুটি সাবক্লাস Manager
এবং Developer
আছে। আমরা চাই যে, Employee
এর জন্য একটি টেবিল এবং Manager
ও Developer
এর জন্য আলাদা টেবিল তৈরি করা হোক।
প্রথমে আমরা Employee
, Manager
, এবং Developer
ক্লাস তৈরি করি।
public class Employee
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Position { get; set; }
}
public class Manager : Employee
{
public virtual int TeamSize { get; set; }
}
public class Developer : Employee
{
public virtual string ProgrammingLanguage { get; set; }
}
এখানে:
Employee
হল বেস ক্লাস, এবং Manager
ও Developer
সাবক্লাস।Manager
এর জন্য একটি অতিরিক্ত প্রপার্টি TeamSize
এবং Developer
এর জন্য ProgrammingLanguage
প্রপার্টি রয়েছে।এখন আমরা Mapping File তৈরি করব। এখানে আমরা Employee
ক্লাসের জন্য একটি টেবিল এবং Manager
ও Developer
এর জন্য আলাদা আলাদা টেবিল তৈরি করব। Mapping ফাইলের মাধ্যমে NHibernate জানবে যে কোন ক্লাসের জন্য কোন টেবিল তৈরি হবে।
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<!-- Employee class to table mapping -->
<class name="Employee" table="Employees">
<id name="Id" column="EmployeeID">
<generator class="identity"/>
</id>
<property name="Name" column="Name"/>
<property name="Position" column="Position"/>
</class>
<!-- Manager class to its own table mapping -->
<subclass name="Manager" table="Managers">
<key column="EmployeeID"/>
<property name="TeamSize" column="TeamSize"/>
</subclass>
<!-- Developer class to its own table mapping -->
<subclass name="Developer" table="Developers">
<key column="EmployeeID"/>
<property name="ProgrammingLanguage" column="ProgrammingLanguage"/>
</subclass>
</hibernate-mapping>
এখানে:
Employee
ক্লাসটি Employees
টেবিলের সাথে ম্যাপ করা হয়েছে।Manager
এবং Developer
ক্লাস দুটি আলাদা টেবিল Managers
এবং Developers
এর সাথে ম্যাপ করা হয়েছে।<subclass>
ট্যাগের মাধ্যমে আমরা সাবক্লাসগুলির জন্য আলাদা টেবিল নির্দেশ করেছি এবং তাদের প্রোপার্টিগুলি ম্যাপ করেছি।Fluent Mapping এর মাধ্যমে একই কাজ করা সম্ভব। নিচে FluentNHibernate ব্যবহার করে এই মডেলটি ম্যাপ করা হয়েছে:
using FluentNHibernate.Mapping;
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("Employees");
Id(x => x.Id).Column("EmployeeID").GeneratedBy.Identity();
Map(x => x.Name).Column("Name");
Map(x => x.Position).Column("Position");
}
}
public class ManagerMap : SubclassMap<Manager>
{
public ManagerMap()
{
Table("Managers");
KeyColumn("EmployeeID");
Map(x => x.TeamSize).Column("TeamSize");
}
}
public class DeveloperMap : SubclassMap<Developer>
{
public DeveloperMap()
{
Table("Developers");
KeyColumn("EmployeeID");
Map(x => x.ProgrammingLanguage).Column("ProgrammingLanguage");
}
}
এখানে:
Employee
টেবিলের সাথে সম্পর্কিত।KeyColumn("EmployeeID")
নির্দেশ করে যে, সাবক্লাসগুলির জন্য EmployeeID
মূল টেবিলের প্রাইমারি কী হবে।EmployeeID
), যার কারণে কিছুটা অতিরিক্ত স্টোরেজ ব্যবহার হতে পারে।Table Per Subclass মেপিং NHibernate এর জন্য একটি শক্তিশালী কৌশল, যেটি ইনহেরিটেন্স ভিত্তিক ডেটাবেস ডিজাইনকে বাস্তবায়ন করতে সহায়তা করে।
Table Per Concrete Class (TPC) মডেল হল একটি ORM কৌশল যেখানে প্রতি কনক্রিট ক্লাসের জন্য আলাদা টেবিল তৈরি করা হয়। এতে, প্রতিটি কনক্রিট ক্লাস তার নিজস্ব টেবিল ধারণ করে এবং এতে সমস্ত প্রপার্টি সংরক্ষিত থাকে, যার মধ্যে সত্ত্বেও পূর্ববর্তী ক্লাসের উত্তরাধিকারসূত্রে প্রাপ্ত প্রপার্টিগুলি অন্তর্ভুক্ত থাকে না। এই কৌশলটি Inheritance Mapping এর একটি অংশ এবং যখন আপনি টেবিলগুলোকে সহজ ও স্বতন্ত্র রাখতে চান তখন এটি ব্যবহার করা হয়।
এখানে, Table Per Concrete Class কৌশলটি ব্যবহার করার জন্য NHibernate এর মাধ্যমে একটি উদাহরণ দেওয়া হলো।
ধরা যাক, আপনি দুটি কনক্রিট ক্লাস তৈরি করেছেন: Employee এবং Manager। Manager ক্লাসটি Employee ক্লাস থেকে উত্তরাধিকারী, তবে এই দুটি ক্লাসের জন্য আলাদা আলাদা টেবিল থাকবে।
public class Employee
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual decimal Salary { get; set; }
}
public class Manager : Employee
{
public virtual string Department { get; set; }
}
এখানে, Manager ক্লাসটি Employee ক্লাস থেকে উত্তরাধিকারসূত্রে প্রাপ্ত, তবে দুটি আলাদা টেবিল থাকবে, একটিতে Employee এবং আরেকটিতে Manager এর ডেটা সংরক্ষিত হবে।
Table Per Concrete Class কৌশলটি ব্যবহার করতে, আপনাকে প্রতিটি কনক্রিট ক্লাসের জন্য আলাদা আলাদা টেবিল এবং ম্যাপিং কনফিগারেশন তৈরি করতে হবে।
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("Employees"); // Employee টেবিল
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Salary);
}
}
public class ManagerMap : ClassMap<Manager>
{
public ManagerMap()
{
Table("Managers"); // Manager টেবিল
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Salary);
Map(x => x.Department); // Manager এর Department
}
}
এখানে, EmployeeMap এবং ManagerMap ক্লাসগুলো আলাদা টেবিল ম্যাপিং কনফিগারেশন করে, এবং Employee এবং Manager এর জন্য আলাদা টেবিল তৈরি করে।
উপরের ম্যাপিং কনফিগারেশনের ভিত্তিতে, আপনি দুটি আলাদা টেবিল পাবেন:
Id | Name | Salary |
---|---|---|
1 | John Doe | 50000 |
2 | Jane Smith | 60000 |
Id | Name | Salary | Department |
---|---|---|---|
1 | Sarah Lee | 80000 | HR |
যেহেতু এই কৌশলে Employee এবং Manager এর জন্য আলাদা টেবিল রয়েছে, আপনি পৃথকভাবে তাদের সারণী থেকে ডেটা রিট্রাইভ করতে পারবেন। উদাহরণস্বরূপ, Employee টেবিল থেকে সমস্ত Employee তথ্য পাওয়া যাবে, এবং Manager টেবিল থেকে শুধুমাত্র Manager তথ্য পাওয়া যাবে।
using (var session = NHibernateHelper.SessionFactory.OpenSession())
{
// All Employees
var employees = session.Query<Employee>().ToList();
// All Managers
var managers = session.Query<Manager>().ToList();
}
এখানে, employees এবং managers দুটি আলাদা তালিকা হিসেবে ডেটা রিট্রাইভ করবে, কারণ তারা আলাদা টেবিল থেকে আসছে।
Table Per Concrete Class (TPC) কৌশলটি কিছু বিশেষ ক্ষেত্রে কার্যকর হতে পারে, তবে এটি সর্বদা সর্বোত্তম পছন্দ নয়। আপনার প্রয়োজনের ভিত্তিতে সঠিক ইনহেরিটেন্স কৌশল নির্বাচন করা উচিত।
NHibernate-এ Inheritance Mapping ব্যবহারের জন্য বিভিন্ন পদ্ধতি রয়েছে, যার মধ্যে Discriminator Column একটি জনপ্রিয় পদ্ধতি। এটি Single Table Inheritance (STI) মডেল অনুসরণ করে, যেখানে সব সাবক্লাসের ডেটা একটি মাত্র টেবিলে সঞ্চিত হয়, এবং Discriminator Column ব্যবহার করে প্রতিটি রেকর্ডের প্রকারভেদ করা হয়।
Discriminator Column একটি বিশেষ কলাম যা নির্দেশ করে যে কোন সাবে সাবক্লাসটি একটি নির্দিষ্ট রেকর্ডের সাথে সম্পর্কিত। এটি মূল ক্লাস এবং তার সাবক্লাসের মধ্যে পার্থক্য করতে সাহায্য করে, যখন ডেটাবেসে সবগুলো ক্লাসের ডেটা একসাথে থাকে।
এখানে, আমরা একটি উদাহরণ দেখাবো যেখানে একটি Employee
ক্লাসের দুটি সাবক্লাস Manager
এবং Developer
রয়েছে। আমরা Single Table Inheritance পদ্ধতি ব্যবহার করে এগুলোর ডেটা একটি টেবিলে সঞ্চয় করব এবং Discriminator Column এর মাধ্যমে সাবক্লাসের পার্থক্য করব।
প্রথমে একটি মূল Employee
ক্লাস তৈরি করব এবং তারপরে দুইটি সাবক্লাস তৈরি করব: Manager
এবং Developer
।
public class Employee
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class Manager : Employee
{
public virtual string Department { get; set; }
}
public class Developer : Employee
{
public virtual string ProgrammingLanguage { get; set; }
}
এখন, hibernate.cfg.xml ফাইলে Inheritance Mapping কনফিগার করব, যাতে Discriminator Column ব্যবহৃত হয়।
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<!-- ডেটাবেস কানেকশন প্রপার্টি -->
<property name="hibernate.connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="hibernate.connection.connection_string">Server=yourserver;Database=yourdb;Integrated Security=True;</property>
<property name="hibernate.dialect">NHibernate.Dialect.MsSql2008Dialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- Inheritance Mapping এর জন্য Discriminator Column ব্যবহার -->
<mapping class="YourNamespace.Employee, YourAssembly"/>
<mapping class="YourNamespace.Manager, YourAssembly"/>
<mapping class="YourNamespace.Developer, YourAssembly"/>
</session-factory>
</hibernate-configuration>
এখানে, Employee
, Manager
, এবং Developer
ক্লাসগুলোকে hibernate.cfg.xml ফাইলে উল্লেখ করা হয়েছে।
এখন, Employee, Manager, এবং Developer ক্লাসের জন্য হাইবারনেট mapping ফাইল তৈরি করব। আমরা Discriminator Column কনফিগার করব যাতে সাবক্লাসের ডেটা পৃথকভাবে চিহ্নিত হয়।
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Employee" table="Employee">
<id name="Id" column="Id">
<generator class="identity"/>
</id>
<property name="Name" column="Name"/>
<!-- Discriminator Column কনফিগার -->
<discriminator column="Discriminator" type="string"/>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<subclass name="Manager" extends="Employee">
<property name="Department" column="Department"/>
</subclass>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<subclass name="Developer" extends="Employee">
<property name="ProgrammingLanguage" column="ProgrammingLanguage"/>
</subclass>
</hibernate-mapping>
এখানে, Employee ক্লাসের জন্য একটি Discriminator কলাম সেট করা হয়েছে, যা Manager
এবং Developer
ক্লাসের মধ্যে পার্থক্য করবে। Discriminator কলামটি ডেটাবেস টেবিলে Discriminator
নামে থাকবে এবং তার মান হিসেবে Manager
অথবা Developer
থাকবে, যা নির্দেশ করবে যে কোন সাবক্লাসের রেকর্ডটি।
ডেটাবেসে Employee টেবিলটি কিছুটা এমন দেখতে হবে:
CREATE TABLE Employee (
Id INT PRIMARY KEY IDENTITY,
Name VARCHAR(255),
Discriminator VARCHAR(50),
Department VARCHAR(255), -- Manager জন্য
ProgrammingLanguage VARCHAR(255) -- Developer জন্য
);
এখানে, Discriminator
কলামটি Manager
অথবা Developer
সাবক্লাসের নির্দেশক হিসেবে কাজ করবে। আর Department
এবং ProgrammingLanguage
কলামগুলো শুধুমাত্র সংশ্লিষ্ট সাবক্লাসের জন্য থাকবে।
এখন, আমরা Session ব্যবহার করে Employee
, Manager
, এবং Developer
Entity সেভ এবং রিট্রিভ করতে পারি।
using NHibernate;
public class EmployeeService
{
public void SaveEmployee()
{
using (ISession session = NHibernateHelper.SessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
var manager = new Manager { Name = "Alice", Department = "HR" };
var developer = new Developer { Name = "Bob", ProgrammingLanguage = "C#" };
session.Save(manager);
session.Save(developer);
transaction.Commit();
}
}
}
}
public void RetrieveEmployees()
{
using (ISession session = NHibernateHelper.SessionFactory.OpenSession())
{
var employees = session.CreateQuery("from Employee").List<Employee>();
foreach (var employee in employees)
{
Console.WriteLine($"Employee: {employee.Name}");
if (employee is Manager manager)
{
Console.WriteLine($"Department: {manager.Department}");
}
else if (employee is Developer developer)
{
Console.WriteLine($"Programming Language: {developer.ProgrammingLanguage}");
}
}
}
}
এভাবে, Discriminator Column ব্যবহার করে Inheritance Mapping করতে পারবেন NHibernate-এ।
Read more