简体   繁体   中英

JPA bidirectional 1..N association, avoid querying in child to set parent

I am using Spring Data JPA + Hibernate for a webapp. For a particular domain model A, we have a 1-to-many association in another domain B. Such that A will have a Set getB() and B will have A getA().

While querying for a A graph, I see hibernate is using 1+n queries. A single outer join query for fetching the A graph, but then 'n' queries for setting A in each B.

Am I missing any pattern here? Since all the childs have the same parent, is not somehow possible to avoid these 'n' queries?





    @MappedSuperclass
    @Data
    public abstract class Batch implements Serializable {

      private static final long serialVersionUID = 1L;

      @OneToOne(fetch = FetchType.EAGER)
      @JoinColumn(name = "batch_id", referencedColumnName = "batch_id")
      protected BatchID batchId;

    }


    /*
    //The parent class in a simplified form
    */
    @Entity
    @Table(name = "DRYRUN")
    @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
    public class DryrunBatch extends Batch {

      /**
       * 
       */
      private static final long serialVersionUID = -1596595930859735318L;
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      @Getter@Setter
      protected Long id;

      public DryrunTNStatus newTNStatus()
      {
        final DryrunTNStatus tn = new DryrunTNStatus();
        tn.setBatch(this);
        getTnStatus().add(tn);
        return tn;
      }

      @OneToMany(fetch = FetchType.LAZY, mappedBy = "batch")
      @Getter@Setter
      private Set tnStatus = new HashSet();
    }


    //The child class in a simplified form

    @Entity
    @Table(name = "DRYRUN_TN_STATUS")
    @Data
    public class DryrunTNStatus implements Serializable{

      /**
       * 
       */
      private static final long serialVersionUID = -4388406636444350023L;

      public DryrunTNStatus(String accountNo, String telNo) {
        super();

        this.accountNo = accountNo;
        this.telNo = telNo;
      }

      @ManyToOne(fetch = FetchType.LAZY)
      @JoinColumn(name = "BATCH_ID", referencedColumnName = "BATCH_ID")
      private DryrunBatch batch;

      public DryrunTNStatus()
      {

      }
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      protected Long id;

    }

The code to fetch the object graph using JpaRepository. Using Spring JPA support to enforce an outer join. I preferred this over Hibernate's @Fetch annotation.



    DryrunBatch drBatch = drBatchRepo.findOne(new Specification() {

          @Override
          public Predicate toPredicate(Root root, CriteriaQuery query,
              CriteriaBuilder cb) {
            query.distinct(true);
            root.fetch("tnStatus", JoinType.LEFT);
            return cb.equal(root.get("batchId").get("id"),
                batch.getId());

          }
        });

And finally the hibernate queries from log. I am running a junit that fetches a parent with 10 childs from DB.



    //this query can fetch data for the complete graph??
    Hibernate: select distinct dryrunbatc0_.id as id1_6_0_, tnstatus1_.id as id1_9_1_[etc..] from dryrun dryrunbatc0_ left outer join dryrun_tn_status tnstatus1_ on dryrunbatc0_.batch_id=tnstatus1_.batch_id where dryrunbatc0_.batch_id=15

    //and then 10 queries like
    Hibernate: select dryrunbatc0_.id as id1_6_3_, [etc..] from dryrun dryrunbatc0_ left outer join batch_id batchid1_ on dryrunbatc0_.batch_id=batchid1_.batch_id inner join users user2_ on dryrunbatc0_.created_by=user2_.login_id left outer join dryrun_tn_status tnstatus3_ on dryrunbatc0_.batch_id=tnstatus3_.batch_id where dryrunbatc0_.batch_id=?

You've encountered the famous N+1 problem with lazy loading. There is no JPA standard way to tackle this, however, every JPA provider provides means to turn on "Batch fetching", which will load all lazy references at once instead loading each in a single SQL query.

Here is information on how to turn it on in hibernate .

Here is an article with explanation of how batch fetching works and examples using eclipselink.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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