Spring @Transactional和继承

9

我有一个基于泛型的DAO类,它是项目中所有其他DAO类的基础,并包含通用功能:

public class Dao<E> {

    private SessionFactory factory;

    public void setSessionFactory(SessionFactory factory) {
        this.factory = factory;
    }

    public E get(int id) {
        // ....
    }

    public void save(E entity) {
        // ...
    }

    public void delete(E entity) {
        // ...
    }

}

在我的项目中,我使用了多个数据源,指向不同的数据库,因此我有多个会话和事务管理器:
<bean id="factory1" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="source1" />
</bean>

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

<bean id="factory2" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="source2" />
</bean>

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

现在我想创建几个DAO,它们操作不同的数据库:

@Repository
@Transactional("manager1")
public class Dao1 extends Dao<Entity1> {

    @Overrides
    @Autowired
    @Qualifier("factory1")
    public void setSessionFactory(SessionFactory factory) {
        super.setSessionFactory(factory);
    }

}

@Repository
@Transactional("manager2")
public class Dao1 extends Dao<Entity2> {

    @Overrides
    @Autowired
    @Qualifier("factory2")
    public void setSessionFactory(SessionFactory factory) {
        super.setSessionFactory(factory);
    }

}

但问题在于,Dao 所有公共方法都没有被子类的 @Transactional 覆盖,因此没有进行事务管理。我所能想到的唯一选择是覆盖父类的方法,让它们在继承的子类中定义,从而被 @Transactional 处理:

@Repository
@Transactional("manager1")
public class Dao1 extends Dao<Entity1> {

    @Overrides
    @Autowired
    @Qualifier("factory1")
    public void setSessionFactory(SessionFactory factory) {
        super.setSessionFactory(factory);
    }

    @Overrides
    public Entity1 get(int id) {
        return super.get(id);
    }

    @Overrides
    public void save(Entity1 entity) {
        super.save(entity);
    }

    @Overrides
    public void delete(Entity1 entity) {
        super.delete(entity);
    }

}

但是我需要在每个DAO类中这样做,而且代码也会到处相同...是否有更好的方法可以在所有类之间共享通用功能,并仍然拥有声明式事务管理的所有优点?

你知道 @Transactional 可以应用于方法。那么 Dao 方法可以被注释吗?此外,与 Dao 层相比,事务更适合放在 Service 层中。 - Subin Sebastian
据我所知,如果您注释了类,则包括继承的方法在内的所有方法都是事务性的。也就是说,事务标记不应该在DAO层面上进行。 - JB Nizet
1
@Subin:如果我没记错的话,如果我开始注释Dao的方法,它们仍然需要我指定要使用哪个事务管理器,而在那个地方并不清楚,因为没有默认的事务管理器... 事务管理器是继承类特定的,只有这样才能清楚地知道哪一个DAO需要哪一个事务管理器... - Laimoncijus
1
事务确实应该属于DAO层。Spring Data JPA (http://projects.spring.io/spring-data-jpa/) 在其所有存储库上都使用了Transactional。人们所指的是谁启动了事务,这由Transactional的“传播”属性控制。服务层应将其设置为REQUIRED(如果不存在,则会创建一个新事务),然后DAO应使用PROPAGATION_MANDATORY(如果不存在事务,则会抛出异常-应由服务层启动)。 - SergeyB
1
事务应该位于服务层而不是存储库层,原因是 Dao 不知道它正在参与哪些业务逻辑。相同的 dao.insertEntity1 方法可能在单个事务中运行,也可能作为多实体插入的一部分运行,Dao 无法确定这一点。当前的实现几乎肯定不是您想要的。 - Angular University
显示剩余3条评论
3个回答

2

如果您不介意super类也是@Transactional的话,可以将注释放在super DAO上。如果您介意,则建议创建一个扩展类TransactionalDao,它将扩展主DAO并且是@Transactional的。所有应该被@Transactional的DAO都将继承它:

public class Dao<E> {

    private SessionFactory factory;

    public void setSessionFactory(SessionFactory factory) {
        this.factory = factory;
    }

    public E get(int id) {
        // ....
    }

    public void save(E entity) {
        // ...
    }

    public void delete(E entity) {
        // ...
    }

}

@Transactional
public class TransactionalDao<E> extends Dao<E>{

    private SessionFactory factory;

    public void setSessionFactory(SessionFactory factory) {
        this.factory = factory;
    }

    public E get(int id) {
        return super.get(id);
    }

    public void save(E entity) {
        super.save(entity);
    }

    public void delete(E entity) {
        super.delete(entity);
    }
}

现在,扩展类将如下所示:

@Repository
@Transactional("manager1") // You'd probably still want the @Transactional for new methods
public class Dao1 extends TransactionalDao<Entity1> {

    @Overrides
    @Autowired
    @Qualifier("factory1")
    public void setSessionFactory(SessionFactory factory) {
        super.setSessionFactory(factory);
    }   
}

这样你只需要一次性进行super的包装即可。


1
你尝试过在父DAO类的方法上添加@Transaction吗?
public class Dao<E> {

    private SessionFactory factory;

    public void setSessionFactory(SessionFactory factory) {
        this.factory = factory;
    }
    @Transactional(readOnly = true)
    public E get(int id) {
        // ....
    }
    @Transactional
    public void save(E entity) {
        // ...
    }
    @Transactional
    public void delete(E entity) {
        // ...
    }
}

这样当您在DAO1上调用save方法时,它将从子类中获取类级别的@Transaction(指定要使用哪个TX管理器),然后从父类的save方法中获取方法级别的@Transactional。


-2

不要在每个地方都添加 @Transactional 注释,你应该使用 AOP based Transaction Management,如 -

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

  <tx:advice id="txAdvice" transaction-manager="txManager">  
  <tx:attributes>  
    <tx:method name="get*" read-only="true"/>  
    <tx:method name="*"/>
  </tx:attributes>
  </tx:advice>

在服务层使用事务是一个好的实践。


错误,事务性基于AOP的事务管理。而且你正在链接到过时的文档。 - JB Nizet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接