Как включить фильтр гибернации для sessionFactory.getCurrentSession ()? - PullRequest
9 голосов
/ 15 апреля 2011

Скажем, есть таблица пользователя со структурой:

Пользователь

  • Элемент списка
  • userId (PK)
  • company (PK)
  • userName
  • address ... etc

И я хочу получить пользователей только для текущей компании (компания может быть изменена с помощьюпользователь через пользовательский интерфейс, поэтому компания является параметром времени выполнения)

Точно так же есть много других таблиц, которые имеют аналогичную структуру с общим столбцом (компанией), и я хочу ограничить данные только текущей компанией, поэтому яЯ использую фильтр гибернации для фильтрации данных.

Аннотации гибернации:

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

, тогда я могу включить фильтр, и все выглядит хорошо, но вместо включения фильтра во время получения сеанса есть более простая альтернатива, чтобы применить и включить фильтр для всегоФабрика сессий / уровень приложения?Если да, то как я могу это сделать, используя конфигурацию пружины?

Я попытался подключиться к перехватчикам гибернации (предварительно загружаемым листам событий), но я немного не уверен, правильный ли это подход, или мне лучше использовать метод getSessionперечисленные выше для включения фильтров?

Ответы [ 2 ]

16 голосов
/ 21 августа 2011

Решение, которое у вас есть, довольно простое, но я предполагаю, что вы пытаетесь сделать так, чтобы вам не приходилось предоставлять реализацию getSession в каждом из ваших DAO. В конечном итоге ваш метод реализации этого будет зависеть от того, насколько гибким вы хотите быть с этим фильтром. Вот два способа, которыми я мог бы решить эту проблему.

Простейшим способом было бы просто заставить ваш 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 и получить сеанс, когда ему нужно что-то сделать. Это очень простой способ сделать то, что вы ищете, но он не является надежным. Если вы пишете фреймворк для использования другими, то что может помешать им просто получить собственную ссылку на ваш SessionFactory, если Spring вставит его, и тогда они смогут получить нефильтрованный сеанс? Это может потребоваться при определенных обстоятельствах для административных процессов, которые могут воздействовать на все данные, но следующий способ, который я опишу, должен предотвратить это.

Этот второй способ решения проблемы заключается в использовании AOP, чтобы обернуть метод getSession SessionFactory в логику, чтобы применить фильтр перед возвратом сеанса. Этот метод означает, что даже если кто-то получит ссылку на вашу 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.

Сначала убедитесь, что у вас есть следующая схема и раздел aop: config в контексте XML вашего приложения для весны:

<?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-компонент в ваш проект для реализации bean-компонента sessionFilterAdvice, на который мы ссылаемся выше с помощью тега aop: aspect. Создайте следующий класс:

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

Последнее, на что нужно обратить внимание, - это то, что ваш проект включает в себя банку с пружинной крышкой и банку aspectjweaver. Я не знаю, используете ли вы управление зависимостями или нет, но вам как-то нужно вставить эти файлы в путь к классам вашего проекта.

Теперь вы сможете перекомпилировать свой проект, и теперь любые вызовы любого из методов openSession в классах, которые реализуют SessionFactory, добавят к ним ваш фильтр.

1 голос
/ 17 июля 2012

Hibernate hbm file: Объявите фильтр в вашем hbm файле.Здесь filterByFacilityIDs - это фильтр, а entityIDsParam - это параметр типа List .

<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;
    }
...