Alfredo Osorio
Alfredo Osorio

Reputation: 11475

Why is EclipseLink querying twice for the same entity?

I am getting a strange behavior with EclipseLink. I added @BatchFetch(value=BatchFetchType.IN) to a relationship in this case to Address from Student. I am checking the generated SQL by EclipseLink when I query all students which are two and it is querying the same Address and a Student twice:

EclipseLink version 2.4.0

First the queries without BatchFetch:

SELECT ID, NAME FROM STUDENT
SELECT ID, DESCRIPTION, STUDENT_ID FROM ADDRESS WHERE (STUDENT_ID = ?)
    bind => [1]
SELECT ID, DESCRIPTION, STUDENT_ID FROM ADDRESS WHERE (STUDENT_ID = ?)
    bind => [2]

Expected SQL but now when I add the BatchFetch to Address relationship this is what is generated:

SELECT ID, NAME FROM STUDENT
SELECT ID, DESCRIPTION, STUDENT_ID FROM ADDRESS WHERE (STUDENT_ID IN (?,?))
    bind => [1, 2]
SELECT ID, NAME FROM STUDENT WHERE (ID = ?)
    bind => [2]
SELECT ID, DESCRIPTION, STUDENT_ID FROM ADDRESS WHERE (STUDENT_ID = ?)
    bind => [2]

Why is EclipseLink querying for a student if it already have all of them from the first query "SELECT ID, NAME FROM STUDENT" and also why is it querying ADDRESS if it already got it from SELECT ID, DESCRIPTION, STUDENT_ID FROM ADDRESS WHERE (STUDENT_ID IN (1,2)) This strange behavior happens only when you add the BatchFetch annotation.

As you can see it is querying twice the Student and the Address.

SELECT ID, NAME FROM STUDENT WHERE (ID = ?)
    bind => [2]

SELECT ID, DESCRIPTION, STUDENT_ID FROM ADDRESS WHERE (STUDENT_ID = ?)
    bind => [2]

These are the classes:

Student.java:

@Entity
public class Student implements Serializable {
    private Long id;
    private String name;
    private List<Address> addresses = new ArrayList<Address>();
    private List<Classroom> classrooms = new ArrayList<Classroom>();


    public Student() {

    }

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQSTUDENTID")
    @SequenceGenerator(name="SEQSTUDENTID", sequenceName="SEQSTUDENTID", allocationSize=1)
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @OneToMany(mappedBy="student", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @BatchFetch(value=BatchFetchType.IN)
    public List<Address> getAddresses() {
        return addresses;
    }

    public void setAddresses(List<Address> addresses) {
        this.addresses = addresses;
    }

    @ManyToMany(mappedBy="students", cascade=CascadeType.ALL, fetch=FetchType.LAZY)
    public List<Classroom> getClassrooms() {
        return classrooms;
    }

    public void setClassrooms(List<Classroom> classrooms) {
        this.classrooms = classrooms;
    }

}

Address.java:

@Entity
public class Address implements Serializable {
    private Long id;
    private Student student;
    private String description;

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQADDRESSID")
    @SequenceGenerator(name="SEQADDRESSID", sequenceName="SEQADDRESSID", allocationSize=1)
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @ManyToOne
    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

Test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/META-INF/application-context-root.xml"})
@Transactional
public class TestingFetch {

    @PersistenceContext
    private EntityManager entityManager;
    private Student student1;
    private Student student2;

    @Before
    public void setUp() {
        List<Student> students = new ArrayList<Student>();

        student1 = new Student();
        student1.setName("Alfredo Osorio");

        Address student1Address1 = new Address();
        student1Address1.setDescription("FirstAddressStudent1");
        student1Address1.setStudent(student1);
        student1.getAddresses().add(student1Address1);

        Address student1Address2 = new Address();
        student1Address2.setDescription("SecondAddressStudent1");
        student1Address2.setStudent(student1);
        student1.getAddresses().add(student1Address2);

        students.add(student1);

        student2 = new Student();
        student2.setName("Jorge Ramirez");
        students.add(student2);

        Address student2Address1 = new Address();
        student2Address1.setDescription("FirstAddressstudent2");
        student2Address1.setStudent(student2);
        student2.getAddresses().add(student2Address1);

        Address student2Address2 = new Address();
        student2Address2.setDescription("SecondAddressstudent2");
        student2Address2.setStudent(student2);
        student2.getAddresses().add(student2Address2);

        Classroom classroom1 = new Classroom();
        classroom1.setName("Mathematics");

        Classroom classroom2 = new Classroom();
        classroom2.setName("Physics");

        Classroom classroom3 = new Classroom();
        classroom3.setName("Chemistry");

        classroom1.getStudents().add(student1);
        student1.getClassrooms().add(classroom1);
        classroom1.getStudents().add(student2);
        student2.getClassrooms().add(classroom1);

        classroom2.getStudents().add(student1);
        student1.getClassrooms().add(classroom2);

        classroom3.getStudents().add(student2);
        student2.getClassrooms().add(classroom3);

        for (Student student : students) {
            entityManager.persist(student);         
        }

        entityManager.flush();
        entityManager.clear();
    }

    @Test
    public void testFetch1() {
        String jpql = "select m from Student m";
        Query query = entityManager.createQuery(jpql);
        List<Student> list = (List<Student>)query.getResultList();
    }
}

Upvotes: 0

Views: 718

Answers (1)

James
James

Reputation: 18379

Remove the EAGER from your relationship. The EAGER is causing the batch query to be executed before the first query is done, causing the extra selects.

(i.e. while building the first student the batch query selects all of the addresses, and the address has a ManyToOne back to Student that is also EAGER so causes the query.

Upvotes: 1

Related Questions