在我们的数据库中,我们在用户之间有很多共享的对象。对于实例Contacts
,Customers
,Companies
等...
还有很多私有对象,它们映射到共享实体之一,但只应对创建它的用户可见 - 例如备注的
我们的客户实体包含@OneToMany
和Notes
的{{1}}映射
Contacts
目前我需要加载一些笔记,只是为了弄清楚:当前用户没有该客户的注释......
所以我想到了"过滤"在加载它之前的集合 - 事实上,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;对于对象树中的每个过滤条件?
答案 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获取实际的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
可用时才能有线。但是对于本项目的范围,可以保证,所以不是真正的限制。 (这也是我设计延迟初始化而不是由使用过滤器引起的方式的原因)