我的业余爱好项目是Java Web应用程序.这是一个带有表单的简单网页.用户填写表单,提交并显示一些结果.
数据来自JDBC连接.当用户提交时,我验证输入,构建"CREATE ALIAS"语句,"SELECT"语句和"DROP ALIAS"语句.我执行它们并从查询中执行我需要对ResultSet执行的任何操作.
由于我正在使用的特定数据库/ JDBC组合上的ALIASes存在问题,因此每次运行查询时都需要使用唯一名称创建这些ALIAS.我正在使用int来确保每次进入数据库时都会增加.
所以,我的数据访问类看起来有点像:
private final static Connection connection = // initialized however private static int uniqueInvocationNumber = 0; public static Whatever getData(ValidatedQuery validatedQuery) { String aliasName = "TEMPALIAS" + String.valueOf(uniqueInvocationNumber); // build statements, execute statements, deal with results uniqueInvocationNumber++; }
这有效.然而,我最近意识到我坚定地坚持使用Jon Skeet的线程知识第0阶段("完全无知 - 忽略任何问题的可能性.") - 我从未编写过线程代码或线程感知代码.我完全不知道当许多用户同时使用该应用程序时会发生什么.
所以我的问题是,(假设我没有通过盲目的运气/ J2EE魔术偶然发现线程安全):
我怎样才能保证这个安全?
我在这里列出了我认为相关的信息,但如果这还不够,请告诉我.
太感谢了.
编辑:这是一个使用Wicket框架的合适的J2EE Web应用程序.我通常在Jetty中部署它.
编辑:关于ALIASes动机的长篇故事,对于那些感兴趣的人:
有问题的数据库是AS400上的DB2(i5,System i,iSeries,这些天IBM正在调用它),我正在使用jt400.
虽然AS400上的DB2有点像任何其他平台上的DB2,但由于遗留的东西,表有一个"成员"的概念.一个成员有点像桌子的一大块.我想要运行的查询是
SELECT thisField FROM thisTable(thisMember)
它将thisMember视为一个表,因此只需为成员中的所有行提供thisField.
现在,像这样的查询在交互式SQL会话中运行良好,但不能通过JDBC工作(我不知道为什么).我使用的解决方法是做类似的事情
CREATE ALIAS tempAlias FOR thisTable(thisMember)
那么一个
SELECT thisField FROM tempAlias
那么一个
DROP ALIAS tempAlias
但是对于一个停止显示的问题有效:当你使用ALIAS重复执行此操作时总是称为"tempAlias",并且有一个thisField从一个查询到下一个查询的长度不同的情况,结果集会在第二次查询时出现乱码查询(第一行的getString很好,下一个有前面有一定数量的空格,下一个空格的前面有相同数量的空格 - 这是来自内存,但它就是这样的).
因此,确保每个ALIAS都有一个明确的名称可以解决这个问题.
我刚刚意识到(花了很多时间来解释这个解释)我可能在抓住解决方法之前没有花足够的时间思考这个问题.不幸的是,我还没有实现为我的卧室购买AS400的梦想;)所以我现在不能尝试任何新的东西.
好吧,我暂时忽略任何SQL内容,只关注uniqueInvocationNumber部分.这里有两个问题:
无法保证线程在任何特定点都能看到最新值
增量不是原子的
在Java中解决此问题的最简单方法是使用AtomicInteger:
private static final AtomicInteger uniqueInvocationNumber = new AtomicInteger(); public static Whatever getData(ValidatedQuery validatedQuery) { String aliasName = "TEMPALIAS" + uniqueInvocationNumber.getAndIncrement() // build statements, execute statements, deal with results }
请注意,这仍然假设您只在单个服务器上运行单个实例.对于一个可能是合理假设的家庭项目:)
另一个潜在的问题是在不同线程之间共享单个连接.通常,处理数据库连接的更好方法是使用连接池,并在需要的地方"打开/使用/关闭"连接(关闭finally块中的连接).