开发者社区> 问答> 正文

关于 Hibernate加载树形结构的数据如何用上二级缓存问题

经常有树形结构的数据保存在一张二维表中,例如组织架构,多级菜单等。一般是在一张表中使用ID,PID作关联。我项目中的是这样一个树形菜单:

@Entity
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region = "Menu")
@Table(name = "DDT_MENU", schema = "CBA", uniqueConstraints = @UniqueConstraint(columnNames = "ENNAME"))
public class Menu implements java.io.Serializable {
     
    private static final long serialVersionUID = 1L;
    private Long id;
    private EMenu enname;
    private String zhname;
    private Long pid;
    private String grade;
     
    private Menu parent;
    private Set<Menu> children=new TreeSet<Menu>();
     
    public Menu() {
    }

下面是关联配置:


@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PID", nullable = true)
public Menu getParent() {
    return parent;
}
 
@OneToMany(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY, mappedBy = "pid")
public Set<Menu> getChildren() {
    return children;
}

这些数据由于很修改少,访问频繁,因此很适合放在缓存中。

下面是EhCache配置:

  <cache name="Menu"   
       maxElementsInMemory="100"
       eternal="false"
       timeToIdleSeconds="120"
       timeToLiveSeconds="120"
       overflowToDisk="false"
       diskPersistent="false"
       diskExpiryThreadIntervalSeconds="120"
       memoryStoreEvictionPolicy="LRU"  
/>

我在程序中用递归方式获得整个树形结构的数据,观察后台Hibernate输出的语句,一直都没使用上缓存。根据Hibernate使用缓存的机制:
1) Hibernate首先根据这些查询信息组成一个Query Key,Query Key包括条件查询的请求一般信息:SQL, SQL需要的参数,记录范围(起始位置rowStart,最大记录个数maxRows),等。   2) Hibernate根据这个Query Key到Query缓存中查找对应的结果列表。如果存在,那么返回这个结果列表;如果不存在,查询数据库,获取结果列表,把整个结果列表根据Query Key放入到Query缓存中。   3) Query Key中的SQL涉及到一些表名,如果这些表的任何数据发生修改、删除、增加等操作,这些相关的Query Key都要从缓存中清空。
来看,也确实使用不上,因为每次递归getChildren()的查询条件不同。现在不知道咋搞,请各位大侠不吝赐教。

展开
收起
爵霸 2016-03-04 14:37:44 2889 0
1 条回答
写回答
取消 提交回答
  • 经过我仔细的调试,发现使用配置对象之间的关联关系(@OneToMany )得到的关联对象并不会放到二级缓存中。例如上述例子中,我遍历整棵树,在递归得到孩子菜单时是通过Menu类的getChildren()方法,调用此方法得到的孩子菜单对象hibernate并不会放到二级缓存中。因此,为了使用上缓存,我自己写了另外一个方法findMyChildren(),代码如下:

    @SuppressWarnings("unchecked")
    public HashSet<Menu> findMyChildren(Menu m) {
        DetachedCriteria dc = DetachedCriteria.forClass(Menu.class);
        dc.add(Restrictions.eq("pid", m.getId()));
        List<Menu> menus = hibernateTemplateUseCache.findByCriteria(dc);
        HashSet<Menu> menusSet = new HashSet<Menu>(menus);
        return menusSet;
    }

    然后递归取得孩子菜单时使用此方法,此方法返回的孩子菜单都会被保存到二级缓存中,以后访问时会直接从缓存中拿。由于项目的菜单在程序运行过程中不会被修改,因此只需从数据库加载一次。于是修改EhCache配置:

      <cache name="Menu"   
           maxElementsInMemory="200"
           eternal="true"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           diskPersistent="false"
           diskExpiryThreadIntervalSeconds="120"
           memoryStoreEvictionPolicy="LRU"  
    />

    这样一来,就达到了树形结构数据永远去缓存中取的目的。

    2019-07-17 18:52:17
    赞同 展开评论 打赏
问答标签:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
基于英特尔 SSD 的虚拟机缓存解决SSD 立即下载
用户态高速块缓存方案 立即下载
高性能Web架构之缓存体系 立即下载