开发者

Hibernate JPA2 - n+1 select problem affects only one side of OneToOne relation

I was not getting lazy loading on either class until I added bytecode instrumentation via:

  <plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
      <execution>
        <phase>process-classes</phase>
        <goals>
          <goal>run</goal>
        </goals>
      </execution>
    </executions>
    <configuration>
      <tasks>
        <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
          <classpath>
            <path refid="maven.runtime.classpath" />
            <path refid="maven.plugin.classpath" />
          </classpath>
        </taskdef>
        <instrument verbose="false">
          <fileset dir="${project.build.outputDirectory}">
            <include name="**/db/**/*.class" />
          </fileset>
        </instrument>
      </tasks>
    </configuration>
  </plugin>

Here are my two entity classes greatly trimmed down:

This tables "from Ininvgrmtr" works as I want (no n+1 issue):

@Entity
@Table(name = "ININVGRMTR", catalog = "CO05IN", schema = "")
@XmlRootElement
public class Ininvgrmtr implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 8)
    @Column(name = "IGMGRUP", nullable = false, length = 8)
    private String igmgrup;

    //other attributes

    @JoinColumn(name = "IGMGRUP", referencedColumnName = "IGGRUP", nullable = false, insertable = false, updatable = false)
    @OneToOne(optional = false, fetch=FetchType.LAZY)
    private Ininvgrp ininvgrp;
}

This tables "from Ininvgrp" does not:

@Entity
@Table(name = "ININVGRP", catalog = "CO05IN", schema = "")
@XmlRootElement
public class Ininvgrp implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 8)
    @Column(name = "IGGRUP", nullable = false, length = 8)
    private String iggrup;

    //other attributes

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "ininvgrp", fetch=FetchType.LAZY)
    private Ininvgrmtr ininvgrmtr;

    //getters setters
} 

To illustrate the problem:

entityManagerFactory.createEntityManager().createQuery("from Ininvgrmtr").ge开发者_如何学GotResultList();

Prints the following to the log (which is good):

INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_, ininvgrmtr0_.IGMTRACE as IGMTRACE96_ from CO05IN.ININVGRMTR ininvgrmtr0_

While,

entityManagerFactory.createEntityManager().createQuery("from Ininvgrp").getResultList();

Prints prints the following:

INFO: Hibernate: select ininvgrp0_.IGGRUP as IGGRUP97_, ininvgrp0_.Added as Added97_, ininvgrp0_.IGABCF as IGABCF97_, ininvgrp0_.IGADCN as IGADCN97_, ininvgrp0_.IGADDT as IGADDT97_, ininvgrp0_.IGADUS as IGADUS97_, ininvgrp0_.IGCAT as IGCAT97_, ininvgrp0_.IGDESC as IGDESC97_, ininvgrp0_.IGMDCN as IGMDCN97_, ininvgrp0_.IGMDDT as IGMDDT97_, ininvgrp0_.IGMDUS as IGMDUS97_, ininvgrp0_.IGRETH as IGRETH97_, ininvgrp0_.IGSTA as IGSTA97_, ininvgrp0_.IGTYPE as IGTYPE97_, ininvgrp0_.IGUBAS as IGUBAS97_, ininvgrp0_.IGUSEL as IGUSEL97_, ininvgrp0_.IGUWGT as IGUWGT97_, ininvgrp0_.Modified as Modified97_ from CO05IN.ININVGRP ininvgrp0_
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_0_, ininvgrmtr0_.IGMTRACE as IGMTRACE96_0_ from CO05IN.ININVGRMTR ininvgrmtr0_ where ininvgrmtr0_.IGMGRUP=?
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_0_, ininvgrmtr0_.IGMTRACE as IGMTRACE96_0_ from CO05IN.ININVGRMTR ininvgrmtr0_ where ininvgrmtr0_.IGMGRUP=?
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_0_, ininvgrmtr0_.IGMTRACE as IGMTRACE96_0_ from CO05IN.ININVGRMTR ininvgrmtr0_ where ininvgrmtr0_.IGMGRUP=?
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_0_, ininvgrmtr0_.IGMTRACE as IGMTRACE96_0_ from CO05IN.ININVGRMTR ininvgrmtr0_ where ininvgrmtr0_.IGMGRUP=?
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_0_, ininvgrmtr0_.IGMTRACE as IGMTRACE96_0_ from CO05IN.ININVGRMTR ininvgrmtr0_ where ininvgrmtr0_.IGMGRUP=?
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_0_, ininvgrmtr0_.IGMTRACE as IGMTRACE96_0_ from CO05IN.ININVGRMTR ininvgrmtr0_ where ininvgrmtr0_.IGMGRUP=?
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_0_, ininvgrmtr0_.IGMTRACE as IGMTRACE96_0_ from CO05IN.ININVGRMTR ininvgrmtr0_ where ininvgrmtr0_.IGMGRUP=?
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_0_, ininvgrmtr0_.IGMTRACE as IGMTRACE96_0_ from CO05IN.ININVGRMTR ininvgrmtr0_ where ininvgrmtr0_.IGMGRUP=?
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_0_, ininvgrmtr0_.IGMTRACE as IGMTRACE96_0_ from CO05IN.ININVGRMTR ininvgrmtr0_ where ininvgrmtr0_.IGMGRUP=?
...

What is causing this?


The property on Ininvgrmtr is marked not-nullable and not optional, so hibernate knows that there has to be an entity with the given id. Hibernate can then create a dynamic proxy and set it as the property value. This proxy will then only get initialized when you access its properties.

In the other direction, from Ininvgrp, the property is marked as optional by default. Hibernate can't use a dynamic proxy in that case since it has to return null if there is no matching entity.

With bytecode weaving each access to the field itself can be modified to query the database. If you can use bytecode instrumentation in your build process then that would be the preferred solution.

Another workaround would be to declare the property as a OneToMany relation and translate from an empty / one element list to null or its first element in the getters and setters like this:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "ininvgrp", fetch=FetchType.LAZY)
private List<Ininvgrmtr> ininvgrmtr;

public void setIninvgrmtr(Ininvgrmtr ininvgrmtr) {
    if (this.ininvgrmtr == null || this.ininvgrmtr.isEmpty()) {
        this.ininvgrmtr = Collections.singletonList(ininvgrmtr);
    } else {
        this.ininvgrmtr.set(0, ininvgrmtr);
    }
}

public Ininvgrmtr getIninvgrmtr() {
    return ininvgrmtr == null || ininvgrmtr.isEmpty() ? null : ininvgrmtr.get(0);
}

Edit: A more detailed description of the problem with some references can be found in this blog post.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜