zoukankan      html  css  js  c++  java
  • hibernate查询不到关联对象列表fetchType的选择

    概述

    昨天排查问题的时候,发现使用hibernate关联对象时,订单实体类对象死活无法获取关联的订单明细列表

    Order order = orderDao.findById(201L);
    //明明数据库表里有关联的订单明细,但是此处的就是查询不到
    int orderImemCount = order.getOrderItemList();
    

    原因在于,OrderItem对象中关联了产品对象product,它是急加载(fetchType="EAGER"),所以在关联查询时order.getOrderItemList(),hibernate底层的查询语句是inner join产品表的,但是订单明细中恰好有1个产品id是空的,结果就是本来有3条订单明细,却只查出了2条。,实际查询语句是这样的:

       select
            orderiteml0_.order_id as order3_0_2_,
            orderiteml0_.id as id1_2_,
            orderiteml0_.id as id1_1_,
            orderiteml0_.order_id as order3_1_1_,
            orderiteml0_.product_id as product4_1_1_,
            orderiteml0_.QUANTITY as QUANTITY1_1_,
            product1_.id as id2_0_,
            product1_.PRODUCT_NAME as PRODUCT2_2_0_ 
        from
            T_ORDER_ITEM orderiteml0_ 
        inner join
            T_PRODUCT product1_ 
                on orderiteml0_.product_id=product1_.id 
        where
            orderiteml0_.order_id=? 
        order by
            orderiteml0_.id
    

    fetchType的选择

    JPA定义实体之间的关系有如下几种:

    • @OneToOne
    • @ManyToOne
    • @OneToMany
    • @ManyToMany

    在定义它们的时候可以通过fetch属性指定加载方式,有两个值:

    • FetchType.LAZY:延迟加载,等到get的时候才会去查询
    • FetchType.EAGER:急加载,在查询主对象的时候就一起查询出来了

    问题复现

    数据库准备

    --region 创建表
    -- 订单表
    create table T_ORDER(
    	id	INTEGER PRIMARY KEY
    );
    COMMENT ON TABLE T_ORDER is '订单表';
    COMMENT ON COLUMN T_ORDER.id is '订单id';
    -- 订单明细表
    create table T_ORDER_ITEM(
    	id	INTEGER PRIMARY KEY,
    	order_id INTEGER,
    	product_id INTEGER,
    	quantity INTEGER
    );
    COMMENT ON TABLE T_ORDER_ITEM is '订单明细表';
    COMMENT ON COLUMN T_ORDER_ITEM.order_id is '订单id';
    COMMENT ON COLUMN T_ORDER_ITEM.product_id is '产品id';
    COMMENT ON COLUMN T_ORDER_ITEM.quantity is '数量';
    -- 产品表
    create table T_PRODUCT(
    	id	INTEGER PRIMARY KEY,
    	product_name varchar2(50)
    );
    COMMENT ON TABLE T_PRODUCT is '产品表';
    COMMENT ON COLUMN T_PRODUCT.product_name is '产品名称';
    -- endregion
    
    -- region 插入测试数据
    insert into T_PRODUCT(id, product_name) values (101, '产品A');
    insert into T_PRODUCT(id, product_name) values (102, '产品B');
    
    insert into T_ORDER (id) values (201);
    
    insert into T_ORDER_ITEM(id, order_id, product_id, quantity) values (301, 201, 101, 10);
    insert into T_ORDER_ITEM(id, order_id, product_id, quantity) values (302, 201, 102, 2);
    -- 注意:为了复现问题,将该处产品id设置为空
    insert into T_ORDER_ITEM(id, order_id, product_id, quantity) values (303, 201, '', 2);
    --endregion
    

    数据表对应的实体类

    订单类

    @Entity
    @DynamicInsert
    @DynamicUpdate
    @Table(name = "T_ORDER")
    public class Order implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        private Long id;
    
        /**
         * 订单明细
         */
        @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "order")
        @OrderBy(value = "id")
        private List<OrderItem> orderItemList = new ArrayList<>();
        
        //...setter 和 getter
    }
    

    订单明细类

    @Entity
    @DynamicInsert
    @DynamicUpdate
    @Table(name = "T_ORDER_ITEM")
    public class OrderItem implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        private Long id;
    
        /**
         * 订单
         */
        @ManyToOne(optional = false, fetch = FetchType.LAZY)
        @JoinColumn(name = "order_id", nullable = false)
        private Order order;
    
        /**
         * 产品
         */
        @ManyToOne(optional = false, fetch = FetchType.EAGER)
        @JoinColumn(name = "product_id", nullable = false)
        private Product product;
    
        /**
         * 数量
         */
        @Column(name = "QUANTITY")
        private int quantity;
        
        //...setter和getter
        
    }
    

    产品类

    @Entity
    @DynamicInsert
    @DynamicUpdate
    @Table(name = "T_PRODUCT")
    public class Product implements Serializable {
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        private Long id;
    
        /**
         * 产品名称
         */
        @Column(name = "PRODUCT_NAME")
        private String productName;
        
        //...setter和getter
    }
    

    测试

    public class App {
        private static SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        public static void main(String[] args) {
            Long id = 201L;
            Session session = sessionFactory.getCurrentSession();
            session.beginTransaction();
            Order order = (Order) session.get(Order.class, id);
    		//数据表里关联了3条订单明细,但查询结果总是只有2条
            System.out.println(order.getOrderItemList().size());
            session.getTransaction().commit();
        }
    }
    

    HibernateUtil 工具类

    public class HibernateUtil {
        private static final SessionFactory sessionFactory = buildSessionFactory();
    
        private static SessionFactory buildSessionFactory() {
            try {
                // Create the SessionFactory from hibernate.cfg.xml
                return new Configuration().configure().buildSessionFactory();
            } catch (Throwable ex) {
                // Make sure you log the exception, as it might be swallowed
                System.err.println("Initial SessionFactory creation failed." + ex);
                throw new ExceptionInInitializerError(ex);
            }
        }
    
        public static SessionFactory getSessionFactory() {
            return sessionFactory;
        }
    
        public static void shutdown() {
            // Close caches and connection pools
            getSessionFactory().close();
        }
    
    }
    

    解决

    问题点在于订单明细OrderItem属性product的加载方式

        /**
         * 产品
         */
        @ManyToOne(optional = false, fetch = FetchType.EAGER)
        @JoinColumn(name = "product_id", nullable = false)
        private Product product;
    

    这导致 order.getOrderItemList() 时总是 inner join 产品表,查询SQL如下:

       select
            orderiteml0_.order_id as order3_0_2_,
            orderiteml0_.id as id1_2_,
            orderiteml0_.id as id1_1_,
            orderiteml0_.order_id as order3_1_1_,
            orderiteml0_.product_id as product4_1_1_,
            orderiteml0_.QUANTITY as QUANTITY1_1_,
            product1_.id as id2_0_,
            product1_.PRODUCT_NAME as PRODUCT2_2_0_ 
        from
            T_ORDER_ITEM orderiteml0_ 
        inner join
            T_PRODUCT product1_ 
                on orderiteml0_.product_id=product1_.id 
        where
            orderiteml0_.order_id=? 
        order by
            orderiteml0_.id
    

    **只需将 FetchType.EAGER 改为 FetchType.LAZY 即可,hibernate的底层查询SQL就会是:*

        select
            orderiteml0_.order_id as order3_0_1_,
            orderiteml0_.id as id1_1_,
            orderiteml0_.id as id1_0_,
            orderiteml0_.order_id as order3_1_0_,
            orderiteml0_.product_id as product4_1_0_,
            orderiteml0_.QUANTITY as QUANTITY1_0_ 
        from
            T_ORDER_ITEM orderiteml0_ 
        where
            orderiteml0_.order_id=? 
        order by
            orderiteml0_.id
    

    总结

    昨天下午5点多遇到这个问题,知道晚上快8点才找到原因。期间尝试了很多,最后将hibernate的底册执行的SQL打印出来,才发现了问题所在。

    hibernate输出SQL的配置如下:

    	<!-- 输出sql-->
    	<property name="show_sql">true</property>
    	<!-- 格式化sql -->
     	<property name="format_sql">true</property>
    

    演示代码 https://gitee.com/anyway2025/issueDemo/tree/master/hibernate-demo-211202

  • 相关阅读:
    springboot ueditor 使用心得
    利用github和git命令,将本地项目共享到服务器上——第二章
    利用github和git命令,将本地项目共享到服务器上
    如何打造亚秒级加载的网页3——用户体验 总结
    如何打造亚秒级加载的网页2——网络性能 过程解读
    如何打造亚秒级加载的网页1——前端性能
    什么是跨域?怎么解决跨域?
    Vue生命周期
    利用JS实现vue中的双向绑定
    按照vue文档使用JavaScript钩子但是却不能执行动画?
  • 原文地址:https://www.cnblogs.com/haicheng92/p/15634358.html
Copyright © 2011-2022 走看看