DAO 方法和同步
Posted
技术标签:
【中文标题】DAO 方法和同步【英文标题】:DAO methods and synchronized 【发布时间】:2011-03-31 05:06:52 【问题描述】:以下是我目前在 Abstract DAO 类中使用的方法。如果有并发调用,它们是安全的还是应该使用同步?我知道如果对方法范围之外的属性的引用应该使用同步,但我不清楚应该如何使用外部资源处理事情。
public Connection getConnection()
// Call to singleton handling JDBC stuff
return Database.getInstance().getCon();
public boolean isConnectionAvailable()
if( getConnection() != null )
return true;
return false;
public PreparedStatement getPreparedStatement( String sqlStatement )
Connection connection = getConnection();
PreparedStatement pS = null;
if( connection != null )
try
pS = connection.prepareStatement( sqlStatement );
catch (SQLException e)
return null;
return pS;
编辑:我可能会重新表述这个问题,以包含有关编写 DAO 的信息,因为这在这里很重要。
【问题讨论】:
不要不要不要不要使用单例进行数据库连接访问 我经常听到这个。但是,当您使用纯 Java 时,还有什么选择? 【参考方案1】:我完全不同意这种实现方式。
首先,拥有工作单元和事务的服务应该向 DAO 提供它们的连接信息。
其次,我没有看到界面。
第三,我没有看到模型或域对象。
第四,准备好的语句应该只是内部实现的一部分。如果他们从你的 DAO 中泄露出来,那么你做错了。
第五,从对象中传递一个准备好的语句使得关闭它和清理的责任变得不那么清晰了。你的 DAO 很快就会因为资源泄漏而死。
这是一个通用 DAO 的接口。您会注意到这都是 CRUD 操作,没有提及连接或 java.sql 包中的任何接口:
package persistence;
import java.io.Serializable;
import java.util.List;
public interface GenericDao<T, K extends Serializable>
T find(K id);
List<T> find();
List<T> find(T example);
List<T> find(String queryName, String [] paramNames, Object [] bindValues);
K save(T instance);
void update(T instance);
void delete(T instance);
你可以走很长的路。这是一个更好的抽象。 T
是您的业务对象类型,K
是主键。
【讨论】:
Spring 有一个不错的 DAO 框架:static.springsource.org/spring/docs/2.5.x/reference/dao.html 不仅仅是体面的。这是要效仿的模式。 感谢达菲莫的回答。我不得不承认我有点挣扎,因为有人要求我不要使用 Spring 这样的框架。如果您有可以用作模板的示例,那将非常受欢迎:)。 @James:这真的很不幸,我很难理解这样的请求背后的动机(你不使用框架)。尤其是春天,对我来说节省了大量时间。【参考方案2】:如果getCon()
每次调用都返回一个新的Connection
,或者返回一个ThreadLocal
连接,那么你是安全的,不需要使用synchronized
如果您将相同的连接返回给每个人,您可能仍会在同步方面保存,因为连接中没有状态被更改(在您当前的代码中)。但是你应该避免这种做法。请考虑使用连接池。
还有一些关于一般设计原则的说明。 DAO 形成一个单独的层。每一层的存在都是有原因的,而不仅仅是为了拥有酷炫的名字。 DAO 层的存在是为了抽象,或者换句话说 - 对使用 DAO 对象的服务隐藏数据库访问。为了更清楚地想象它 - DAO 必须以这样一种方式编写,如果明天您决定从 RDBMD 存储(通过 JDBC)切换到 XML 存储,您应该能够通过更改 only em> DAO 对象,仅此而已。
【讨论】:
具体的 DAO 类如您所解释的那样工作。上面的方法只是在 Asbtract 类中添加一些实用方法的想法,但也许这是一个不好的方法。我可能还不明白它应该如何与 JDBC 和数据库交互。【参考方案3】:不保证 JDBC Connection 类是线程安全的。如果您的 Database.getInstance().getCon() 方法总是返回相同的连接,那么您将遇到问题。但是,如果它使用一个池,这样每次调用 getInstance().getCon() 都会返回一个不同的连接,那么你就可以了。
也就是说,如果您在每次调用 getCon() 时返回不同的连接,那么如果您希望两个 Prepared Statement 调用使用相同的连接(和相同的事务),则 getPreparedStatement() 将不起作用。
我喜欢 Spring 的 JDBCTemplate 类作为我的 DAO 类的基础。
【讨论】:
Connection
只是一个接口,实现是否线程安全并不完全取决于所使用的 JDBC 驱动程序【参考方案4】:
您不应该在每个线程中使用相同的连接。 JDBC 驱动程序不应该是线程安全的。如果你想要一个线程安全的代码,你应该为每个线程创建一个连接。
您的其余代码似乎是安全的。
【讨论】:
你认为我应该在 DAO 中添加某种线程机制吗? 不是你的 DAO 不是线程安全的,而是连接部分。就像@Bozho 所说,如果 getCon() 每次为每个线程返回一个特定连接,那么就没有竞争条件。 好的,你的意思是我应该隔离对连接的任何访问。我看过单例,它每次都返回相同的连接,所以这可能是个问题。 不,应该将连接传递到 DAO。让知道工作单元的对象 - 服务 - 实例化 Connection,将其交给 DAO,然后在方法范围内清理它。【参考方案5】:看看 Spring 是怎么做的,他们已经弄清楚了所有这些东西,没有必要重新发明它。查看与 Spring 的完整发行版捆绑在一起的 petclinic 示例代码,或者(对于非 Spring 方法)阅读 Bauer/King Hibernate 书的 DAO 章节。
当然,DAO 不应该负责获取数据库连接,因为您会希望在同一个事务中对多个 DAO 调用进行分组。 Spring 这样做的方式是有一个服务层,它将其事务方法包装在一个拦截器中,该拦截器从数据源中提取连接并将其放入一个线程本地变量中,DAO 可以在其中找到它。
吃掉你的 SQLException 并返回 null 是不好的。正如 duffymo 指出的那样,让 PreparedStatement 在不保证它会被关闭的情况下被传递是非常糟糕的。此外,没有人应该再使用原始 JDBC,Ibatis 或 spring-jdbc 是更好的选择。
【讨论】:
-1 似乎有点苛刻。你用过spring jdbc吗?它几乎是原始的 jdbc,您仍然可以在其中操作 ps 和 rs(如果您愿意),但会处理所有异常处理(和资源管理)以及与 tx 的交互。因此,您拥有对数据进行原始访问的优势,而且泄露的风险更小。 我认为这是一个可以辩护的立场,GreenieMeanie。我更希望人们将 Spring 仅用于 JDBC,而不是我在 SO 上看到的令人发指的 JDBC 代码。我投票支持 Nathan Hughes。 @stepanian:为什么?我的立场是,即使对于专家来说,做正确的 JDBC 也太痛苦了。结果要么是剪切-粘贴代码,要么是一个可能不如 spring-jdbc 或 Mybatis 的内部框架(两者都投入了大量工作)。这有什么问题? Nathan Hughes:说你更喜欢使用 spring-jdbc、mybaits、ibatis、necessarybatis 或任何你喜欢的框架是一回事。说“没有人应该再使用原始 JDBC”之类的绝对是另一回事。 Java 开发完全取决于选择。我不想和你争论框架的优点和缺点,以及它们与各种项目的关系,网上有很多。但我希望你意识到,你对编码技术的偏好并不总是被所有人所认同。【参考方案6】:连接只是一个接口。它不是线程安全的。 JDBC 驱动程序也不是线程安全的。
【讨论】:
【参考方案7】:我觉得不错。这些函数是线程安全的。
【讨论】:
以上是关于DAO 方法和同步的主要内容,如果未能解决你的问题,请参考以下文章