Транзакции не работают, если внутри DAO - PullRequest
0 голосов
/ 08 сентября 2010

У меня есть проблема с транзакциями в том, что аннотирование службы, которая вызывает DAO с @Transactional, выдает исключение, указывающее, что Сессия не открыта. Единственный способ заставить его работать - это пометить DAO с помощью @Transactional. Что на земле может происходить?

Это то, что я хотел бы сделать, но не работает:

class CustomerService {
    private CustomerDao dao;

    @Transactional
    public void foo() {
        int customerId = dao.getCustomer("fred");
    }
}

class CustomerDao {
    private HibernateTemplate hibernateTemplate;

    public int getCustomer(String name) {
        String sql = "SELECT {m.*} from Customers {m} where name=:name";
        Query qry = getSession().createSQLQuery(sql).addEntity("m", Customer.class);
        qry.setParameter("name", name);
        qry.setCacheable(false);
        List<Customer> list = qry.list();
        return list.iterator().next().getId();
    }

    private Session getSession() {
        return hibernateTemplate.getSessionFactory().getCurrentSession();
    }
}

Это то, что я делаю вместо этого, но, скорее всего, не нужно:

class CustomerService {
    private CustomerDao dao;

    public Customer(CustomerDao dao) {
        this.dao = dao;
    }

    public void foo() {
        int customerId = dao.getCustomer("fred");
    }
}

class CustomerDao {
    private HibernateTemplate hibernateTemplate;

    @Transactional
    public int getCustomer(String name) {
        String sql = "SELECT {m.*} from Customers {m} where name=:name";
        Query qry = getSession().createSQLQuery(sql).addEntity("m", Customer.class);
        qry.setParameter("name", name);
        qry.setCacheable(false);
        List<Customer> list = qry.list();
        return list.iterator().next().getId();
    }

    private Session getSession() {
        return hibernateTemplate.getSessionFactory().getCurrentSession();
    }
}

Похоже, что проблема вызвана тем, что CustomerService создается в конструкторе класса оболочки, где оболочка объявлена ​​в файле контекста Spring xml:

class AllServices {
    private final CustomerService customerService;
    private final OrderService orderService;

    @Autowired
    public AllServices(CustomerDao customerDao, OrderDao orderDao) {
        this.customerService = new CustomerService(customerDao);
        this.orderService = new OrderService(orderDao);
    }

    public CustomerService getCustomerService() {
        return this.customerService;
    }

    public OrderService getOrderService() {
        return this.orderService;
    }
}

Файл пружины выглядит так:

<context:annotation-config />
<import resource="classpath:db-spring-conf.xml"/>
<bean id="allServices" class="myPackage.AllServices" />

и db-spring-conf:

<bean id="editorDatasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">  
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="${versioning.db}" />
    <property name="username" value="${versioning.user}" />
    <property name="password" value="${versioning.pass}" />
</bean>

<tx:annotation-driven transaction-manager="editorTransactionManager"/>

<bean id="editorSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="editorDatasource"/>
    <property name="exposeTransactionAwareSessionFactory">
        <value>true</value>
    </property>
    <property name="annotatedClasses">
        <list>
            <value>myPackage.Order</value>
        </list>
    </property> 
    <property name="mappingResources">
        <list>
            <value>mappings/customer.hbm.xml</value>
        </list>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">validate</prop>
            <!-- Enable Query Cache -->
            <prop key="hibernate.cache.use_query_cache">false</prop>
            <!-- Enable 2nd Level Cache -->
            <prop key="hibernate.cache.use_second_level_cache">false</prop>
            <prop key="hibernate.connection.autocommit">false</prop>
            <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate3.SpringSessionContext</prop>
        </props>
    </property>
</bean>

<bean id="editorHibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
    <property name="sessionFactory" ref="editorSessionFactory"/>
</bean>

<bean id="editorTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="editorSessionFactory" />
</bean>

<!-- DAOs -->
<bean id="customerDao" class="myPackage.CustomerHibernateDao" />
<bean id="orderDao" class="myPackage.OrderHibernateDao" />

Я переместил создание экземпляра CustomerService в конфигурационный файл Spring, и все работает без проблем. Все ли классы, использующие @Transactional, должны быть в файле контекста? Также, чтобы заставить его работать, мне пришлось создать интерфейс для CustomerService, чтобы предотвратить исключение при загрузке файла контекста - Could not generate CGLIB subclass of class

1 Ответ

2 голосов
/ 09 сентября 2010

Итак, вы определили причину проблемы - поддержка Spring @Transactional является аспектом, а аспекты в Spring применяются только к компонентам, управляемым Springinintrainer (хотя можно изменить , но это сложная функция для сложных случаев).

Если вам не нравится объявление сервисов в XML, вы можете взглянуть на другие варианты компонентов Delcare, управляемых Spring:

Что касается проблемы с прокси CGLIB, см. 7.6 Механизмы прокси - возможно, у вас нет реализации CGLIB в пути к классам.

...