Business Delegate Pattern
1. What is Business Delegate Pattern?
The Business Delegate pattern is a JEE Business Tier Pattern. It acts as a bridge between the presentation tier (servlet/controller) and the business tier (service + DAO classes). The Business Delegate and DAO classes together are capable of processing requests and generating results.
One line: A class that sits between the controller and the DAO layer, handling data conversion, exception translation, and transaction management so the presentation tier stays loosely coupled from the business tier.
Why loose coupling between tiers?
It is always recommended to develop presentation tier and business tier comps as loosely coupled comps, i.e., the degree of dependency should be less between both tiers. So the modifications done in presentation tier comps should not affect business tier comps and vice-versa.
The PM/Project Leader divides team into two parties:
- Party 1: Business tier developers - uses Java classes or Spring or Hibernate to develop business logic and persistence logic
- Party 2: Presentation tier developers - uses Servlet/JSP technologies and develops view/controller layer logics
Since these two parties can work parallelly either by staying in the same company or in two different branches of the same company, we need loose coupling between presentation tier comps and business tier comps.
2. Three Responsibilities of Business Delegate
- Converts VO class object to BO class object by collecting all inputs from enduser in the form of String values
- Converts DAO class generated technology-specific exception to project-specific user-defined exception
- Provides single JDBC connection object to multiple related DAO classes for Transaction Management support
3. Responsibility a) VO to BO Conversion
Problem (without Business Delegate)
// Problem: DTO class mirrors BO class types
public class EmployeeDTO { // Presentation tier
private Integer empno;
private String ename;
private String eaddr;
private String salary; // If DB changes salary to double,
private String desg; // this must change too!
// setters and getters
}
public class EmployeeBO { // Business tier
private Integer empno;
private String ename;
private String eaddr;
private Integer salary; // Changed to double in DB
private Integer grossSalary; // Business tier level
private Integer netSalary; // Business tier level
// setters and getters
}
Solution: Business Delegate converts VO to BO
Key insight: For any change happened in Business tier DB table, we are forced to modify other business tier comps like BO, DAO, Business Delegate (not a problem because they are in the same tier). But there is no need modifying any code in VO class, Servlet comp of presentation tier, because VO class keeps all the inputs as String values always.
4. Responsibility b) Exception Translation
Problem (without Business Delegate)
Solution: Convert to project-specific exception
// Custom project-specific exception
public class InternalProblemException extends Exception {
public InternalProblemException(String msg) {
super(msg);
}
}
// In BusinessDelegate: catch technology-specific, throw project-specific
public class EmployeeOperations {
// BusinessDelegate class
public String registerEmployee(EmployeeVO vo) throws InternalProblemException {
try {
// ... business logic + DAO calls
} catch (SQLException sqle) {
// Catch technology-specific exception
// Convert to project-specific user-defined exception
throw new InternalProblemException(sqle.getMessage());
}
}
}
// In Servlet: catches ONLY InternalProblemException
// If DAO changes from JDBC to Hibernate, only BusinessDelegate changes.
// Servlet code stays the same.
// Controller Servlet code:
try {
// ... business logic
// ... invoke service method
} catch (InternalProblemException ipe) {
// project-specific exception, not technology-specific
ipe.printStackTrace();
}
Because of this code written in BusinessDelegate class, if the technology and exception changes in DAO class, we need to just modify BusinessDelegate class code exception handling. The Servlet comp code exception because business delegate is converting the technology-specific exception to project-specific user-defined exception.
5. Responsibility c) Transaction Management
The process of combining related operations into single unit and executing them by applying do everything or nothing principle is called Transaction Management (Tx Mgmt).
- Use case 1: Transfer Money - The withdraw and deposit operations should be executed by applying do everything or nothing principle.
- Use case 2: Employee Registration - Insertion of record in HR DB table AND insertion of record in Finance DB table should be executed by applying do everything or nothing principle.
Transaction Management (TxMgmt) deals with 3 operations:
- Begin Tx -
con.setAutoCommit(false) - Execute logic - execute the queries
- Commit or Rollback Tx -
con.commit()orcon.rollback()
Problem: Separate connections per DAO
Solution: Single shared connection
6. Full-Length Industry Project: Employee Registration
When a new employee joins National Bank of India, their data must be inserted into two database tables: HR_Employee (HR department) and Finance_Employee (Finance department with salary details). Both inserts must succeed or both must fail (transaction). The Business Delegate handles: (1) converting VO (all Strings from form) to BO (correct types), (2) catching technology-specific exceptions and throwing project-specific ones, (3) managing the transaction across both DAOs using a shared JDBC connection.
High-Level Design (HLD)
Low-Level Design (LLD)
Project Structure
EmployeeVO.java (Presentation tier - all Strings)
package com.nbi.employee.vo;
public class EmployeeVO {
private String empno;
private String ename;
private String eaddr;
private String salary;
private String desg;
// All properties are String regardless of actual DB types.
// Presentation tier never changes when DB types change.
public String getEmpno() { return empno; }
public void setEmpno(String empno) { this.empno = empno; }
public String getEname() { return ename; }
public void setEname(String ename) { this.ename = ename; }
public String getEaddr() { return eaddr; }
public void setEaddr(String eaddr) { this.eaddr = eaddr; }
public String getSalary() { return salary; }
public void setSalary(String salary) { this.salary = salary; }
public String getDesg() { return desg; }
public void setDesg(String desg) { this.desg = desg; }
}
HREmployeeBO.java and FinanceEmployeeBO.java
// HREmployeeBO.java
package com.nbi.employee.bo;
public class HREmployeeBO {
private int empno;
private String ename;
private String eaddr;
private double salary;
public HREmployeeBO(int empno, String ename, String eaddr, double salary) {
this.empno = empno;
this.ename = ename;
this.eaddr = eaddr;
this.salary = salary;
}
public int getEmpno() { return empno; }
public String getEname() { return ename; }
public String getEaddr() { return eaddr; }
public double getSalary() { return salary; }
}
// FinanceEmployeeBO.java
package com.nbi.employee.bo;
public class FinanceEmployeeBO {
private int empno;
private String ename;
private String eaddr;
private double salary;
private double netSalary;
public FinanceEmployeeBO(int empno, String ename, String eaddr,
double salary, double netSalary) {
this.empno = empno;
this.ename = ename;
this.eaddr = eaddr;
this.salary = salary;
this.netSalary = netSalary;
}
public int getEmpno() { return empno; }
public String getEname() { return ename; }
public String getEaddr() { return eaddr; }
public double getSalary() { return salary; }
public double getNetSalary() { return netSalary; }
}
InternalProblemException.java
package com.nbi.employee.exception;
public class InternalProblemException extends Exception {
public InternalProblemException(String msg) {
super(msg);
}
}
HREmployeeDAO.java
package com.nbi.employee.dao;
import com.nbi.employee.bo.HREmployeeBO;
import java.sql.Connection;
import java.sql.PreparedStatement;
public class HREmployeeDAO {
private static final String INSERT_QUERY =
"INSERT INTO HR_Employee (empno, ename, eaddr, salary) VALUES (?, ?, ?, ?)";
private final Connection con;
// Connection injected by BusinessDelegate (not created internally)
public HREmployeeDAO(Connection con) {
this.con = con;
}
public int insertRecord(HREmployeeBO bo) throws Exception {
PreparedStatement ps = con.prepareStatement(INSERT_QUERY);
ps.setInt(1, bo.getEmpno());
ps.setString(2, bo.getEname());
ps.setString(3, bo.getEaddr());
ps.setDouble(4, bo.getSalary());
return ps.executeUpdate();
}
}
FinanceEmployeeDAO.java
package com.nbi.employee.dao;
import com.nbi.employee.bo.FinanceEmployeeBO;
import java.sql.Connection;
import java.sql.PreparedStatement;
public class FinanceEmployeeDAO {
private static final String INSERT_QUERY =
"INSERT INTO Finance_Employee (empno, ename, eaddr, salary, net_salary) VALUES (?, ?, ?, ?, ?)";
private final Connection con;
public FinanceEmployeeDAO(Connection con) {
this.con = con;
}
public int insertRecord(FinanceEmployeeBO bo) throws Exception {
PreparedStatement ps = con.prepareStatement(INSERT_QUERY);
ps.setInt(1, bo.getEmpno());
ps.setString(2, bo.getEname());
ps.setString(3, bo.getEaddr());
ps.setDouble(4, bo.getSalary());
ps.setDouble(5, bo.getNetSalary());
return ps.executeUpdate();
}
}
EmployeeOperations.java (Business Delegate - all 3 responsibilities)
package com.nbi.employee.delegate;
import com.nbi.employee.bo.HREmployeeBO;
import com.nbi.employee.bo.FinanceEmployeeBO;
import com.nbi.employee.dao.HREmployeeDAO;
import com.nbi.employee.dao.FinanceEmployeeDAO;
import com.nbi.employee.exception.InternalProblemException;
import com.nbi.employee.factory.ConnectionFactory;
import com.nbi.employee.vo.EmployeeVO;
import java.sql.Connection;
public class EmployeeOperations {
public String registerEmployee(EmployeeVO vo) throws InternalProblemException {
Connection con = null;
try {
// RESPONSIBILITY a) Convert VO (all Strings) to BO (correct types)
int empno = Integer.parseInt(vo.getEmpno());
double salary = Double.parseDouble(vo.getSalary());
double netSalary = salary * 0.75; // business logic: 25% tax
HREmployeeBO hrBO = new HREmployeeBO(
empno, vo.getEname(), vo.getEaddr(), salary);
FinanceEmployeeBO finBO = new FinanceEmployeeBO(
empno, vo.getEname(), vo.getEaddr(), salary, netSalary);
// RESPONSIBILITY c) Single connection for Transaction Management
con = ConnectionFactory.getConnection();
con.setAutoCommit(false); // BEGIN TRANSACTION
// Create both DAO classes with SHARED connection
HREmployeeDAO hrDAO = new HREmployeeDAO(con);
FinanceEmployeeDAO finDAO = new FinanceEmployeeDAO(con);
// Execute both inserts
int hrCount = hrDAO.insertRecord(hrBO);
int finCount = finDAO.insertRecord(finBO);
// Both succeeded: COMMIT
con.commit();
System.out.println("Transaction committed. HR: " + hrCount
+ ", Finance: " + finCount);
return "Employee registration successful";
} catch (NumberFormatException nfe) {
return "Invalid input format: " + nfe.getMessage();
} catch (Exception e) {
// RESPONSIBILITY b) Convert technology-specific exception
// to project-specific user-defined exception
try {
if (con != null) {
con.rollback(); // ROLLBACK both tables
System.out.println("Transaction rolled back");
}
} catch (Exception rollbackEx) {
rollbackEx.printStackTrace();
}
// Wrap technology-specific exception (SQLException/HibernateException)
// into project-specific InternalProblemException
throw new InternalProblemException(
"Employee registration failed: " + e.getMessage());
} finally {
ConnectionFactory.closeConnection(con);
}
}
}
Application.java (simulates Controller Servlet)
package com.nbi.employee;
import com.nbi.employee.delegate.EmployeeOperations;
import com.nbi.employee.exception.InternalProblemException;
import com.nbi.employee.vo.EmployeeVO;
public class Application {
public static void main(String[] args) {
EmployeeOperations delegate = new EmployeeOperations();
// Scenario 1: Successful registration
System.out.println("=== Scenario 1: Successful Registration ===");
EmployeeVO vo1 = new EmployeeVO();
vo1.setEmpno("1001");
vo1.setEname("Rajesh Kumar");
vo1.setEaddr("Hyderabad");
vo1.setSalary("75000");
vo1.setDesg("Senior Developer");
try {
String result = delegate.registerEmployee(vo1);
System.out.println("Result: " + result);
// forward to show_result.jsp
} catch (InternalProblemException ipe) {
System.out.println("Error: " + ipe.getMessage());
// forward to error.jsp
}
System.out.println();
// Scenario 2: Invalid input (VO to BO conversion fails)
System.out.println("=== Scenario 2: Invalid Salary ===");
EmployeeVO vo2 = new EmployeeVO();
vo2.setEmpno("1002");
vo2.setEname("Priya Singh");
vo2.setEaddr("Mumbai");
vo2.setSalary("not_a_number"); // invalid!
vo2.setDesg("Manager");
try {
String result = delegate.registerEmployee(vo2);
System.out.println("Result: " + result);
} catch (InternalProblemException ipe) {
System.out.println("Error: " + ipe.getMessage());
}
System.out.println();
// Scenario 3: DB failure (simulated - table doesn't exist)
System.out.println("=== Scenario 3: DB Failure ===");
try {
String result = delegate.registerEmployee(vo1);
System.out.println("Result: " + result);
} catch (InternalProblemException ipe) {
// This is InternalProblemException, NOT SQLException
// Controller doesn't know which DB technology is used
System.out.println("Error (project-specific): " + ipe.getMessage());
}
}
}
Expected Output
Key observations from the project
- VO class has ALL String properties. Presentation tier never changes when DB column types change. Only BusinessDelegate handles the conversion.
- Shared connection enables transaction. Both HREmployeeDAO and FinanceEmployeeDAO receive the same Connection object.
con.commit()commits both inserts.con.rollback()rolls back both. - Exception translation protects presentation tier. Controller catches
InternalProblemException(project-specific), neverSQLExceptionorHibernateException. If DAO technology changes, only BusinessDelegate's catch block changes. - Business logic lives in BusinessDelegate. Net salary calculation (75% of gross) is done here, not in DAO or Controller. DAO has only SQL.
- If FinanceEmployeeDAO fails, HREmployeeDAO insert is also rolled back. Do everything or nothing principle. Consistent state guaranteed.
7. DB Schema (from course notes)
-- HR Employee table
CREATE TABLE HR_Employee (
empno NUMBER(10) NOT NULL ENABLE,
ename VARCHAR2(20 BYTE),
eaddr VARCHAR2(20 BYTE),
desg VARCHAR2(20 BYTE),
CONSTRAINT HR_Employee_PK PRIMARY KEY (empno)
);
-- Finance Employee table
CREATE TABLE Finance_Employee (
empno NUMBER(10) NOT NULL ENABLE,
ename VARCHAR2(20 BYTE),
eaddr VARCHAR2(20 BYTE),
desg VARCHAR2(20 BYTE),
salary FLOAT(126),
CONSTRAINT Finance_Employee_PK PRIMARY KEY (empno)
);
-- Sequence for auto-generated employee IDs
CREATE SEQUENCE empno_seq MINVALUE 1 MAXVALUE 1000
INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
8. When to Use and When NOT to Use
When to use Business Delegate
| Scenario | Why |
|---|---|
| Multi-tier web application | Decouples presentation from business tier |
| Multiple DAO operations in one request | Shared connection enables transaction management |
| Technology-agnostic error handling | Converts DB exceptions to project exceptions |
| Data format conversion needed | Converts String VO to typed BO |
| Team separation | Presentation and business teams work independently |
When NOT to use
| Scenario | Alternative |
|---|---|
| Spring Boot application | Spring @Transactional handles transactions. @ControllerAdvice handles exceptions. ModelMapper handles VO-to-BO. |
| Single DAO per request | No need for shared connection or transaction coordination |
| Microservice with REST API | Service layer with DTOs replaces BusinessDelegate |
9. Quick Reference Card
| Responsibility | What it does | Without BD | With BD |
|---|---|---|---|
| VO to BO | Converts all-String VO to typed BO | VO changes when DB types change | Only BD changes |
| Exception translation | Converts tech exception to project exception | Controller catches SQLException | Controller catches project exception |
| Transaction Mgmt | Shared connection, atomic commit/rollback | Each DAO has own connection | All DAOs share one connection |
Business Delegate Design Pattern in Java
1. What is Business Delegate Pattern?
The Business Delegate pattern is a JEE Business Tier Pattern. It acts as a bridge between the presentation tier (servlet/controller) and the business tier (service + DAO classes). The Business Delegate and DAO classes together are capable of processing requests and generating results.
One line: A class that sits between the controller and the DAO layer, handling data conversion, exception translation, and transaction management so the presentation tier stays loosely coupled from the business tier.
Why loose coupling between tiers?
It is always recommended to develop presentation tier and business tier comps as loosely coupled comps, i.e., the degree of dependency should be less between both tiers. So the modifications done in presentation tier comps should not affect business tier comps and vice-versa.
The PM/Project Leader divides team into two parties:
- Party 1: Business tier developers - uses Java classes or Spring or Hibernate to develop business logic and persistence logic
- Party 2: Presentation tier developers - uses Servlet/JSP technologies and develops view/controller layer logics
Since these two parties can work parallelly either by staying in the same company or in two different branches of the same company, we need loose coupling between presentation tier comps and business tier comps.
2. Three Responsibilities of Business Delegate
- Converts VO class object to BO class object by collecting all inputs from enduser in the form of String values
- Converts DAO class generated technology-specific exception to project-specific user-defined exception
- Provides single JDBC connection object to multiple related DAO classes for Transaction Management support
3. Responsibility a) VO to BO Conversion
Problem (without Business Delegate)
// Problem: DTO class mirrors BO class types
public class EmployeeDTO { // Presentation tier
private Integer empno;
private String ename;
private String eaddr;
private String salary; // If DB changes salary to double,
private String desg; // this must change too!
// setters and getters
}
public class EmployeeBO { // Business tier
private Integer empno;
private String ename;
private String eaddr;
private Integer salary; // Changed to double in DB
private Integer grossSalary; // Business tier level
private Integer netSalary; // Business tier level
// setters and getters
}
Solution: Business Delegate converts VO to BO
Key insight: For any change happened in Business tier DB table, we are forced to modify other business tier comps like BO, DAO, Business Delegate (not a problem because they are in the same tier). But there is no need modifying any code in VO class, Servlet comp of presentation tier, because VO class keeps all the inputs as String values always.
4. Responsibility b) Exception Translation
Problem (without Business Delegate)
Solution: Convert to project-specific exception
// Custom project-specific exception
public class InternalProblemException extends Exception {
public InternalProblemException(String msg) {
super(msg);
}
}
// In BusinessDelegate: catch technology-specific, throw project-specific
public class EmployeeOperations {
// BusinessDelegate class
public String registerEmployee(EmployeeVO vo) throws InternalProblemException {
try {
// ... business logic + DAO calls
} catch (SQLException sqle) {
// Catch technology-specific exception
// Convert to project-specific user-defined exception
throw new InternalProblemException(sqle.getMessage());
}
}
}
// In Servlet: catches ONLY InternalProblemException
// If DAO changes from JDBC to Hibernate, only BusinessDelegate changes.
// Servlet code stays the same.
// Controller Servlet code:
try {
// ... business logic
// ... invoke service method
} catch (InternalProblemException ipe) {
// project-specific exception, not technology-specific
ipe.printStackTrace();
}
Because of this code written in BusinessDelegate class, if the technology and exception changes in DAO class, we need to just modify BusinessDelegate class code exception handling. The Servlet comp code exception because business delegate is converting the technology-specific exception to project-specific user-defined exception.
5. Responsibility c) Transaction Management
The process of combining related operations into single unit and executing them by applying do everything or nothing principle is called Transaction Management (Tx Mgmt).
- Use case 1: Transfer Money - The withdraw and deposit operations should be executed by applying do everything or nothing principle.
- Use case 2: Employee Registration - Insertion of record in HR DB table AND insertion of record in Finance DB table should be executed by applying do everything or nothing principle.
Transaction Management (TxMgmt) deals with 3 operations:
- Begin Tx -
con.setAutoCommit(false) - Execute logic - execute the queries
- Commit or Rollback Tx -
con.commit()orcon.rollback()
Problem: Separate connections per DAO
Solution: Single shared connection
6. Full-Length Industry Project: Employee Registration
When a new employee joins National Bank of India, their data must be inserted into two database tables: HR_Employee (HR department) and Finance_Employee (Finance department with salary details). Both inserts must succeed or both must fail (transaction). The Business Delegate handles: (1) converting VO (all Strings from form) to BO (correct types), (2) catching technology-specific exceptions and throwing project-specific ones, (3) managing the transaction across both DAOs using a shared JDBC connection.
High-Level Design (HLD)
Low-Level Design (LLD)
Project Structure
EmployeeVO.java (Presentation tier - all Strings)
package com.nbi.employee.vo;
public class EmployeeVO {
private String empno;
private String ename;
private String eaddr;
private String salary;
private String desg;
// All properties are String regardless of actual DB types.
// Presentation tier never changes when DB types change.
public String getEmpno() { return empno; }
public void setEmpno(String empno) { this.empno = empno; }
public String getEname() { return ename; }
public void setEname(String ename) { this.ename = ename; }
public String getEaddr() { return eaddr; }
public void setEaddr(String eaddr) { this.eaddr = eaddr; }
public String getSalary() { return salary; }
public void setSalary(String salary) { this.salary = salary; }
public String getDesg() { return desg; }
public void setDesg(String desg) { this.desg = desg; }
}
HREmployeeBO.java and FinanceEmployeeBO.java
// HREmployeeBO.java
package com.nbi.employee.bo;
public class HREmployeeBO {
private int empno;
private String ename;
private String eaddr;
private double salary;
public HREmployeeBO(int empno, String ename, String eaddr, double salary) {
this.empno = empno;
this.ename = ename;
this.eaddr = eaddr;
this.salary = salary;
}
public int getEmpno() { return empno; }
public String getEname() { return ename; }
public String getEaddr() { return eaddr; }
public double getSalary() { return salary; }
}
// FinanceEmployeeBO.java
package com.nbi.employee.bo;
public class FinanceEmployeeBO {
private int empno;
private String ename;
private String eaddr;
private double salary;
private double netSalary;
public FinanceEmployeeBO(int empno, String ename, String eaddr,
double salary, double netSalary) {
this.empno = empno;
this.ename = ename;
this.eaddr = eaddr;
this.salary = salary;
this.netSalary = netSalary;
}
public int getEmpno() { return empno; }
public String getEname() { return ename; }
public String getEaddr() { return eaddr; }
public double getSalary() { return salary; }
public double getNetSalary() { return netSalary; }
}
InternalProblemException.java
package com.nbi.employee.exception;
public class InternalProblemException extends Exception {
public InternalProblemException(String msg) {
super(msg);
}
}
HREmployeeDAO.java
package com.nbi.employee.dao;
import com.nbi.employee.bo.HREmployeeBO;
import java.sql.Connection;
import java.sql.PreparedStatement;
public class HREmployeeDAO {
private static final String INSERT_QUERY =
"INSERT INTO HR_Employee (empno, ename, eaddr, salary) VALUES (?, ?, ?, ?)";
private final Connection con;
// Connection injected by BusinessDelegate (not created internally)
public HREmployeeDAO(Connection con) {
this.con = con;
}
public int insertRecord(HREmployeeBO bo) throws Exception {
PreparedStatement ps = con.prepareStatement(INSERT_QUERY);
ps.setInt(1, bo.getEmpno());
ps.setString(2, bo.getEname());
ps.setString(3, bo.getEaddr());
ps.setDouble(4, bo.getSalary());
return ps.executeUpdate();
}
}
FinanceEmployeeDAO.java
package com.nbi.employee.dao;
import com.nbi.employee.bo.FinanceEmployeeBO;
import java.sql.Connection;
import java.sql.PreparedStatement;
public class FinanceEmployeeDAO {
private static final String INSERT_QUERY =
"INSERT INTO Finance_Employee (empno, ename, eaddr, salary, net_salary) VALUES (?, ?, ?, ?, ?)";
private final Connection con;
public FinanceEmployeeDAO(Connection con) {
this.con = con;
}
public int insertRecord(FinanceEmployeeBO bo) throws Exception {
PreparedStatement ps = con.prepareStatement(INSERT_QUERY);
ps.setInt(1, bo.getEmpno());
ps.setString(2, bo.getEname());
ps.setString(3, bo.getEaddr());
ps.setDouble(4, bo.getSalary());
ps.setDouble(5, bo.getNetSalary());
return ps.executeUpdate();
}
}
EmployeeOperations.java (Business Delegate - all 3 responsibilities)
package com.nbi.employee.delegate;
import com.nbi.employee.bo.HREmployeeBO;
import com.nbi.employee.bo.FinanceEmployeeBO;
import com.nbi.employee.dao.HREmployeeDAO;
import com.nbi.employee.dao.FinanceEmployeeDAO;
import com.nbi.employee.exception.InternalProblemException;
import com.nbi.employee.factory.ConnectionFactory;
import com.nbi.employee.vo.EmployeeVO;
import java.sql.Connection;
public class EmployeeOperations {
public String registerEmployee(EmployeeVO vo) throws InternalProblemException {
Connection con = null;
try {
// RESPONSIBILITY a) Convert VO (all Strings) to BO (correct types)
int empno = Integer.parseInt(vo.getEmpno());
double salary = Double.parseDouble(vo.getSalary());
double netSalary = salary * 0.75; // business logic: 25% tax
HREmployeeBO hrBO = new HREmployeeBO(
empno, vo.getEname(), vo.getEaddr(), salary);
FinanceEmployeeBO finBO = new FinanceEmployeeBO(
empno, vo.getEname(), vo.getEaddr(), salary, netSalary);
// RESPONSIBILITY c) Single connection for Transaction Management
con = ConnectionFactory.getConnection();
con.setAutoCommit(false); // BEGIN TRANSACTION
// Create both DAO classes with SHARED connection
HREmployeeDAO hrDAO = new HREmployeeDAO(con);
FinanceEmployeeDAO finDAO = new FinanceEmployeeDAO(con);
// Execute both inserts
int hrCount = hrDAO.insertRecord(hrBO);
int finCount = finDAO.insertRecord(finBO);
// Both succeeded: COMMIT
con.commit();
System.out.println("Transaction committed. HR: " + hrCount
+ ", Finance: " + finCount);
return "Employee registration successful";
} catch (NumberFormatException nfe) {
return "Invalid input format: " + nfe.getMessage();
} catch (Exception e) {
// RESPONSIBILITY b) Convert technology-specific exception
// to project-specific user-defined exception
try {
if (con != null) {
con.rollback(); // ROLLBACK both tables
System.out.println("Transaction rolled back");
}
} catch (Exception rollbackEx) {
rollbackEx.printStackTrace();
}
// Wrap technology-specific exception (SQLException/HibernateException)
// into project-specific InternalProblemException
throw new InternalProblemException(
"Employee registration failed: " + e.getMessage());
} finally {
ConnectionFactory.closeConnection(con);
}
}
}
Application.java (simulates Controller Servlet)
package com.nbi.employee;
import com.nbi.employee.delegate.EmployeeOperations;
import com.nbi.employee.exception.InternalProblemException;
import com.nbi.employee.vo.EmployeeVO;
public class Application {
public static void main(String[] args) {
EmployeeOperations delegate = new EmployeeOperations();
// Scenario 1: Successful registration
System.out.println("=== Scenario 1: Successful Registration ===");
EmployeeVO vo1 = new EmployeeVO();
vo1.setEmpno("1001");
vo1.setEname("Rajesh Kumar");
vo1.setEaddr("Hyderabad");
vo1.setSalary("75000");
vo1.setDesg("Senior Developer");
try {
String result = delegate.registerEmployee(vo1);
System.out.println("Result: " + result);
// forward to show_result.jsp
} catch (InternalProblemException ipe) {
System.out.println("Error: " + ipe.getMessage());
// forward to error.jsp
}
System.out.println();
// Scenario 2: Invalid input (VO to BO conversion fails)
System.out.println("=== Scenario 2: Invalid Salary ===");
EmployeeVO vo2 = new EmployeeVO();
vo2.setEmpno("1002");
vo2.setEname("Priya Singh");
vo2.setEaddr("Mumbai");
vo2.setSalary("not_a_number"); // invalid!
vo2.setDesg("Manager");
try {
String result = delegate.registerEmployee(vo2);
System.out.println("Result: " + result);
} catch (InternalProblemException ipe) {
System.out.println("Error: " + ipe.getMessage());
}
System.out.println();
// Scenario 3: DB failure (simulated - table doesn't exist)
System.out.println("=== Scenario 3: DB Failure ===");
try {
String result = delegate.registerEmployee(vo1);
System.out.println("Result: " + result);
} catch (InternalProblemException ipe) {
// This is InternalProblemException, NOT SQLException
// Controller doesn't know which DB technology is used
System.out.println("Error (project-specific): " + ipe.getMessage());
}
}
}
Expected Output
Key observations from the project
- VO class has ALL String properties. Presentation tier never changes when DB column types change. Only BusinessDelegate handles the conversion.
- Shared connection enables transaction. Both HREmployeeDAO and FinanceEmployeeDAO receive the same Connection object.
con.commit()commits both inserts.con.rollback()rolls back both. - Exception translation protects presentation tier. Controller catches
InternalProblemException(project-specific), neverSQLExceptionorHibernateException. If DAO technology changes, only BusinessDelegate's catch block changes. - Business logic lives in BusinessDelegate. Net salary calculation (75% of gross) is done here, not in DAO or Controller. DAO has only SQL.
- If FinanceEmployeeDAO fails, HREmployeeDAO insert is also rolled back. Do everything or nothing principle. Consistent state guaranteed.
7. DB Schema (from course notes)
-- HR Employee table
CREATE TABLE HR_Employee (
empno NUMBER(10) NOT NULL ENABLE,
ename VARCHAR2(20 BYTE),
eaddr VARCHAR2(20 BYTE),
desg VARCHAR2(20 BYTE),
CONSTRAINT HR_Employee_PK PRIMARY KEY (empno)
);
-- Finance Employee table
CREATE TABLE Finance_Employee (
empno NUMBER(10) NOT NULL ENABLE,
ename VARCHAR2(20 BYTE),
eaddr VARCHAR2(20 BYTE),
desg VARCHAR2(20 BYTE),
salary FLOAT(126),
CONSTRAINT Finance_Employee_PK PRIMARY KEY (empno)
);
-- Sequence for auto-generated employee IDs
CREATE SEQUENCE empno_seq MINVALUE 1 MAXVALUE 1000
INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
8. When to Use and When NOT to Use
When to use Business Delegate
| Scenario | Why |
|---|---|
| Multi-tier web application | Decouples presentation from business tier |
| Multiple DAO operations in one request | Shared connection enables transaction management |
| Technology-agnostic error handling | Converts DB exceptions to project exceptions |
| Data format conversion needed | Converts String VO to typed BO |
| Team separation | Presentation and business teams work independently |
When NOT to use
| Scenario | Alternative |
|---|---|
| Spring Boot application | Spring @Transactional handles transactions. @ControllerAdvice handles exceptions. ModelMapper handles VO-to-BO. |
| Single DAO per request | No need for shared connection or transaction coordination |
| Microservice with REST API | Service layer with DTOs replaces BusinessDelegate |
9. Quick Reference Card
| Responsibility | What it does | Without BD | With BD |
|---|---|---|---|
| VO to BO | Converts all-String VO to typed BO | VO changes when DB types change | Only BD changes |
| Exception translation | Converts tech exception to project exception | Controller catches SQLException | Controller catches project exception |
| Transaction Mgmt | Shared connection, atomic commit/rollback | Each DAO has own connection | All DAOs share one connection |