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

时间:2011-04-15 18:17:43

标签: hibernate spring hibernate-annotations hibernate-filters

假设有一个包含结构的用户表:

用户

  • 列表项
  • userId(PK)
  • 公司(PK)
  • userName
  • 地址......等

我想只检索当前公司的用户(用户可以通过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>

过滤器定义:

@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();
}

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

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

2 个答案:

答案 0 :(得分:16)

你拥有的解决方案非常简单,但我猜你正在努力做到这一点,这样你就不必在每个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
    }
}

现在你可以让你的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 for spring中具有以下模式和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>

接下来,您需要向项目中添加一个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());
    }
}

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

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

答案 1 :(得分:1)

Hibernate hbm文件: 在hbm文件中声明过滤器。这里filterByFacilityIDs是一个过滤器,而facilityIDsParam是一个List&lt;字符串&gt;类型参数。

<hibernate-mapping package="com.ABC.dvo">
 <class name="ItemMasterDVO" table="Item_Master">
  ....
<set name="inventoryTaxesSet" inverse="true" cascade="all">
<key column="item_ID" />
<one-to-many class="InventoryTaxesDVO" />
    <filter name="filterByFacilityIDs" condition="Facility_ID in(:facilityIDsParam)"/>  
</set>
</class>
<filter-def name="filterByFacilityIDs">
 <filter-param name="facilityIDsParam" type="string"/>
</filter-def>
</hibernate-mapping>

** Java class **

public List<ItemMasterDVO> getItemMaster(String[] itemIDs, String[] facilityIDs){
    Session session = getSessionFactory().getCurrentSession();
    Criteria criteria = session.createCriteria(ItemMasterDVO.class)
        .add(Restrictions.in("itemNumber", itemIDs))
        .setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
    if(facilityIDs!=null && facilityIDs.length>0){          
        org.hibernate.Filter filter = session.enableFilter("filterByFacilityIDs");
        filter.setParameterList("facilityIDsParam", facilityIDs);
    }   
    criteria.addOrder(Order.asc("itemNumber"));
    List<ItemMasterDVO> result = criteria.list(); 
    return result;
    }