Hibernate:@OneToMany - 其他条件? @过滤?

时间:2014-10-31 22:47:59

标签: hibernate filter

在我们的数据库中,我们在用户之间有很多共享的对象。对于实例ContactsCustomersCompanies等...

还有很多私有对象,它们映射到共享实体之一,但只应对创建它的用户可见 - 例如备注

我们的客户实体包含@OneToManyNotes的{​​{1}}映射

Contacts

目前我需要加载一些笔记,只是为了弄清楚:当前用户没有该客户的注释......

所以我想到了&#34;过滤&#34;在加载它之前的集合 - 事实上,Hibernate确实为此目的支持@Entity public Class Customer{ @OneToMany Set<Contact> contacts; @OneToMany Set<Note> notes; } 注释(看起来如此),因此,我还需要应用以下注释:

@Filter

但在其他方面,文档留下了一些问题。现在,每个示例都会继续显示如何设置过滤器值( @OneToMany @Filter(name="userIdFilter", condition=" :userIdFromSession = userId") Set<Note> notes; ),使用:userIdFromSession

session.createQuery()

如何可以在使用实体管理器和Session session = ...; session.enableFilter("userIdFilter").setParameter("userIdFromSession", session.getUser().getId()); List results = session.createQuery(...) 时启用过滤器(并设置目标值),目标值为&#34;已应用&#34;对于对象树中的每个过滤条件?

1 个答案:

答案 0 :(得分:0)

我设法解决了这个问题。由于项目布局,它比预期更棘手,而且 hacky ,但毕竟最终的结果现在正是我对改变的预期:

无论我如何加载实体(服务,懒惰初始化),每当加载私有实体时,Hibernate都会应用用户ID过滤器。

过滤器定义

首先,Filter的定义需要在其中一个实体上附加注释:@FilterDef

@Entity
@FilterDef(name = "userFilter", parameters = @ParamDef(name = "user", type = "java.lang.Integer"), defaultCondition = "user_id = :user")
public class Customer extends AbstractEntity implements Serializable {

现在可以将Filter应用于任何Collection,以便让Hibernate应用过滤器:

@OneToMany
@Filter(name="userFilter")
Set<Note> notes;

的EntityManager

如问题中所述,我使用的EntityManager没有公开的过滤器方法。但是,由于我们可以从EntityManager获取实际的Session,因此我能够像这样启用过滤器(这需要在每次获得新的EntityManager实例时执行(或者说,当前一个会话已经过时)闭)):

org.hibernate.Session sess = (org.hibernate.Session) this.em.getDelegate();
sess.enableFilter("userFilter").setParameter("user", this.session.getCurrentUser().getId());

用户?

现在是棘手的部分:首先,Hibernate在Circles中运行抛出异常而根本不运行。这是项目相关设置发挥作用的地方。我正在使用泛型 Dataservice来处理fetchAll, fetchByPK, save, ...

之类的内容

所以这些方法最初看起来像这样:

public <T> LinkedList<T> fetchAll(Class<T> clazz) {
    LinkedList<T> result = new LinkedList<>();
    org.hibernate.Session sess = (org.hibernate.Session) this.em.getDelegate();
    sess.enableFilter("userFilter").setParameter("user", this.session.getCurrentUser().getId());
    result = new LinkedList<T>(this.em.createQuery("SELECT x FROM " + clazz.getSimpleName() + " x", clazz).getResultList());
    return result;
}

由于我使用了相同的方法来获取User本身 - 我最终得到了一个循环,因为每当我尝试设置过滤器时,Hibernate都试图初始化我的会话获取用户对象 - 再次尝试相同的用户对象。

解决方案。是要从启用过滤器中排除User.class的提取:

public <T> LinkedList<T> fetchAll(Class<T> clazz) {
    LinkedList<T> result = new LinkedList<>();
    org.hibernate.Session sess = (org.hibernate.Session) this.em.getDelegate();
    if (clazz != User.class) {
        sess.enableFilter("userFilter").setParameter("user", this.session.getCurrentUser().getId());
    } else {
        sess.disableFilter("userFilter");
    }
    result = new LinkedList<T>(this.em.createQuery("SELECT x FROM " + clazz.getSimpleName() + " x", clazz).getResultList());
    return result;
}

(这也适用于em.find

惰性加载

我的所有集合都是惰性加载的,因为这可能发生在Ajax请求期间,我需要在整个项目中使用多个会话。

为了确保用于LazyLoading的initializeCollection()方法应用了相同的过滤器,我不得不再次考虑,用户对象本身内部有 LazyLoaded 集合。

因此,为了避免在LazyLoading 使所有其他会话都使用过滤器的情况下无限循环,我只应用了Generic DataService中使用的原则。 (初始化集合的方法是最抽象的实体的一部分,它是所有实体的父实体)

User u = null;
if (this instanceof User) {
    u = (User) this;
} else {
    u = ContextHelper.getBean("mysession", my.namespace.Session.class).getCurrentUser();
}

//...

newSession.enableFilter("userFilter").setParameter("user", u.getId());

从实体内部评估EL-Expressions(Helper.getBean)的唯一缺点是,此实体现在只能在FacesContext可用时才能有线。但是对于本项目的范围,可以保证,所以不是真正的限制。 (这也是我设计延迟初始化而不是由使用过滤器引起的方式的原因)