繁体   English   中英

没有任何 ORM 的延迟加载/初始化设计模式

[英]Lazy Loading/Initialization design pattern without any ORM

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

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

延迟获取背后的主要思想是定义如何获取数据而不是自己获取数据,这意味着调用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
         }
      }
   }
}

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

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

order.getOrderDetails()  

所以你要实现它,你为什么不做 JPA hibernate 做的事情。

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

您想要延迟加载订单对象中的订单详细信息。 为此,您必须在映射文件中将 Order Details 集合设置为LAZY=truefetch=FetchType.LAZY如果您使用注释)。

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

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

Hibernate.initialize(order.getOrderDetails());

我希望我回答了你的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM