java - 除非在 DAO 内部,否则交易无法进行

标签 java spring transactions annotations dao

我在事务方面遇到问题,因为使用 @Transactional 注释调用 DAO 的服务会引发异常,表明 session 未打开。我让它工作的唯一方法是使用 @Transactional 注释 DAO。到底发生了什么?

这是我想做的,但不起作用:

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

spring 文件如下所示:

<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 创建一个接口(interface),以防止加载上下文文件时出现异常 - 无法生成类的 CGLIB 子类

最佳答案

所以,您确定了问题的原因 - Spring 的 @Transactional 支持是一个切面,Spring 中的切面仅应用于由 Spring 容器管理的组件(尽管它是 can be changed ,但是这是针对复杂情况的高级功能)。

如果您不喜欢在 XML 中声明服务,您可以查看 delcare Spring 管理的组件的其他选项:

关于 CGLIB 代理的问题,请参阅 7.6 Proxying mechanisms - 可能您的类路径中没有 CGLIB 实现。

关于java - 除非在 DAO 内部,否则交易无法进行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3670152/

相关文章:

java - 如何使用 onCreate 方法在 MainActivity 中打开 fragment

java - 使用可能返回 null 的函数转换 Guava Optional

java - 初始化数据库以在 Spring Data R2DBC 上进行测试

java - Spring MVC 中 Errors 对象的模型键是什么?

transactions - ArangoDB 事务 - 如何防止抛出异常

mysql - 当我执行 'lock tables [table_name] write' 时出现“元数据锁”

sql-server - 为什么在一个事务中创建具有外键约束的表会阻止访问另一个事务中的引用表?

java - 使用spring kafka在kafka消费者中手动提交偏移量的方法

java - 如果 double 类型可以处理数字 4.35 和 435,为什么 4.35 * 100 的计算结果为 434.99999999999994?

java - 在 @ExceptionHandler 内部抛出异常