数据库连接-应该将它们作为参数传递吗?


11

我们有一个系统,通过该系统,可以使用通用方法获得一次数据库连接,并在整个要使用的相关类中传递数据库连接。有人怀疑将数据库连接作为参数传递给不同的类会导致问题,因此我在这里检查是否确实可行,并且还有更好的模式吗?

我知道有一些ORM工具可以实现持久性,但我们还不能介绍。

欢迎任何反馈,谢谢。


您指的是哪种问题?谁有这些疑问?(我认为不是您。)
Greg Hewgill 2013年

1
诸如导致开发人员忘记关闭连接之类的问题,通常我只是想看看将数据库连接作为参数传递给各种方法是否是一种好习惯。是的,怀疑来自另一个开发人员。
ipohfly

Answers:


8

是的,绕过连接是安全的。您可以在外部控制块中处理连接。没有什么不安全的。

不安全的是编写不能保证及时正确处理连接的代码。忘记清理资源与传递资源无关。您可以轻松地编写留下悬挂连接而无需传递到任何地方的代码。

在C ++中,如果在堆栈上分配或使用智能指针,则将受到RAII的保护。在C#中,硬性规定所有一次性对象(例如连接)都必须在“使用”块中声明。在Java中,使用try-finally逻辑进行清理。对所有数据层代码进行代码审查以确保这一点。

最常见的用例是当您具有可以组合成许多排列的多个操作时。这些排列中的每一个都必须是原子事务(全部成功或回滚)。那么您必须将事务(以及相应的连接)传递给所有方法。

假设我们有许多foobar()操作,可以将它们以各种方式组合为原子事务。

//example in C#
//outer controlling block handles clean up via scoping with "using" blocks.
using (IDbConnection conn = getConn())
{
    conn.Open();
    using (IDbTransaction tran = conn.BeginTransaction())
    {
        try
        {//inner foobar actions just do their thing. They don't need to clean up.
            foobar1(tran);
            foobar2(tran);
            foobar3(tran);
            tran.Commit();
        }
        catch (Exception ex)
        { tran.Rollback(); }
    }
}//connection is returned to the pool automatically

顺便说一句,您将希望尽快打开连接,并尽快处理它们。如果您将连接视为对象成员,将它们介绍为不必要的状态,并使连接打开的时间比必要的时间长得多,则您的队友可能是正确的。但是,将连接或事务作为参数传递的行为并不是天生的错误。

顺便说一句。根据您的语言对一流函数的支持,您可以列出foobar()操作列表。因此,一个函数可以处理动作的所有排列。消除每个排列的外部控制块的重复。


将此标记为答案,因为它使我对情况如何
有了

6

听起来您好像在依赖注入之后。也就是说,池化连接仅创建一次,并在需要时注入。当然,通过方法参数传递连接是依赖关系注入的一种方法,但是IoC容器(例如Guice,PicoContainer或Spring)是另一种(更安全)的方法。

使用DI意味着您可以围绕连接的创建,打开,使用和关闭整齐地包装逻辑-远离核心业务逻辑。

Spring JDBC等是为您执行这种行为的其他示例


Emm并不是真正在考虑依赖注入。只是试图找出通常这样做是否是一种好习惯,如果不是,那么更好的管理数据库连接的方法是什么(尽管DI是这样做的一种方法)。
ipohfly

-1。一个连接不适合多用户系统。由于用户数量少和执行速度快,它似乎可以工作。使用池,即使在单个用户系统中,也最好按操作实例化连接对象。
mike30

2

传递数据库事物而不是数据事物会导致问题。在这种情况下,只要可行,除非可以保证适当的数据库卫生,否则不要传递数据库内容。

传递数据库事物的问题在于它可能草率。我已经看到一个人在数据库连接周围传递代码的一个以上错误,即有人随后将结果集抓取并存储在本地对象(结果集,仍连接到数据库)中,然后将游标绑在数据库的时间很长。另一个实例有人将结果集传递给其他人(然后保存),然后传递结果集的方法将其关闭(和该语句),当其他方法尝试不再使用该结果集时会导致错误。

所有这些都源于不尊重数据库,连接,语句,结果集及其生命周期。

为了避免这种情况,现有的模式和结构可以更好地与数据库配合使用,并且不需要从受限的类中脱离数据库内容。数据输入,数据输出,数据库保持原状。


1
+1 db连接应具有最短的时间跨度。打开它,使用它,并尽可能快地关闭它。如今,存在大量的连接池实现,因此将连接用于多个操作会带来虚假的结果。以及错误或性能问题的邀请(对表进行锁定,使用连接资源)
jqa

这些现有模式和结构中的某些名称是什么?
丹尼尔·卡普兰

@tieTYT最主要的是数据访问对象,该对象用于对应用程序其余部分隐藏数据库。 另请参见数据访问层对象关系映射

当我想到这些模式时,我觉得它们处于比他所要求的更高的抽象水平。假设您有一种方法可以Root从Dao 上获得一个。但是随后您意识到,您还希望获得一种方法,Node而不用将其拉出整个Root对象。如何使RootDao调用NodeDao代码(即重用),但是要确保NodeDao仅在Node直接调用Dao 时关闭连接,而在Dao调用时保持连接打开Root
丹尼尔·卡普兰

1
只是想补充一下,如果您不在自动提交模式下,则传递连接可能会导致以下情况:一个对象更新数据库,然后另一个(可能不相关的)对象获取连接,出现错误并结束回滚第一个对象的更改。这些类型的错误可以非常调试困难。
TMN

2

Connection尽管在大多数情况下,只有DAO实现应该与它们有任何关系,但是传递实例通常不是问题。现在,您的问题在于连接在使用后没有关闭,因此实际上很容易修复:Connection需要以与打开对象相同的级别(即,使用相同的方法)关闭对象。我个人使用以下代码模式:

final Connection cnx = dataSource.getConnection();
try {
    // Operations using the instance
} finally {
    cnx.close();
}

这样,即使在块中引发异常,我也确保所有连接始终关闭。实际上,只要为StatementResultSet实例使用完全相同的模式,到目前为止,一切都进展顺利。

编辑2018-03-29:如以下注释中的user1156544所示,从Java 7开始,应优先使用try-with-resources构造。使用它,我在初始答案中提供的代码模式可以简化为:

try (final Connection cnx = dataSource.getConnection()) {
    // Operations using the instance
}

1
我用类似的东西。我有函数doInTransaction(DbTask task),其中DbTask是我的带有连接参数的方法的接口。doInTransaction获取连接,调用task和commit(如果发生异常,则回滚)并关闭该连接。
user470365

从您的示例来看,这意味着DataSource对象是单例吗?
ipohfly

@ipohfly实际上,我应该为该对象命名,dataSource而不是DataSource(我将针对这一点解决问题)。该对象的确切类型为javax.sql.DataSource。在以前的代码中,我曾经让一个人管理我的应用程序中的所有可用数据源。我的DAO不必完全了解这一点,因为DataSource实例是通过依赖项注入提供的。
KevinLH 2013年

如果使用此架构,请更好地使用try-with-resources
user1156544 '18

当我回答时,我还没有使用Java7。但是您说的很正确,这些天这应该是首选的方式。我将更新我的答案以包括您的建议。
KevinLH

0

要以这种方式做事要折衷,而不是使用可以根据需要获取的单例。我过去做过两种事情。

通常,您需要考虑数据库连接管理的后果,这可能与数据库查询用法正交,也可能不正交。例如,如果给定应用程序实例具有一个数据库连接,并且在不使用该数据库时将其关闭,则该连接将是正交的。将管理置于单例课程中,不要忽略它。这使您可以根据需要管理数据库连接。例如,如果您想在每次提交时都关闭连接(并在下一次调用时重新打开),那么在单例上就更容易完成,因为该API可以集中化。

另一方面,假设您需要管理一个连接池,其中给定的调用可能需要使用任何任意连接。例如,在多个服务器之间进行分布式事务时,可能会发生这种情况。在这种情况下,与使用单例相比,传递数据库连接对象通常要好得多。我认为这通常是很少见的情况,但是在需要时这样做没有任何错误。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.