如何为sessionFactory.getCurrentSession()启用hibernate过滤器?

sac*_*ink 9 spring hibernate hibernate-annotations hibernate-filters

假设有一个带有结构的User表:

用户

  • 项目清单
  • userId(PK)
  • 公司(PK)
  • 用户名
  • 地址......等

我想只检索当前公司的用户(公司可以由用户通过UI更改,因此公司是运行时参数)

类似地,还有许多其他表与公共列(公司)具有相似的结构,我想将数据限制为仅当前公司,因此我使用hibernate过滤器来过滤数据.

Hibernate注释:

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

    <property name="dataSource">
        <ref bean="dataSource" />
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">Dialect....</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.generate_statistics">true</prop>
            <prop key="hibernate.connection.release_mode">after_transaction</prop>
            <prop key="hibernate.cache.use_second_level_cache">false</prop>
        </props>
    </property>
    <property name="annotatedClasses">
        <list>
            <value>User</value>
        .....
        </list>
    </property>
</bean>
Run Code Online (Sandbox Code Playgroud)

过滤器定义:

@org.hibernate.annotations.FilterDef(name="restrictToCurrentCompany",
    parameters = {@org.hibernate.annotations.ParamDef(
            name = "currentCompanyNumber", type = "int"
        )
    }
)
@Entity
@Table(name = "USER")
@org.hibernate.annotations.Filter(
        name = "restrictToCurrentCompany",
        condition="company = :currentCompanyNumber"
)
public class User implements Serializable {
    private int company;
    private String userName;
    ...etc..
}

道的:


@Repository
@Transactional(readOnly = true)
public class UserDAOImpl implements UserDAO {

    @Autowired(required = true)
    private SessionFactory sessionFactory;

    public Set getUsers(){
        .....Criteria queries to retrieve users for the current company     
    }

    private Session getSession(){
        return sessionFactory.getCurrentSession();
    }

}

如果我像这样更改getSession;

private Session getSession(){
    Session session = sessionFactory.getCurrentSession();
    Filter filter = session.enableFilter("restrictToCurrentCompany");
    filter.setParameter("currentCompanyNumber", UserUtils.getCurrentCompany());
    return sessionFactory.getCurrentSession();
}
Run Code Online (Sandbox Code Playgroud)

那么我可以启用过滤器并且一切看起来都不错,但是在获取会话期间没有启用过滤器是否有更简单的替代方法来应用并启用整个会话工厂/应用程序级别的过滤器?如果是这样,我怎么能用弹簧配置呢?

我试图挂钩到hibernate拦截器(预加载事件listerns)但我不确定这是否是一个正确的方法或我是否应该使用上面列出的getSession方法来启用过滤器?

小智 17

你所拥有的解决方案非常简单,但我猜你正在努力做到这一点,这样你就不必在每个DAO中提供"getSession"实现.最终,您实现此方法的方法取决于您希望使用此过滤器的灵活性.这有两种方法可以解决这个问题.

最简单的方法是简单地让UserDAOImpl扩展一个包含"getSession"逻辑的新基类.此方法允许您减少代码,因为在大多数情况下应用此过滤器逻辑,但是您可以在需要时覆盖过滤.

你可以创建这样的东西:

public class BaseDAO
{

    // ... possibly some other methods and variables

    @Autowired(required = true)
    private SessionFactory sessionFactory;

    protected Session getSession()
    {
        //Your session filter logic above
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以让你的UserDAOImpl子类化它并在需要做某事时得到一个会话.这是一种非常简单的方法来做你想要的,但它不是万无一失的.如果你正在编写一个供其他人使用的框架,那么什么会阻止他们通过让Spring注入它来获得自己对SessionFactory的引用然后他们可以得到一个未经过滤的Session?在某些情况下,您可能希望这对可以对所有数据起作用的管理进程有所帮助,但我将描述的下一种方法应该可以防止这种情况发生.

解决问题的第二种方法是使用AOP将SessionFactory的getSession方法与您的逻辑一起包装,以便在返回会话之前应用过滤器.此方法意味着即使有人自己获得对SessionFactory的引用,它们仍将应用此过滤逻辑.

首先,如果您在春季不熟悉AOP,请查看参考资料http://static.springsource.org/spring/docs/current/spring-framework-reference/html/aop.html.我将使用基于模式的方法将建议应用于Hibernate,因为我们不想修改Hibernate的源代码.;)您可以在http://static.springsource.org/spring/docs/current/spring-framework-reference/html/aop.html#aop-schema找到此方法的具体内容.

首先,确保在应用程序上下文XML中有以下模式和aop:config部分:

<?xml version="1.0" encoding="UTF-8"?>
<beans ...
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
        ...
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    ...

<aop:config>
    <aop:aspect id="forceFilter" ref="sessionFilterAdvice">
        <aop:pointcut id="hibernateSessionFactoryGetSession"
            expression="execution(* org.hibernate.SessionFactory.openSession(..))" />
        <aop:after-returning method="setupFilter"
            pointcut-ref="hibernateSessionFactoryGetSession" returning="session" />
    </aop:aspect>
</aop:config>

    ...
</beans>
Run Code Online (Sandbox Code Playgroud)

接下来,您需要向项目中添加一个bean,以使用aop:aspect标记实现上面引用的sessionFilterAdvice bean.创建以下类:

package net.grogscave.example;

import org.hibernate.Filter;
import org.hibernate.Session;
import org.springframework.stereotype.Service;

@Service
public class SessionFilterAdvice
{
    public void setupFilter(Session session)
    {
        Session session = sessionFactory.getCurrentSession();
        Filter filter = session.enableFilter("restrictToCurrentCompany");
        filter.setParameter("currentCompanyNumber", UserUtils.getCurrentCompany());
    }
}
Run Code Online (Sandbox Code Playgroud)

最后要确保的是你的项目包括spring-aop jar和aspectjweaver jar.我不知道您是否使用依赖关系管理,但是您需要将这些jar放入项目类路径中.

您现在应该能够重新编译项目,现在对实现SessionFactory的类的任何openSession方法的任何调用都会将过滤器添加到它们中.