Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy Loading/Initialization design pattern without any ORM

I use Spring JDBC to load the data from a DB into the business model entity. My DAO interfaces are implemented by the classes that extend the JdbcDaoSupport class, which are responsible to creating the entity using the RowMapper classes. Then I have a facade class, which holds all the DAO interfaces and acts as a gateway to my model class (business logic class) requesting the business entities. Since, there is no lazy loading, all business entity data are loaded at once. This is fine for most of the cases, but in some cases I do want to load the data from DB lazily.

For example, I get all the Orders from the DB and would like to load Order Details only if the date meets certain condition (business logic). Here is a simple example of my design. I am looking for ways to improve the design so I could support lazy loading of Order Detail data if needed. You can see in the Business logic class, the ProcessOrder method needs the Order details only if the Order Date is Today, else doen't care about the details. However, my DAO loads all the Order Details when querying for Order by the facade class.

I understand one way is to expose the GetOrderDetails method in DAO and Facade class and allow the business logic class to load it, when needed. But, it wouldn't work for me. I have just mentioned a very simple case here, but there could be complex scenarios where the business object is passed on to another transformation layer and I don't want these layers to be responsible for loading the lazy part of the data.

Also, another possibility is to use a ORM tool like Hibernate. However, in my application this case may be applicable only at very few places and feel using an ORM tool to achieve this behavior is an overkill.

I am not looking for anything super fancy, just a simple design to make this happen for my special cases. Thanks for any help/suggestions.

class Order // Simple POJO business entity
{
   int OrderID;
   Date OrderDate;
   Collection<OrderDetail> OrderDetails; // Need to load lazily
}

class OrderDetail //Simple POJO business entity
{
   Order order;
   int ItemID;
   double Cost;
   int Quantity;
}

// DAO Interface
interface OrderDAO
{
   Order getOrder(int aOrderID);
}

// Concrete DAO class
JdbcOrderDao extends JdbcDaoSupport implements OrderDao
{
   Order getOrder(int aOrderID)
   {
      Order order = getJdbcTemplate.query(....., new OrderRowMapper());
      populateOrderDetails(order);
      return order;
   }

   private PopulateOrderDetails(Order aOrder)
   {
      //Query the DB and fill the Order details data.
   }

   private class OrderRowMapper implements ParameterizedRowMapper<Order>
   {
      // Code implemented to create and return the business entity from the resultset;
   }
}

// Facade class that hides the DAO interface and exposes the business methods
ServiceFacade
{
   private OrderDAO orderDAO;

   public Order GetOrder(int aOrderID)
   {
      return orderDAO.getOrder(aOrderID);
   }
}

// Business Logic class
BusinessModelLoader
{
   List<Order> Orders = new ArrayList<Order>();
   LoadOrders()
   {
     for(Integer orderID : allOrderIDs)
       Orders.add(facade.getOrder(orderID));
   }

   ProcessOrders()
   {
      for(Order order: Orders)
      {
         if (order.OrderDate == Today)
         {
            List<OrderDetail> details = order.OrderDetails; // Need to perform lazy loading here automatically
            // Do something with details
         }
      }
   }
}
like image 237
user320587 Avatar asked Dec 20 '25 16:12

user320587


1 Answers

The main idea behind lazy fetching is to define how to fetch the data instead of fetching it yourself, this means that when order.getOrderDetails() is called that is exactly when the query should be triggered. One way to achieve that is by decorating the getOrderDetails() method by extending the model in the OrderRowMapper in this case, for your example this would look like this

class Order // Simple POJO business entity
{
    int OrderID;
    Date OrderDate;
    Collection<OrderDetail> OrderDetails; // Need to load lazily

    public void setOrderDetails(Collection<OrderDetail> orderDetails) {
        this.OrderDetails = OrderDetails;
    }

    public Collection<OrderDetail> getOrderDetails() {
        return OrderDetails;
    }

}

class OrderDetail //Simple POJO business entity
{
    Order order;
    int ItemID;
    double Cost;
    int Quantity;
}

// DAO Interface
interface OrderDAO
{
    Order getOrder(int aOrderID);
}

// Concrete DAO class
class JdbcOrderDao extends JdbcDaoSupport implements OrderDao
{
    Order getOrder(int aOrderID)
    {
        Order order = getJdbcTemplate().queryForObject("...", new OrderRowMapper(this));
        return order;
    }

    public void populateOrderDetails(Order aOrder)
    {
        //Query the DB and retrieve the order details
    }

    private class OrderRowMapper implements RowMapper<Order>
    {
        private JdbcOrderDao dao;

        public OrderRowMapper(JdbcOrderDao dao) {
            this.dao = dao;
        }

        @Override
        public Order mapRow(ResultSet rs, int rowNum) throws SQLException {
            Order order = new Order() {
                @Override
                public Collection<OrderDetail> getOrderDetails() {
                    dao.populateOrderDetails(this);
                    return super.getOrderDetails();
                }
            };
            // set other fields
            return order;
        }

    }
}


So your business class will trigger the query now

// Business Logic class
class BusinessModelLoader
{
   List<Order> Orders = new ArrayList<Order>();
   LoadOrders()
   {
     for(Integer orderID : allOrderIDs)
       Orders.add(facade.getOrder(orderID));
   }

   ProcessOrders()
   {
      for(Order order: Orders)
      {
         if (order.OrderDate == Today)
         {
            Collection<OrderDetail> details = order.getOrderDetails(); // performs lazy loading here automatically
            // Do something with details
         }
      }
   }
}
like image 57
George Kyriakopoulos Avatar answered Dec 23 '25 06:12

George Kyriakopoulos



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!