4

I am facing a weird problem with n+1 select queries. My mapping looks like that:

@Entity
@IdClass(MyTablePK.class)
@Table(name = "my_table", schema = "schema")
public class MyTable {

    @Id
    @Column(name = "name", nullable = false, length = 12)
    private String name="";

    @Id
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "myStringValue", referencedColumnName = "myStringValue")
    private AdditionalData data;

    ... (other fields, getters, setters)
}

public class MyTablePK implements Serializable {

    private String name;

    private AdditionalData data;

    (getters,setters)

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        MyTablePK that = (MyTablePK) o;

        if (name!= null ? !name.equals(that.name) : that.name!= null) return false;
        return !(data!= null ? !data.equals(that.data) : that.data!= null);

    }

    @Override
    public int hashCode() {
        int result = name!= null ? name.hashCode() : 0;
        result = 31 * result + (data!= null ? data.hashCode() : 0);
        return result;
    }
}

@Entity
@Table(name = "info", schema = "schema")
public class AdditionalData implements Serializable {

    @Id
    @Column(name = "recno")
    private Long recno;

    @Column(name = "info1", length = 3)
    private String info1;

    @Column(name = "info2", length = 3)
    private String info2;

    ... (getters, setters)

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        AdditionalData data = (AdditionalData) o;

        return recno.equals(data.recno);

    }

    @Override
    public int hashCode() {
        return recno.hashCode();
    }
}

Now, I select all the values from MyTable. As expected I get n+1 selects, for every MyTable row a new AdditionalData query arrives. In order to fight that I wrote a join fetch query:

FROM MyTable mytab join fetch mytab.data

That however... did not change anything.

Now, the interesting thing is, that if I ignore for a moment business requirements, and remove @IdClass making name the only @Id - everything works correctly, all the data is got with a single query. Why is that? Can't I fight n+1 selects with a part of composite id?

In case it's relevant - I use Hibernate 4.3.5.Final with Oracle database

Deltharis
  • 2,320
  • 1
  • 18
  • 29
  • Can you post your MyTablePK class? – Alan Hay Nov 19 '15 at 12:58
  • @AlanHay added as per request – Deltharis Nov 19 '15 at 13:01
  • You have added part of it.......... What I am getting at is whether you have overriden hashCode() and equals() correctly. – Alan Hay Nov 19 '15 at 13:02
  • Oh... No, I did not override them. Is that a problem? – Deltharis Nov 19 '15 at 13:03
  • Yes, quite possibly first level cache miss because of this: See https://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing "Some JPA products also use the IdClass as a cache key to track an object's identity. Because of this, it is required (depending on JPA product) to implement an equals() and hashCode() method on the IdClass. Ensure that the equals() method checks each part of the primary key, and correctly uses equals for objects and == for primitives. Ensure that the hashCode() method will return the same value for two equal objects. – Alan Hay Nov 19 '15 at 13:06
  • If you can try and see if it works and, if so, I will post as an answer. – Alan Hay Nov 19 '15 at 13:07
  • @AlanHay Tried that, didn't change anything. Edited the question with equals and hashCode I used (IntelliJ generated). – Deltharis Nov 19 '15 at 13:12
  • Can you try **left** join fetch? – geert3 Nov 19 '15 at 13:41
  • Okay, and have you created corresponding equals() and hashcode() methods on your AdditionalData class so that data.equals(that.data) returns true? – Alan Hay Nov 19 '15 at 13:41
  • Can you post the AdditionalData class as that is what is being missed in 1st level cache. Changes I suggested above are probably not going to fix but not implementing equals() and hashcode() in the ID class will likely have other serious side effect. – Alan Hay Nov 19 '15 at 13:50
  • I may have misunderstood your problem. See the following question as hinted at by @geert3 http://stackoverflow.com/questions/463349/jpa-eager-fetch-does-not-join where one answer specifically covers your case. – Alan Hay Nov 19 '15 at 14:04
  • I can't find something immediately relevant in that question - making that join a `left join fetch` doesn't fix anything. I added AdditionalData class to the question. – Deltharis Nov 23 '15 at 10:01

1 Answers1

0

This might be related to this known issue here: https://hibernate.atlassian.net/browse/HHH-10292

Try to map the myStringValue column twice. Once as being part of the id and as String and another time as AdditionalData with insertable = false, updatable = false in the join column.

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58