【问题标题】:Lazy Loading/Initialization design pattern without any ORM没有任何 ORM 的延迟加载/初始化设计模式
【发布时间】:2012-11-05 18:09:56
【问题描述】:

我使用 Spring JDBC 将数据从数据库加载到业务模型实体中。我的 DAO 接口由扩展 JdbcDaoSupport 类的类实现,这些类负责使用 RowMapper 类创建实体。 然后我有一个外观类,它包含所有 DAO 接口并充当我的模型类(业务逻辑类)请求业务实体的网关。由于没有延迟加载,所有业务实体数据都会立即加载。这对于大多数情况来说都很好,但在某些情况下,我确实想懒惰地从数据库中加载数据。

例如,我从数据库中获取所有订单,并且希望仅在日期满足特定条件(业务逻辑)时才加载订单详细信息。这是我设计的一个简单示例。我正在寻找改进设计的方法,以便在需要时支持延迟加载订单明细数据。 您可以在业务逻辑类中看到,ProcessOrder 方法仅在 Order Date 为 Today 时才需要 Order 详细信息,否则不关心详细信息。但是,当通过外观类查询 Order 时,我的 DAO 会加载所有 Order Details。

我理解一种方法是在 DAO 和 Facade 类中公开 GetOrderDetails 方法,并允许业务逻辑类在需要时加载它。但是,它对我不起作用。我刚刚在这里提到了一个非常简单的案例,但是可能存在业务对象被传递到另一个转换层的复杂场景,我不希望这些层负责加载数据的惰性部分。

另外,另一种可能性是使用像 Hibernate 这样的 ORM 工具。但是,在我的应用程序中,这种情况可能仅适用于极少数地方,并且感觉使用 ORM 工具来实现这种行为是一种矫枉过正。

我不是在寻找任何超级花哨的东西,只是一个简单的设计来让我的特殊情况发生这种情况。感谢您的任何帮助/建议。

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
         }
      }
   }
}

【问题讨论】:

  • 您在延迟加载对象时遇到的具体问题是什么?
  • 我正在寻找如何在我当前的设计中适应延迟加载。这更像是一个设计问题。

标签: java lazy-loading spring-jdbc


【解决方案1】:

延迟获取背后的主要思想是定义如何获取数据而不是自己获取数据,这意味着当调用order.getOrderDetails() 时,正是应该触发查询的时间。实现这一目标的一种方法是通过在这种情况下扩展 OrderRowMapper 中的模型来装饰 getOrderDetails() 方法,对于您的示例,这看起来像这样

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;
        }

    }
}


所以你的业务类现在会触发查询

// 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
         }
      }
   }
}

【讨论】:

    【解决方案2】:

    JPA 执行此操作的方式是执行另一个查询以获取满足该条件的每个订单的每个 orderDetails 实体。

    它会在您在代码中调用它的那一刻进行获取。

    order.getOrderDetails()  
    

    所以你为什么不做 JPA hibernate 做的事情。

    【讨论】:

      【解决方案3】:

      我能理解你的问题,这是我的回复。

      您想在 Order 对象中延迟加载 Order Details。为此,您必须在映射文件中将您的订单详情集合设置为LAZY=true,如果您使用注释,则将其设置为fetch=FetchType.LAZY

      现在,当您查询订单对象时,订单详细信息将在需要时才加载。

      现在由于在 DAO 类中执行 getOrder 后会话将关闭,因此您将无法在业务层使用延迟加载的对象。为此,在返回对象之前在您的 DAO 方法中执行以下操作

      Hibernate.initialize(order.getOrderDetails());
      

      希望我回答了你的问题。

      【讨论】:

      • 阿南德,感谢您的回复。就我而言,我没有使用任何 ORM 工具。对我来说,只有几个案例确实需要延迟加载。为此使用 ORM 工具,对我的应用程序来说可能有点过头了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-16
      • 2019-06-03
      • 2020-05-10
      • 1970-01-01
      • 2011-02-01
      • 1970-01-01
      • 2012-09-01
      相关资源
      最近更新 更多