【问题标题】:Spring managed transactions without @Transactional annotation没有 @Transactional 注释的 Spring 托管事务
【发布时间】:2014-02-14 05:21:06
【问题描述】:

我正在使用 Spring 注释来管理我的事务,如下所示:

@Transactional(readOnly = true)
public class AlertServiceImpl implements AlertService {

     private AlertDAO alertDAO;

     public List<Alert> getAlerts(){
         List<Alert> alerts = alertDAO.getAlerts();
         return alerts;
     }

}

我想知道如果我忘记了注释会发生什么:

// Oops! Forgot to use transactional annotation 
public class AlertServiceImpl implements AlertService {

    private AlertDAO alertDAO;

    public List<Alert> getAlerts(){
         List<Alert> alerts = alertDAO.getAlerts();
         return alerts;
    }

}

当 alertDAO 实现如下时:

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

// no annotation here either
public class HibernateAlertDAO extends HibernateDaoSupport implements AlertDAO {

    public List<Alert> getAlerts(){
         // some implementation details that define queryString

         Query query = getSession().createQuery(queryString);
         List<Alert> alerts = query.list();

         return alerts;
    }

}

Hibernate 似乎允许我在没有注释的情况下从数据库中获取数据。

这种粗心大意的后果是什么?可能发生的最坏情况是什么?

【问题讨论】:

  • 关于您的问题,您可能会遇到会话耗尽,即会话已打开但由于没有事务边界(spring 用于打开/关闭会话)而从未关闭。您的 dao 有缺陷,您不应该在 HibernateDaoSupport 类上调用 getSession(这将打开一个非托管会话)。 (HibernateTemplateHibernateDaoSupport 必须被视为在 2006 年某个地方从 Hibernate 3.0.1 开始弃用!)。见docs.spring.io/spring/docs/current/spring-framework-reference/…
  • 事务边界是否不是默认情况下服务调用返回的地方或服务线程(在 webapp 的情况下)返回 MVC 请求的地方?如果这是一个非网络应用场景,那么是的,会话消耗是一个大问题。但是,因为它总是调用 getSession() ,如果它存在,它将返回当前会话,不是吗?因为它存储在线程本地?
  • @M.Deinum 您能否在stackoverflow.com/questions/42831749/… 提供您对类似问题的意见?

标签: java spring hibernate transactions


【解决方案1】:

根据文档 (Spring docs),它只是元数据,表明方法或接口可以由“事务感知”的东西(即&lt;tx:annotation-driven/&gt;)配置。

只有 tx:annotation-driven 而没有 @Transactional 属性,我相信您会应用“默认”事务性:

  • 传播设置是必需的
  • 隔离级别为默认
  • 事务是读/写的。
  • 事务超时默认为底层事务系统的默认超时,如果不支持超时则无。
  • 任何 RuntimeException 都会触发回滚,而任何已检查的 Exception 都不会。

假设您使用 &lt;tx:annotation-driven /&gt; 通过事务管理器驱动它,然后错过 @Transactional 属性意味着您不能应用诸如 readOnlyisolation、传播rollbackFornoRollbackFor

我相信 MVC 略有不同 - Hibernate 会话直接绑定到 MVC 请求 - 即,当收到请求时,事务开始。

回到你的例子,HibernateDAOSupport中getSession()的代码如下:

protected final Session getSession()
    throws DataAccessResourceFailureException, IllegalStateException 
{
    return getSession(this.hibernateTemplate.isAllowCreate());
}

依次调用:

/**
 * Obtain a Hibernate Session, either from the current transaction or
 * a new one. The latter is only allowed if "allowCreate" is true.
 *.......
 */
protected final Session getSession()
    throws DataAccessResourceFailureException, IllegalStateException {
    return getSession(this.hibernateTemplate.isAllowCreate());
}

最终调用:

/** 
 * ....
 * @param allowCreate whether a non-transactional Session should be created
 * when no transactional Session can be found for the current thread
 * ....
 */
private static Session doGetSession(
    SessionFactory sessionFactory, Interceptor entityInterceptor,
SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)

从根本上说,Transaction:Session 是 1:1 AFAIK 绑定的,并且在没有事务的情况下运行的唯一方法是使用 say JBoss,它有一个“内置”持久层,为您提供事务性(在幕后) .即使您在getSession() 之后调用getQuery(),您仍然有效地发生事务,因为它是一个JDBC/Hibernate 连接。

【讨论】:

  • Spring 存储库 .save/.saveAll 方法已经是事务性的,因此无需注释您的服务,除非您想将多个 Save 调用组合到一个事务中。另一方面,DAO 方法和 JDBC 模板默认不是事务性的,所以它们应该在 @Transactional 方法中调用
【解决方案2】:

在您的场景中,您的 DAO 将在没有事务的情况下执行,很可能是自动提交。

如果你想在未来避免这种错误并要求你的所有服务在事务中运行,你可以使用以下@Transactional 注解来保护 DAO 层:

@Transactional(propagation = MANDATORY)
public class HibernateAlertDAO extends HibernateDaoSupport implements AlertDAO {
   ...
}

这个注解需要服务层声明事务,否则会抛出异常。

【讨论】:

    【解决方案3】:

    没有注释,您将失去回滚等事务的优势。

    使用@Transactional 注释,您正在执行不止一项数据库操作,例如多次插入和一项失败,事务中的所有操作都可以回滚以使您的数据保持一致。

    这也是为什么建议将注释放在服务中而不是 DAO 中的原因。

    【讨论】:

    • “没有回滚或事务”?不 - 您仍然对非检查异常 RTE 派生有回滚,以及任何事务性已使用 spring-config 配置或作为 Spring 默认提供?
    • 如果你使用声明式事务管理,当然它不会影响回滚的工作方式,但是如果你完全依赖注解,它会影响一组数据库操作的行为方式。
    【解决方案4】:

    如果您不放置@transactional 注释,则不会发生任何事情基本上@Transactional 不是必须放在方法签名的顶部,它只是用于在您的事务为只读=true 时通知编译器(仅用于数据检索目的) 并且当它为只读时=false(仅适用于插入更新删除操作)

    【讨论】:

      猜你喜欢
      • 2018-03-28
      • 2023-02-16
      • 2019-01-09
      • 2011-03-20
      • 2014-07-15
      • 1970-01-01
      • 1970-01-01
      • 2017-02-05
      • 2019-11-02
      相关资源
      最近更新 更多