简体   繁体   中英

Hibernate: N+1 fix for @OneToOne

I have @OneToOne relationship with classes:

@Entity
@Table(name = "persons", schema = "persons_info")
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "first_name",
        nullable = false)
    @JsonProperty("first_name")
    private String firstName;

    @Column(name = "last_name",
        nullable = false)
    @JsonProperty("last_name")
    private String lastName;

    @Basic
    @Column(name = "birth_date", nullable = false)
    private Date birthDate;

    @OneToOne(cascade = CascadeType.ALL,
        orphanRemoval = true)
    @JoinColumn(name = "address_id",
        referencedColumnName = "id")
    private Address address;
    
    // setters, getters, equals...
}

Address:

@Entity
@Table(name = "addresses", schema = "persons_info")
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String city;

    @Column(nullable = false)
    private String street;

    @Column(nullable = false)
    private String house;

    @Column(nullable = false)
    private String flat;

    @JsonIgnore
    @OneToOne(mappedBy = "address")
    private Person person;

    // setters, getters, equals...
}

I have a default JpaRepository for person:

@Repository
public interface PersonRepository
    extends JpaRepository<Person, Long> {
}

And when i calling repository.findAll() i taking n+1 problem:

    Hibernate:
        select
            a1_0.id,
            a1_0.city,
            a1_0.flat,
            a1_0.house,
            p1_0.id,
            p1_0.birth_date,
            p1_0.first_name,
            p1_0.last_name,
            a1_0.street
        from
            persons_info.addresses a1_0
        left join
            persons_info.persons p1_0
                on a1_0.id=p1_0.address_id
        where
            a1_0.id=?
    Hibernate:
        select
            a1_0.id,
            a1_0.city,
            a1_0.flat,
            a1_0.house,
            p1_0.id,
            p1_0.birth_date,
            p1_0.first_name,
            p1_0.last_name,
            a1_0.street
        from
            persons_info.addresses a1_0
        left join
            persons_info.persons p1_0
                on a1_0.id=p1_0.address_id
        where
            a1_0.id=?

How i can fix that? (I want fetching with JOIN like this example: SELECT * FROM persons INNER JOIN address ON person.address_id = address.id )

Solved by adding @Query annotation:

@Repository
public interface PersonRepository
    extends JpaRepository<Person, Long> {
    @Query(nativeQuery = true,
        value = """
            SELECT
              person.id AS person_id,
              person.address_id,
              person.first_name,
              person.last_name,
              person.birth_date,
              address.id,
              address.city,
              address.street,
              address.house,
              address.flat
            FROM persons_info.persons person
              INNER JOIN persons_info.addresses address
                ON person.address_id = address.id""")
    public List<Person> findAll();
}

Maybe that's not best solution (if we will adding more properties to object we need update query everytime) but thats work very fine:

PersonServiceTest > Find all STANDARD_OUT
    Hibernate:
        SELECT
            person.id AS person_id,
            person.address_id,
            person.first_name,
            person.last_name,
            person.birth_date,
            address.id,
            address.city,
            address.street,
            address.house,
            address.flat
        FROM
            persons_info.persons person
        INNER JOIN
            persons_info.addresses address
                ON person.address_id = address.id

Solved by adding @Query annotation:

@Repository
public interface PersonRepository
    extends JpaRepository<Person, Long> {
    @Query("""
      SELECT p FROM Person p
        LEFT JOIN FETCH p.address a""")
    public List<Person> findAll();
}

Now that's looks like:

    Hibernate:
        select
            p1_0.id,
            a1_0.id,
            a1_0.city,
            a1_0.flat,
            a1_0.house,
            a1_0.street,
            p1_0.birth_date,
            p1_0.first_name,
            p1_0.last_name
        from
            persons_info.persons p1_0
        left join
            persons_info.addresses a1_0

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