Hibernate кэш второго уровня с реализацией Hazelcast, элементы коллекции извлекаются не из кеша, а из базы данных - PullRequest
0 голосов
/ 25 октября 2019

У меня «странное» поведение с кешем второго уровня, когда элементы из коллекции попадают не из кеша, а из базы данных.

Существует три класса для моделирования отношений ManyToMany с атрибутами междупользователь и группа.

Класс Person, от которого наследуются классы User и Group:

@Entity
@DiscriminatorColumn(name = "userType", discriminatorType = DiscriminatorType.INTEGER)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Person implements Serializable {
...
}


@Entity
@DiscriminatorValue("2")
public class User extends Person {

    @OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, orphanRemoval = true, fetch = FetchType.LAZY)
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    protected Set<UserGroup> userGroups = new HashSet<>();
}


@Entity
@DiscriminatorValue("2")
public class Group extends Person {

    @OneToMany(mappedBy = "group", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    protected Set<UserGroup> users = new HashSet<>();

}

Класс userGroup, сопоставляющий промежуточную таблицу с дополнительными атрибутами

@Entity
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@NoArgsConstructor
public class UserGroup implements Serializable, Ientity<PkUserGroup> {

    @EmbeddedId
    @NotNull
    private PkUserGroup id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "idUser", referencedColumnName = "id", insertable = false, updatable = false)
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "idGroup", referencedColumnName = "id", insertable = false, updatable = false)
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private Group group;

    ...

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

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

}

Встраиваемый ключ для идентификатора класса UserGroup

@Embeddable
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
@ToString
public class PkUserGroup implements Serializable {

    private Long idUser;
    private Long idGroup;

}

Все сущности и коллекции настроены для кэширования.

Я сделал тест, в котором извлекаю пользователя из БД изего идентификатор, затем я получаю на коллекции UserGroup и Hibernate сгенерировал запрос из-за конфигурации режима Lazy. Я делал это несколько раз в цикле.

@Test
@Transactional
public void level2QueryCacheSelectTest() {

    for (int i = 0; i < 5; i++) {

        Optional<User> oUser = this.userRepository.findById(1l);

        User user = oUser.get();

        logger.info(user.toString());

        for (UserGroup ug : user.getUserGroups()) {
            logger.info(ug.toString());
        }
    }
}

Hibernate генерирует два выбора на первой итерации, как и ожидалось, затем использует кэш первого уровня и печатает те же результаты.

пользовательrequest:

select
    user0_.id as id2_1_0_,
    user0_.name as name7_1_0_,
    ...
from
    users user0_ 
where
    user0_.id=? 
    and user0_.user_type=1

userGroups, извлеченные с использованием только идентификатора пользователя, элементы коллекции User.userGroups

select
    usergroups0_.id_user as id_user2_6_0_,
    usergroups0_.id_group as id_group1_6_0_,
    ...
from
    user_group usergroups0_ 
where
    usergroups0_.id_user=?

Вот результат в кеше второго уровня, который я могусм. с Hazelcast mancenter.

enter image description here

Пользователь является членом 4 групп, поэтому в области oauth2.domain.UserGroup ожидается 4 записи,и 1 коллекция в области oauth2.domain.userGroups.

Теперь при втором выполнении теста все идет не так:)

На первой итерации теста Hibernate делает 4 запросав таблицу UserGroup, используя идентификатор пользователя и идентификатор группы. Таким образом, он может получить коллекцию в кеше (поскольку он уже знает идентификатор группы), но не элементы коллекции, которые должны находиться в области oauth2.domain.UserGroup.

    select
        usergroup0_.id_group as id_group1_6_0_,
        usergroup0_.id_user as id_user2_6_0_,
    from
        user_group usergroup0_ 
    where
        usergroup0_.id_group=? 
        and usergroup0_.id_user=?

binding parameter [1] as [BIGINT] - [9]
binding parameter [2] as [BIGINT] - [1]

то же самое для 3 других групп пользователей

Теперь в кеше у меня есть 8 элементов группы пользователей

enter image description here

Я не уверен, почему элементы не найдены в кеше, я реализовал хэш-код и эквиваленты класса UserGroup, используя встроенный идентификатор. Но я не уверен, как Hazelcast делает работу, так что, возможно, есть неправильная конфигурация. Если у вас есть идеи, как я могу решить эту проблему, пожалуйста.

Версии:

Спящий режим: 5.3.12. Окончательный вариант

Hazelcast-hibernate53: 1.3.2

Hazelcast: 3.12.3

Spring boot: 2.0.0.RELEASE

Вот конфигурация Spring-boot для Hazelcast и jpa.

spring:
    datasource:
        jdbc-url: jdbc:mysql://mysql-server:3306/oauth2
        username: ********
        password: ********
        initialization-mode: never
    jpa:
        hibernate:
            ddl-auto: update
        properties:
            hibernate:
                cache:
                    #required - turn on L2 cache.
                    use_second_level_cache: true
                    #optional - turn on query cache.
                    use_query_cache: true
                    hazelcast:
                      use_native_client: true
                      native_client_address: hazelcast-member:5701
                      native_client_group: *********
                      native_client_password: *********
                    region:
                      factory_class: com.hazelcast.hibernate.HazelcastCacheRegionFactory
                format_sql: true
                #optional - generate statistics to check if L2/query cache is actually being used.
                generate_statistics: true 
...