← Back to quizzesFree quiz

DAO and Builder Pattern in Java

The Data Access Object (DAO) pattern separates the persistence logic from business logic. By isolating database interactions, DAO promotes a clean architecture that is easier to maintain,…

10 questions~5 min
DAO and Builder Pattern in Java — Qwi
0 / 10
Score: 0%
1

Which advantage of the DAO pattern directly supports easier unit testing of business logic?

2

In the CarBuilder example, what is the purpose of returning 'this' from each setter method?

3

If a developer replaces the CarDAO implementation with a mock that returns an empty list, what will the CarController's handleShowCars method display?

4

Which of the following statements best describes why the Builder pattern is preferred over telescoping constructors for the Car class?

5

During execution of addCar, which SQL statement is prepared and what values are bound to its placeholders?

6

What would be the effect of moving the static block that creates the car table from DBUtil to the CarDAOImpl constructor?

7

Which method in CarDAO would you call to obtain the average price of all cars, and what type does it return?

8

If the Car class added a new mandatory field 'color' but the CarBuilder was not updated, what runtime problem would most likely occur when adding a car?

9

Comparing the Singleton pattern used for DBUtil with the Builder pattern for Car, which statement correctly identifies a key difference in their intent?

10

Which of the following DAO methods would be most appropriate to implement a feature that deletes all cars older than a given year in a single database call?

Understanding the DAO Pattern in Java

The Data Access Object (DAO) pattern separates the persistence logic from business logic. By isolating database interactions, DAO promotes a clean architecture that is easier to maintain, test, and extend.

Key Benefits of DAO

  • Separation of concerns: Data access code lives in its own layer, keeping business services free from SQL details.
  • Reusability: A single DAO implementation can be used across multiple services or projects.
  • Testability: Because the DAO is abstracted behind an interface, you can replace it with a mock during unit tests, allowing you to verify business logic without a real database.
  • Maintainability: Changes to the database schema affect only the DAO layer, leaving the rest of the application untouched.

When you ask, "Which advantage of the DAO pattern directly supports easier unit testing of business logic?", the correct answer is the separation of concerns that isolates data access code. This isolation lets you inject a mock DAO that returns controlled data, ensuring deterministic tests.

Typical DAO Interface for a Car Entity

A DAO interface defines the contract for CRUD operations and any specialized queries. Below is a concise example:

public interface CarDAO {
    List<Car> getAllCars();
    Car getCarById(int id);
    void addCar(Car car);
    void updateCar(Car car);
    void deleteCar(int id);
    double getAveragePrice(); // specialized query
}

Implementations such as CarDAOImpl contain the actual JDBC code, while the rest of the application depends only on the interface.

Mocking DAO for Unit Tests

Consider a CarController that calls CarDAO.getAllCars() and forwards the result to a view. If you replace the real DAO with a mock that returns an empty list, the controller’s handleShowCars method will render only the static parts of the page (e.g., the header) and no car entries. This demonstrates how DAO mocking isolates the controller from the database.

Common DAO Pitfalls

  • Embedding SQL strings directly in business classes – keep them in the DAO.
  • Using static blocks for schema creation inside DAO constructors – this can cause redundant execution.
  • Neglecting to close ResultSet, Statement, and Connection objects – always use try‑with‑resources.

For instance, moving a static block that creates the car table from a utility class to the CarDAOImpl constructor would cause the table creation SQL to run each time a new DAO instance is created, potentially leading to performance issues or duplicate‑table errors.

Mastering the Builder Pattern for Complex Objects

The Builder pattern addresses the problem of telescoping constructors—situations where a class has many optional parameters, leading to a proliferation of overloaded constructors that are hard to read and maintain.

Why Builder Beats Telescoping Constructors

  • Improves readability by allowing a fluent, chainable API.
  • Avoids a large number of constructor overloads.
  • Enables immutable objects once construction is complete.
  • Provides compile‑time safety for mandatory fields when designed carefully.

The statement "It avoids a large number of constructor overloads and improves readability" captures the core advantage of the Builder pattern for the Car class.

Typical CarBuilder Implementation

public class CarBuilder {
    private String brand;
    private String model;
    private int year;
    private double price;

    public CarBuilder setBrand(String brand) {
        this.brand = brand;
        return this; // enables method chaining
    }
    public CarBuilder setModel(String model) { this.model = model; return this; }
    public CarBuilder setYear(int year) { this.year = year; return this; }
    public CarBuilder setPrice(double price) { this.price = price; return this; }

    public Car build() {
        return new Car(brand, model, year, price);
    }
}

Returning this from each setter method is essential because it enables method chaining. Developers can write concise code such as:

Car car = new CarBuilder()
        .setBrand("Toyota")
        .setModel("Corolla")
        .setYear(2023)
        .setPrice(21000)
        .build();

Handling Mandatory Fields

If a new mandatory field, like color, is added to Car but the builder is not updated, the resulting SQL INSERT statement will lack a value for the color column. This omission causes the database to reject the row, throwing an SQLException. Therefore, whenever you introduce a required attribute, you must extend the builder to enforce its presence.

Builder vs. Telescoping Constructors – A Quick Comparison

AspectTelescoping ConstructorsBuilder Pattern
ReadabilityLow – many overloads with ambiguous parameter orderHigh – named setter methods make intent clear
ScalabilityPoor – each new optional field adds another overloadExcellent – add a new setter without changing existing code
ImmutabilityPossible but cumbersomeNatural – object is built once and then immutable

Integrating DAO and Builder in a Real‑World Application

Let’s walk through a typical flow where a web controller uses both patterns to add a new car to the database.

Step‑by‑Step Execution of addCar

  1. The controller receives a request with car details (brand, model, year, price).
  2. It creates a Car instance using CarBuilder, chaining the setters for each field.
  3. The built Car object is passed to CarDAO.addCar(car).
  4. Inside CarDAOImpl, a prepared statement is created:
    "INSERT INTO car (brand, model, year, price) VALUES (?, ?, ?, ?)"
    The placeholders are bound to the values from the Car object.
  5. The statement executes, persisting the new row.

This flow demonstrates why the correct SQL for the addCar operation is the INSERT statement with four placeholders, matching the builder‑produced object.

Specialized DAO Queries

Beyond basic CRUD, DAOs often expose domain‑specific queries. For the car domain, a method like getAveragePrice() returns a double representing the mean price of all stored cars. This method encapsulates the SQL SELECT AVG(price) FROM car and shields callers from the underlying aggregation logic.

Best Practices Checklist

  • Define clear interfaces for DAO operations; keep implementations hidden.
  • Use Builder for objects with many optional parameters to avoid constructor overloads.
  • Inject DAO implementations (e.g., via constructor injection) to facilitate mocking in tests.
  • Validate mandatory fields in the Builder before calling build() – throw an IllegalStateException if required data is missing.
  • Close JDBC resources using try‑with‑resources to prevent leaks.
  • Keep SQL statements in DAO, never in controllers or business services.

Common Interview Questions Recap

  • What DAO advantage aids unit testing? Separation of concerns isolates data access code.
  • Why does a builder setter return this? To enable fluent method chaining.
  • What happens if a mock DAO returns an empty list? The view renders without car entries.
  • Why prefer Builder over telescoping constructors? It avoids many overloads and improves readability.
  • Which SQL is used in addCar? INSERT with placeholders for brand, model, year, price.
  • Effect of moving static table‑creation block to the DAO constructor? The table creation runs each time a DAO instance is created.
  • Method to obtain average car price? getAveragePrice() returning double.
  • Consequence of adding a mandatory color field without updating the builder? SQL insertion fails, throwing an SQLException.