10

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

用户

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

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

同样,还有许多其他表具有与公共列(公司)相似的结构,并且我想将数据限制为仅当前公司,因此我使用休眠过滤器来过滤数据。

休眠注释:

<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",
    参数 = {@org.hibernate.annotations.ParamDef(
            名称 = “currentCompanyNumber”,类型 = “int”
        )
    }
)
@实体
@表(名称=“用户”)
@org.hibernate.annotations.Filter(
        name = "restrictToCurrentCompany",
        条件="公司 = :currentCompanyNumber"
)
公共类用户实现可序列化{
    私人国际公司;
    私人字符串用户名;
    ...ETC..
}

道:

@Repository
@Transactional(readOnly = true)
公共类 UserDAOImpl 实现 UserDAO {

    @Autowired(必需 = 真)
    私人 SessionFactory 会话工厂;

    公共设置 getUsers(){
        .....检索当前公司用户的条件查询     
    }

    私人会话 getSession(){
        返回 sessionFactory.getCurrentSession();
    }

}

如果我像这样更改 getSession;

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

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

我尝试连接到休眠拦截器(预加载事件侦听器),但我有点不确定这是否是正确的方法,还是我应该使用上面列出的 getSession 方法来启用过滤器?

4

2 回答 2

19

您拥有的解决方案非常简单,但我猜您正在尝试实现它,以便您不必在每个 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 的引用,他们仍然会应用此过滤逻辑。

首先,如果您不熟悉 spring 中的 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找到这个方法的细节。

首先,确保您在 spring 的应用程序上下文 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>

接下来,您需要在项目中添加一个 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 方法的任何调用都会将您的过滤器添加到它们。

于 2011-08-21T02:45:52.460 回答
1

休眠 hbm 文件: 在您的 hbm 文件中声明过滤器。这里 filterByFacilityIDs 是一个过滤器,facilityIDsParam 是一个 List<String> 类型的参数。

<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 类 **

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;
    }
于 2012-07-17T17:54:38.913 回答