JPA 一对多延迟加载与关系维护

本文探讨了JPA中一对多关系的延迟加载特性,如何在需要时才执行SQL获取数据,提高性能。默认情况下,一对多关系采用延迟加载,而多对一关系需通过注解开启。同时提到了属性级延迟加载的限制,如大字段可以通过单独设置实体并使用OneToOne懒加载来解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JPA一对多延迟加载(orders和orderdetails), 对于一次查询大量数据和多线程并发非常有帮助。

1. 一对多一般默认就是延迟加载, 在用到具体属性的时候才会执行SQL去抓取数据。

entity.getItems(); //不执行sql
entity.getItems().getProperty(); // 具体使用的才会执行sql

   1.1 多对一默认是关闭懒加载的, 需要注解显示开启懒加载

2. 属性级延迟加载注解(就算是blob等大字段JPA好像也不支持, 可以单独设置为一个实体, 然后做OneToOne懒加载)

@Basic(fetch = FetchType.Lazy)

    package cn.itcast.bean;   
      
    import java.util.HashSet;   
    import java.util.Set;   
      
    import javax.persistence.CascadeType;   
    import javax.persistence.Column;   
    import javax.persistence.Entity;   
    import javax.persistence.FetchType;   
    import javax.persistence.GeneratedValue;   
    import javax.persistence.Id;   
    import javax.persistence.OneToMany;   
    import javax.persistence.Table;   
      
    @Entity  
    @Table(name="orders")   //把表名改成orders(默认表名是order),防止默认表名order与数据库的关键字"order by"中的order冲突。不改的话测试不成功,出现异常,orderitem表建立成功,order表建不了。   
    public class Order {   
        private String orderId;   
        private Float amount = 0f;   
        private List<OrderDetail> orderDetails= new ArrayList<OrderDetail>();   
      
        @Id //要注意:目前JPA规范并没有提供UUID这种生成策略,目前主键值只提供了整型的生成方式,所以@GeneratedValue这个注解就不能在这里用上,不能对字符串进行id自增长。   
        @Column(length = 12)   
        public String getOrderId() {   
            return orderId;   
        }   
      
        public void setOrderId(String orderId) {   
            this.orderId = orderId;   
        }   
      
        @Column(nullable = false)   
        public Float getAmount() {   
            return amount;   
        }   
      
        public void setAmount(Float amount) {   
            this.amount = amount;   
        }   
      
        @OneToMany(cascade = { CascadeType.REFRESH, CascadeType.PERSIST,   
                CascadeType.MERGE, CascadeType.REMOVE },fetch=FetchType.LAZY,mappedBy="order")   
        //mappedBy="order",中的order是关系维护端的order属性,这个order属性的类型是这个bean。   
        public Set<OrderDetail> getOrderDetails() {   
            return orderDetails;   
        }   
        /*  
        @OneToMany(fetch=FetchType.)的选项有,如下图:  
            FetchType.EAGER:代表立即加载;  
            FetchType.LAZY:代表延迟加载。  
                  
        当我们把fetch设置为FetchType.LAZY的时候,什么时候初始化items里面的数据呢?当我们第一次访问这个属性,并对这个属性进行操作的时候,这个集合的数据才会从数据库里面load出来。但要注意:当我们访问这个延迟属性的时候,我们的前提要EntityManager这个对象没有被关闭,如果被关闭了我们再去访问延迟属性的话,就访问不到,并抛出延迟加载意外。  
        如果没有设置fetch这属性的话,会怎么样呢?是立即加载?还是延迟加载呢?  
        记住@OneToMany这个标签最后的英文单词,如果是要得到Many的一方,我不管你前面是什么,只要后面的单词是Many,也就是说要得到多的一方,你们就给我记住,默认的加载策略就是延迟加载(Many记录可能上几万条,立即加载的话可能对效率影响大,所以延迟加载)。  
        反过来,如果后面是One呢?因为它是加载一的一方,这对性能影响不是很大,所以它的默认加载策略是立即加载。  
          
        mappedBy:我们怎么知道关系的维护端和被维护端呢?当然JPA规范规定多的一端应该是为维护端(关系维护段增加一个字段为外键,里面保存的是一的一端的主键),一的一端为关系被维护端,那么我们总要在程序里给他们打上标志吧?虽然规范是这么规定,但总要申明一下吧?就是通过mappedBy属性,只要哪个类出现了mappedBy,那么这个类就是关系的被维护端。里面的值指定的是关系维护端。  
        orderDetail这边由哪一个属性去维护关系呢?是OrderItem类的order属性。  
        mappedBy属性对应Hibernate里面的inverse属性:<SET name="orderDetails" inverse="true"></SET>  
          
        */  
      
        public void setOrderDetails(List<OrderDetail> orderDetails) {   
            this.orderDetails<span style="font-family: Arial, Helvetica, sans-serif;"> </span>= orderDetails;   
        }   
      
        //用这个方法会方便很多   
        public void addOrderDetail(OrderDetail orderDetail){   
            orderDetail.setOrder(this);   //关系维护方orderDetail加入关系被维护方(this)后,才能维护更新关系(orderDetail表中的外键字段order_id),维护关系其实就是更新外键。只有为关系维护端设置了关系被维护端,关系才能建立起来。   
            this.orderDetails.add(orderDetail);   
        }
        
        public void removeOrderDetail(OrderDetail orderDetail){
             orderDetail.setOrderDetail(null);
             this.orderDetails.remove(orderDetail);
        }


    package cn.itcast.bean;   
      
    import javax.persistence.CascadeType;   
    import javax.persistence.Column;   
    import javax.persistence.Entity;   
    import javax.persistence.GeneratedValue;   
    import javax.persistence.Id;   
    import javax.persistence.JoinColumn;   
    import javax.persistence.ManyToOne;   
      
    @Entity  
    public class OrderDetail {   
        private Integer id;   
        private String productName;   
        private Float sellPrice = 0f;   //默认值为0。   
        private Order order;   
      
        @Id  
        @GeneratedValue  //id自增长方式生成主键。   
        public Integer getId() {   
            return id;   
        }   
      
        public void setId(Integer id) {   
            this.id = id;   
        }   
      
        @Column(length = 40, nullable = false)   
        public String getProductName() {   
            return productName;   
        }   
      
        public void setProductName(String productName) {   
            this.productName = productName;   
        }   
      
        @Column(nullable = false)   
        public Float getSellPrice() {   
            return sellPrice;   
        }   
      
        public void setSellPrice(Float sellPrice) {   
            this.sellPrice = sellPrice;   
        }   
      
        @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)   
        @JoinColumn(name="order_id")   //设置外键的名称。   
        public Order getOrder() {   //OrderItem是关系维护端,负责关系更新,它是根据它的order属性值维护关系的。当它保存的时候(主动保存或是被级联保存),他会根据order属性的值更新关系,当order为null时,就不会更新关系了。级联操作也是根据双方对象中的映射属性值进行的,当映射属性没值的时候就不会对对方进行级联操作了。   
            return order;   
        }   
        /*  
        @ManyToOne的级联保存(CascadeType.PERSIST)是不需要的,不可能说你保存某个订单项OrderItem的时候,也保存订单Order的。通常都是保存订单Order的时候,保存订单项OrderItem的。  
        CascadeType.MERGE:如果我们更新了订单项orderItem产品的价钱,那么整个订单Order的总金额是会发生改变的,所以可以定义这个级联更新。  
        CascadeType.REFRESH:如果我们想得到目前数据库里orderItem最新的数据的话,我们也希望得到订单order的最新数据,我们可以定义这个级联刷新,就是说把数据库里最新的数据重新得到。  
        CascadeType.REMOVE:这个属性这里肯定不设。就好比现在有一个订单,一个订单里面有3个购物项orderItem,买了A,B,C三个产品,我现在不要A这个产品了,我们只是把A这条记录删掉,那么如果这里定义了级联删除的话,那么你删除A记录的同时,也会把整个订单也删除掉,所以这里不需要设级联删除。  
        */  
        //optional:说明order这个是否是可选的?是否可以没有的?false表示必须的,true表示是可选的。   
      
        public void setOrder(Order order) {   
            this.order = order;   
        }   
    }   



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值