Spring Data Jpa Lazy Загрузка не работает, как ожидалось - PullRequest
0 голосов
/ 18 апреля 2020

Эй, я работаю над Spring Data jpa, и я хотел бы только лениво получать данные.

Я использую lombook в своем весеннем проекте

Я хотел бы получить пользователя и его роли по электронной почте.

У меня есть несколько классов

@Entity
@Table(name = "user")
@Data
public class User extends DateAudit {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    @Column(columnDefinition = "char(50)")
    private String email;

    @Column(columnDefinition = "char(15)")
    private String phone;

    private String password;

    @Column(columnDefinition = "bit")
    private boolean isActive;

    @Column(columnDefinition = "ENUM('MALE', 'FEMALE', 'OTHER')")
    @Enumerated(EnumType.STRING)
    private Gender gender;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
    private List<UserRole> userRoles;

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
    private UserProfile userProfile;

}
@Entity
@Table(name = "user_profile")
@Data
public class UserProfile extends DateAudit {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String picture;
    private float avgRating;

    @Column(columnDefinition = "char(13)")
    private String cnic;

    @Temporal(TemporalType.DATE)
    private Date dateOfBirth;

    //relations

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "city_id")
    private City city;
}
@Entity
@Data
@Table
public class UserRole {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "role_id")
    private Role role;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "hospital_id")
    private Hospital hospital;
}

А вот мой пользовательский репозиторий

@Repository
public interface UserRepository extends CrudRepository<User, Integer> {

    @EntityGraph(attributePaths = {"userRoles"})
    User findByEmail(String email);
}

Ниже мое действие, которое я Я использую для получения пользователя

    @GetMapping("/test")
    public String test(){

        userRepository.findByEmail("admin@domain.com");
        return "test";
    }

Я добавил свойство ниже в application.properties

spring.jpa.properties.hibernate.show_sql=true

, поэтому, когда я нажимаю на моё / тестовое действие от почтальона, я вижу два запроса, выполненных в моем приложении console.

Hibernate: select user0_.id as id1_21_0_, userroles1_.id as id1_23_1_, user0_.created_at as created_2_21_0_, user0_.updated_at as updated_3_21_0_, user0_.email as email4_21_0_, user0_.gender as gender5_21_0_, user0_.is_active as is_activ6_21_0_, user0_.name as name7_21_0_, user0_.password as password8_21_0_, user0_.phone as phone9_21_0_, userroles1_.hospital_id as hospital2_23_1_, userroles1_.role_id as role_id3_23_1_, userroles1_.user_id as user_id4_23_1_, userroles1_.user_id as user_id4_23_0__, userroles1_.id as id1_23_0__ from user user0_ left outer join user_role userroles1_ on user0_.id=userroles1_.user_id where user0_.email=?
Hibernate: select userprofil0_.id as id1_22_0_, userprofil0_.created_at as created_2_22_0_, userprofil0_.updated_at as updated_3_22_0_, userprofil0_.avg_rating as avg_rati4_22_0_, userprofil0_.city_id as city_id8_22_0_, userprofil0_.cnic as cnic5_22_0_, userprofil0_.date_of_birth as date_of_6_22_0_, userprofil0_.picture as picture7_22_0_, userprofil0_.user_id as user_id9_22_0_ from user_profile userprofil0_ where userprofil0_.user_id=?

Я не заинтересован в профиле пользователя. что я тут не так делаю.

Ответы [ 2 ]

0 голосов
/ 20 апреля 2020

Это происходит потому, что это двунаправленная ассоциация, и Hibernate должен знать, должен ли он инициализировать атрибут userProfile с нулевым или прокси-классом, и это можно выяснить только путем запроса таблицы user_profile.

Вы можете избежать он удаляет столбец внешнего ключа и использует одно и то же значение первичного ключа для обеих связанных сущностей, для этого необходимо использовать аннотацию MapsId на стороне владельца, например:

@Entity
@Table(name = "user_profile")
@Data
public class UserProfile extends DateAudit {

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private Long id;

 @OneToOne
 @MapsId
 @JoinColumn(name = "id")
 private User user;
.
.
.
}

Здесь вы можете ознакомиться с полным руководством по этой проблеме. https://thoughts-on-java.org/hibernate-tip-lazy-loading-one-to-one/


Другой вариант, если вы не хотите использовать один и тот же ключ, это просто отобразить это как отношение OneToMany.

0 голосов
/ 18 апреля 2020

Отношения один к одному загружаются с нетерпением. Даже если мы даем команду hibernate на ленивую загрузку, hibernate игнорирует эту подсказку.

Чтобы решить эту проблему, нам нужно включить улучшение байт-кода.

Enhancement Bytecode

В нашем случае мы используем плагин улучшения байт-кода, который расширяет байт-код классов сущностей и позволяет нам использовать стратегию отложенного извлечения без прокси. Мы можем определить плагин в файле pom. xml следующим образом:

<build>
<plugins>
    <plugin>
        <groupId>org.hibernate.orm.tooling</groupId
        <artifactId>hibernate-enhance-maven-plugin</artifactId>
        <version>5.4.2.Final</version>
        <executions>
        <execution>
        <configuration><enableLazyInitialization>true</enableLazyInitialization></configuration>
        <goals>
            <goal>enhance</goal>
        </goals>
        </execution>
        </executions>
    </plugin>
</plugins>
</build>

Включение прокси-ленивых ассоциаций

Теперь нам нужно добавить @LazyToOne аннотация в классах сущностей, позволяющая hibernate узнать, что мы хотим включить прокси-ленивую выборку для связанных сущностей.

Обновление:

По умолчанию все ленивые свойства сущности Класс принадлежит группе с именем DEFAULT. И выбор любого свойства из группы DEFAULT выбирает также и другие. Чтобы решить эту проблему, нам нужно определить группы, которые мы будем sh выбирать отдельно, используя аннотацию @LazyGroup.

В нашем случае мы хотим, чтобы вложенные объекты выбирались по отдельности. Итак, мы помечаем как userRoles, так и userProfiles аннотацией @LazyGroup, как показано ниже.

@OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
@LazyToOne(LazyToOneOption.NO_PROXY)
@LazyGroup("userProfile")
private UserProfile userProfile;
...