【问题标题】:Setting context in a JPA connection — is this safe?在 JPA 连接中设置上下文——这安全吗?
【发布时间】:2012-12-12 06:50:25
【问题描述】:

我需要在每次数据库操作之前设置一些上下文(我曾尝试使用 Oracle 的包级变量,但由于包重新编译的一些问题,我将尝试使用 DBMS_SESSION 和/或DBMS_APPLICATION_INFO),这样我们就可以在任何我们需要的地方(过程、触发器等)获取特定的用户信息,而不是将数十个数据库连接标识为“JBoss”。

我编写了一个 Java EE 拦截器,用于拦截对 @Stateless bean 的调用。它调用一个 Oracle 函数来设置一些会话上下文(查看这个问题以获取一些示例代码 How to tell if a transaction is active in a Java EE 6 interceptor)。

我首先担心的是连接池。起初我认为 Java EE 提供的默认 @PersistenceContext 传播足以保证一切都在同一个连接/事务/EntityManager 中运行,我只需要在拦截器末尾取消设置所有内容(在finally块)在连接返回到池之前。它看起来有点脆弱,但我认为它可以工作。

然后我发现Hibernate有一个属性叫hibernate.connection.release_mode (Hibernate docs about hibernate.connection.release_mode, Red Hat docs about org.hibernate.ConnectionReleaseMode) 并且使用 JTA 事务时的默认和推荐行为是在每个语句之后释放连接(尽管文档说了一些关于 重新获取相同的底层连接,这让我很困惑)。

现在我什至不确定我是否可以在拦截器中可靠地设置一些内容,这些内容只有该用户/操作才能看到,而不会冒其他人在我的业务方法中间抓住相同连接并弄乱的风险我的用户上下文。据我了解,Oracle 数据库会话变量是为连接保留的,而不是为事务或 @PersistenceContext 保留的(数据库毕竟对持久性上下文一无所知,一个连接可用于多个事务)。

我即将放弃,因为随着我更多地了解所涉及的所有技术的实施细节,这看起来越来越脆弱。 这个用户上下文的想法是否可行,或者我应该尝试一种完全不同的方法?我如何测试/调试我的实现以确保没有任何并发​​问题?我还没有找到任何有用的事件侦听器来监视框架行为,并构建一个程序来对服务器进行压力测试投资于我不确定是否应该工作的东西太多了。

我正在使用 JBoss AS 7.1、EJB 3.1、Oracle 10g 数据库和 JPA 2.0(由 Hibernate 提供支持,尽管我们不使用任何特定于 Hibernate 的 API)。

【问题讨论】:

  • 我想我会避免这种事情,因为它很容易出错,但最好了解幕后发生的事情。 David Blevins (stackoverflow.com/questions/269186/…) 建议 release_mode=after_statement 只是一个“不要费心管理已经管理的内容”的设置,这可以解释说“(...)重新获取相同的底层 JDBC 连接(。 ..)”
  • 现在发生在我身上的事情:也许 release_mode 默认是“after_statement”,这样程序员就可以在同一个事务中使用 JPA 和 JDBC 调用,而无需使用两个单独的连接?
  • 所以我终于发现了 为什么 release_mode=after_statement 被添加并且需要成为 JTA 事务中的默认值:mail-archive.com/hibernate-dev@lists.jboss.org/msg00612.html (tl;dr: 因为有些容器可能会认为 Hibernate如果它在嵌套的 EJB 调用中打开一个连接并使其保持打开状态以供调用者重用,则会泄漏连接)。我仍然不能完全确定假设没有其他人会重用 JPA 语句之间的连接是否安全。

标签: oracle hibernate jakarta-ee jpa jboss7.x


【解决方案1】:

我个人会避免尝试在池中的 JDBC 连接上设置单个参数。毕竟,池的想法是所有连接都是相同的。因此,尽管您的拦截器想法可行,但我也担心它会变得多么脆弱。该实现中的任何错误都将是最恶劣的竞争条件。

另一方面,由于您使用的是 Oracle,因此您可能希望查看 EclipseLink。它实现了 JPA2,并得到了 Oracle 的大力资助,因此它支持他们所有的奇异特性。您可能想考虑使用'Isolated Client Sessions'。它看起来支持需要单独会话设置的虚拟专用数据库。如果您需要更改会话上下文,这将是一个解决方案。

【讨论】:

  • 感谢您的回复,这很有用,因为我对独立客户端会话一无所知。现在,当我不确定自己真的需要它们时,我会避免做一些花哨的事情。另外,我不确定在 JBoss AS 上使用 EclipseLink 是否容易:blog.ringerc.id.au/2012/06/…(见最后一条评论)
【解决方案2】:

所以我会用我最近发现的所有东西来回答我自己的问题。

为了了解服务器行为,我更改了我的 JBoss 配置以使用一个只有 1 个连接的池,这样我就可以检测到其他人何时被阻塞等待。

如果当前操作在事务内部(例如 @TransactionAttribute(REQUIRED)),则在事务完成之前,连接不会用于其他任何操作(其他客户端必须等待)。但是,如果您在没有事务的情况下读取数据库,则其他客户端可能会在您不使用它时获取相同的连接,甚至在您的业务方法完成之前(我不知道这种行为有多少是标准的,有多少是实现详细)。

默认情况下,Hibernate 在每条语句之后都会释放连接,这就是为什么可以在非事务方法中重用连接的原因。另一方面,如果您仍在同一个事务中,则 JDBC 和 JEE 具有重新获取相同连接所需的功能。

但是为什么 Hibernate 会释放一个稍后会重新获取的连接呢?如果 Hibernate 没有释放它,一些 JEE 服务器可能会认为 Hibernate 在嵌套 EJB 调用中打开一个连接并使其保持打开状态以供调用者重用时正在泄漏连接。这篇文章对此进行了解释:

http://www.mail-archive.com/hibernate-dev@lists.jboss.org/msg00612.html

为了安全起见,我们需要保存的所有数据(以及稍后呈现给用户)都作为参数显式传递,但是出于日志记录的目的,我们确实使用了存储在 Oracle 会话中的一些数据,因为我们知道连接不能被其他客户,只要您使用事务性业务方法。

更新:似乎可以使用与连接器和/或自定义数据源相关的一些应用服务器特定设置来做我想做的事情(不确定这里的最佳答案是什么:https://developer.jboss.org/thread/250132 )

【讨论】:

  • 这样的东西对 DBA 来说非常有用。当使用连接池时,许多花哨的 Oracle 调查工具都是无用的。 DBA 再也无法说出“谁、如何以及为什么正在扼杀 DB server 的性能”。但是一些性能计数器也预定在 MODULE 帐户上,因此“我们” 将能够判断哪个应用程序的部分是最有问题的。如果可以为特定事务打开会话跟踪(在 Oracle 端),那就太好了。
  • 我们已经让它工作了,但还有另一个问题:嵌套的 EJB 调用。如果一个 EJB 调用另一个 EJB 并且两者都使用相同的拦截器,那么您必须确保 last 最终重置上下文,而不是第一个(因为它会在简单的实现中发生)。我使用 TransactionSynchronizationRegistry 来存储一个计数器,该计数器在每次调用时递增并在 finally 块中递减。
猜你喜欢
  • 2013-05-01
  • 2011-04-16
  • 1970-01-01
  • 2018-12-15
  • 1970-01-01
  • 1970-01-01
  • 2012-11-10
  • 2011-11-08
  • 2022-09-30
相关资源
最近更新 更多