首页 > 解决方案 > 具有hazelcast实现的hibernate二级缓存,集合元素不是从缓存中检索而是从数据库中检索

问题描述

我对二级缓存有一个“奇怪”的行为,它不会从缓存中命中集合的元素,而是从数据库中命中。

有三个类可以用用户和组之间的属性来建模多对多关系。

User 和 Group 类继承的 Person 类:

@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 类 ID 的可嵌入键

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

    private Long idUser;
    private Long idGroup;

}

所有实体和集合都配置为可缓存。

我做了一个测试,从他的 ID 中检索数据库中的用户,然后我对 UserGroup 集合进行了获取,并且由于 Lazy 模式配置,hibernate 生成了查询。我在一个循环中做了几次。

@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 在第一次迭代时按预期生成两个选择,然后它使用第一级缓存并打印相同的结果。

用户请求:

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

仅使用用户的 id 检索到的 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 中看到的二级缓存结果。

在此处输入图像描述

用户是 4 个组的成员,因此 oauth2.domain.UserGroup 区域中有 4 个条目,oauth2.domain.userGroups 区域中有 1 个集合。

现在第二次执行测试出错了:)

在测试的第一次迭代中,Hibernate 使用用户 ID 和组 ID 对 UserGroup 表进行 4 次查询。所以他可以检索缓存中的集合(因为他已经知道组的 ID),但不能检索应该在 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 个用户组元素

在此处输入图像描述

我不确定为什么在缓存中找不到元素,我使用嵌入式 ID 实现了 UserGroup 类的 hashcode 和 equals。但我不确定 Hazelcast 是如何完成这项工作的,所以可能存在配置错误。如果您有任何想法我该如何解决该问题,欢迎您。

版本:

休眠:5.3.12.Final

Hazelcast-hibernate53:1.3.2

榛树:3.12.3

春季启动:2.0.0.RELEASE

这是 Hazelcast 和 jpa 的 Spring-boot 配置。

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 

标签: hibernatespring-bootormhazelcastsecond-level-cache

解决方案


推荐阅读