PreparedStatement多次使用,是否推荐每次都关闭然后重新生成。

有明丶 2014-09-04 11:37:11
我自定义了一个连接池,因为连接长期保持,所以多次使用PreparedStatement成为可能。

现在遇到一个问题,暨有两种情况:
1) 我为每一个连接设置一个PreparedStatement 的 Map,用来存储与Sql语句对应PreparedStatement 。因为项目中的Sql语句毕竟是有限的,所以只要维系一个有限大小的内存,我就可以维系所有的PreparedStatement的存在。

2) 我每次从连接池获取到Connection,都重新建立PreparedStatement,并且在处理完之后销毁它。

不知道大家对这两种方法怎么看,哪种比较高效又符合实际。
...全文
3274 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
落魄白菜 2014-10-31
  • 打赏
  • 举报
回复
我也在考虑这个问题, 在高并发的情况下, 由于使用了连接池. 能否把preparedStatement绑到Connection上, 至少可以免去频繁创建preparedStatement对象方面的开销, 每次用时直接拿过来用,而不是现创建, 不知道是否真的可行
有明丶 2014-09-10
  • 打赏
  • 举报
回复
引用 12 楼 rumlee 的回复:
建议不要这么做,缓存PreparedStatement 可能并不会对性能有什么影响,却对开发造成了很大的麻烦。 直接用连接池就可以了。
这两个东西都是建立在连接池的基础上的。
有明丶 2014-09-10
  • 打赏
  • 举报
回复
引用 13 楼 sinat_19250161 的回复:
多次使用会加大电脑的负载,严重会死机的
这是误区,我之间看过很多所谓多次使用的例子,齐根本思想就是错误的,所以造成死机也就是正常的。 多次使用是只一个PS对象多次使用,但是很多展示PS多次使用的例子,都是每次都生成新的PS对象,而且还不关闭原来的对象,这是对JAVA机制的不熟悉。
有明丶 2014-09-10
  • 打赏
  • 举报
回复
引用 16 楼 HinanaiTenshi 的回复:
另外sql才多大点? sq的关键是数据库是否做了预编译缓存,你在java端怎么折腾都没用。
谢谢,经过你的指导,我把mysql连接设置了预编译,果然两种方法的效率就出现了差异。 在无索引查询时,每次关闭PS的方法效率上要比不关闭的多耗费三分之一的时间。 不过在有索引的查询是,效率还是近乎相似的,这里不知道有什么玄机。
sunaer 2014-09-09
  • 打赏
  • 举报
回复
看下其他的连接池怎么实现的, 比如Proxool , 这个连接池里就有ps的cache , ali的druid也有关于ps的cache(druid已经明确说明mysql不能的缓存ps, proxool不清楚)
HinanaiTenshi 2014-09-09
  • 打赏
  • 举报
回复
另外sql才多大点? sq的关键是数据库是否做了预编译缓存,你在java端怎么折腾都没用。
HinanaiTenshi 2014-09-09
  • 打赏
  • 举报
回复
引用 11 楼 youmingdot 的回复:
好吧,经过实测,在MYSQL上,两种方法的效率基本上是一样的。这可能印证了我以前听到的一些消息,PreparedStatement对MYSQL是没有加速效果的。
我怀疑你这是msyql的常见问题,建立的数据库链接没有打开预编译,打印一下mysql的查询日志就能看出来。实际全mysql端全是query。
wangqiao4j 2014-09-09
  • 打赏
  • 举报
回复
这个应该看项目的大小和访问数据库的频繁程度来决定吧。
sinat_19250161 2014-09-09
  • 打赏
  • 举报
回复
多次使用会加大电脑的负载,严重会死机的
rumlee 2014-09-09
  • 打赏
  • 举报
回复
建议不要这么做,缓存PreparedStatement 可能并不会对性能有什么影响,却对开发造成了很大的麻烦。 直接用连接池就可以了。
有明丶 2014-09-09
  • 打赏
  • 举报
回复
好吧,经过实测,在MYSQL上,两种方法的效率基本上是一样的。这可能印证了我以前听到的一些消息,PreparedStatement对MYSQL是没有加速效果的。
humanity 2014-09-09
  • 打赏
  • 举报
回复
觉得关联到那个物理连接才比较可靠,毕竟这里面跟具体 厂商 驱动有关系,我们不能假定 Statement 和 Connection 的实现类之间没有任何关系。规范只是保证它们向我们展示的接口是符合功能要求的,并未规定性能要求,所以各厂商基于性能的考虑,各种可能的方案都有可能被采用,比如 JDBC Type 4 驱动,是厂商私有的通信协议,厂商的驱动也可能会为了性能而缓冲部分状态码,当物理连接不同时可能会出现怪异的错误。
qiuqiupeng 2014-09-08
  • 打赏
  • 举报
回复
连接池一开始就创建一批,然后动态保持一定数量,不够用时动态增加,平时回复到常态,用完只释放,超时关闭连接,然后重新创建连接,总之,让其保证一定连接数比较好。具体根据你的数据库和机器配置来定。
wyx100 2014-09-08
  • 打赏
  • 举报
回复
引用 3 楼 arkwrightzhn 的回复:
呃。。。还是测试一下,写个PreparedStatement,连续发送多次请求试一下。很早以前我用一个连接多次操作数据库时,都是把ResultSet和PreparedStatement都关了重开的,不记得是不是因为多次会报错才那样干的了。
代码间的舞者 2014-09-06
  • 打赏
  • 举报
回复
1、没必要为每个连接都设置PreparedStatement吧。 2、而且连接池里面Connection 也不是一直都存在的。 3、还得看具体的系统的运行情况吧。如果数据库使用极其频繁选第一种,不然还是选第二种。
有明丶 2014-09-06
  • 打赏
  • 举报
回复
引用 4 楼 Ghost_520 的回复:
显然是第二种啊, 每次都从连接池取出一个 Connection 获得链接, 你第一种要考虑并发量和锁, 并发量如果大的话, 就算给系统分配在高的内存,server也会溢出的,锁是说你每次只重复使用PreparedStatement, 但是 Connection 确实只有一个, 如果其中一个 PreparedStatement 发生死锁, 那其他的 PreparedStatement 不就是得一直wait 锁释放么
这个貌似没有关系把,就算发生死连接也是 Connection 的事情,这是连接池应该处理的问题,而且当错误的Connection被连接池处理的时候相关的PreparedStatement 也会被回收。
Norris_Zhang 2014-09-04
  • 打赏
  • 举报
回复
呃。。。还是测试一下,写个PreparedStatement,连续发送多次请求试一下。很早以前我用一个连接多次操作数据库时,都是把ResultSet和PreparedStatement都关了重开的,不记得是不是因为多次会报错才那样干的了。
有明丶 2014-09-04
  • 打赏
  • 举报
回复
引用 1 楼 u013456370 的回复:
用完之后还可以放回到连接池中啊 下次可以在用啊
我是想了解一下这两种使用PreparedStatement的方法哪个更好。
指尖de柔情 2014-09-04
  • 打赏
  • 举报
回复
用完之后还可以放回到连接池中啊 下次可以在用啊
Ghost_520 2014-09-04
  • 打赏
  • 举报
回复
显然是第二种啊, 每次都从连接池取出一个 Connection 获得链接, 你第一种要考虑并发量和锁, 并发量如果大的话, 就算给系统分配在高的内存,server也会溢出的,锁是说你每次只重复使用PreparedStatement, 但是 Connection 确实只有一个, 如果其中一个 PreparedStatement 发生死锁, 那其他的 PreparedStatement 不就是得一直wait 锁释放么
二、JDBC连接MySql方式 下面是使用JDBC连接MySql的一个小的教程 1、查找驱动程序 MySQL目前提供的java驱动程序为Connection/J,可以从MySQL官方网站下载,并找到mysql-connector-java-3.0.15-ga-bin.jar文件,此驱动程序为纯java驱动程序,不需做其他配置。 2、动态指定classpath 如果需要执行时动态指定classpath,就在执行时采用-cp方式。否则将上面的.jar文件加入到classpath环境变量中。 3、加载驱动程序 try{ Class.forName(com.mysql.jdbc.Driver); System.out.println(Success loading Mysql Driver!); }catch(Exception e) { System.out.println(Error loading Mysql Driver!); e.printStackTrace(); } 4、设置连接的url jdbc:mysql://localhost/databasename[?pa=va][&pa=va] 三、以下列出了在使用JDBC来连接Oracle数据库时可以使用的一些技巧,这些技巧能够使我们更好地发挥系统的性能和实现更多的功能(系转载)。   1、在客户端软件开发中使用Thin驱动程序   在开发Java软件方面,Oracle的数据库提供了四种类型的驱动程序,二种用于应用软件、applets、servlets等客户端软件,另外二种用于数据库中的Java存储过程等服务器端软件。在客户机端软件的开发中,我们可以选择OCI驱动程序或Thin驱动程序。OCI驱动程序利用Java本地化接口(JNI),通过Oracle客户端软件与数据库进行通讯。Thin驱动程序是纯Java驱动程序,它直接与数据库进行通讯。为了获得最高的性能,Oracle建议在客户端软件的开发中使用OCI驱动程序,这似乎是正确的。但我建议使用Thin驱动程序,因为通过多次测试发现,在通常情况下,Thin驱动程序的性能都超过了OCI驱动程序。   2、关闭自动提交功能,提高系统性能   在第一次建立与数据库的连接时,在缺省情况下,连接是在自动提交模式下的。为了获得更好的性能,可以通过调用带布尔值false参数的Connection类的setAutoCommit()方法关闭自动提交功能,如下所示:   conn.setAutoCommit(false);   值得注意的是,一旦关闭了自动提交功能,我们就需要通过调用Connection类的commit()和rollback()方法来人工的方式对事务进行管理。   3、在动态SQL或有时间限制的命令中使用Statement对象   在执行SQL命令时,我们有二种选择:可以使用PreparedStatement对象,也可以使用Statement对象。无论多少次地使用同一个SQL命令,PreparedStatement都只对它解析和编译一次。当使用Statement对象时,每次执行一个SQL命令时,都会对它进行解析和编译。这可能会使你认为,使用PreparedStatement对象比使用Statement对象的速度更快。然而,我进行的测试表明,在客户端软件中,情况并非如此。因此,在有时间限制的SQL操作中,除非成批地处理SQL命令,我们应当考虑使用Statement对象。   此外,使用Statement对象也使得编写动态SQL命令更加简单,因为我们可以将字符串连接在一起,建立一个有效的SQL命令。因此,我认为,Statement对象可以使动态SQL命令的创建和执行变得更加简单。   4、利用helper函数对动态SQL命令进行格式化   在创建使用Statement对象执行的动态SQL命令时,我们需要处理一些格式化方面的问题。例如,如果我们想创建一个将名字O'Reilly插入表中的SQL命令,则必须使用二个相连的“''”号替换O'Reilly中的“'”号。完成这些工作的最好的方法是创建一个完成替换操作的helper方法,然后在连接字符串心服用公式表达一个SQL命令时,使用创建的helper方法。与此类似的是,我们可以让helper方法接受一个Date型的值,然后让它输出基于Oracle的to_date()函数的字符串表达式。   5、利用PreparedStatement对象提高数据库的总体效率   在使用PreparedStatement对象执行SQL命令时,命令被数据库进行解析和编译,然后被放到命令缓冲区。然后,每当执行同一个PreparedStatement对象时,它就会被再解析一次,但不会被再次编译。在缓冲区中可以发现预编译的命令,并且可以重新使用。在有大量用户的企业级应用软件中,经常会重复执行相同的SQL命令,使用PreparedStatement对象带来的编译次数的减少能够提高数据库的总体性能。如果不是在客户端创建、预备、执行PreparedStatement任务需要的时间长于Statement任务,我会建议在除动态SQL命令之外的所有情况下使用PreparedStatement对象。   6、在成批处理重复的插入或更新操作中使用PreparedStatement对象   如果成批地处理插入和更新操作,就能够显著地减少它们所需要的时间。Oracle提供的Statement和 CallableStatement并不真正地支持批处理,只有PreparedStatement对象才真正地支持批处理。我们可以使用addBatch()和executeBatch()方法选择标准的JDBC批处理,或者通过利用PreparedStatement对象的setExecuteBatch()方法和标准的executeUpdate()方法选择速度更快的Oracle专有的方法。要使用Oracle专有的批处理机制,可以以如下所示的方式调用setExecuteBatch(): PreparedStatement pstmt3D null; try { ((OraclePreparedStatement) pstmt).setExecuteBatch(30); ... pstmt.executeUpdate(); }   调用setExecuteBatch()时指定的值是一个上限,当达到该值时,就会自动地引发SQL命令执行,标准的executeUpdate()方法就会被作为批处理送到数据库中。我们可以通过调用PreparedStatement类的sendBatch()方法随时传输批处理任务。   7、使用Oracle locator方法插入、更新大对象(LOB)   Oracle的PreparedStatement类不完全支持BLOB和CLOB等大对象的处理,尤其是Thin驱动程序不支持利用PreparedStatement对象的setObject()和setBinaryStream()方法设置BLOB的值,也不支持利用setCharacterStream()方法设置CLOB的值。只有locator本身中的方法才能够从数据库中获取LOB类型的值。可以使用PreparedStatement对象插入或更新LOB,但需要使用locator才能获取LOB的值。由于存在这二个问题,因此,我建议使用locator的方法来插入、更新或获取LOB的值。   8、使用SQL92语法调用存储过程   在调用存储过程时,我们可以使用SQL92或Oracle PL/SQL,由于使用Oracle PL/SQL并没有什么实际的好处,而且会给以后维护你的应用程序的开发人员带来麻烦,因此,我建议在调用存储过程时使用SQL92。   9、使用Object SQL将对象模式转移到数据库中   既然可以将Oracle的数据库作为一种面向对象的数据库来使用,就可以考虑将应用程序中的面向对象模式转到数据库中。目前的方法是创建Java bean作为伪装的数据库对象,将它们的属性映射到关系表中,然后在这些bean中添加方法。尽管这样作在Java中没有什么问题,但由于操作都是在数据库之外进行的,因此其他访问数据库的应用软件无法利用对象模式。如果利用Oracle的面向对象的技术,可以通过创建一个新的数据库对象类型在数据库中模仿其数据和操作,然后使用JPublisher等工具生成自己的Java bean类。如果使用这种方式,不但Java应用程序可以使用应用软件的对象模式,其他需要共享你的应用中的数据和操作的应用软件也可以使用应用软件中的对象模式。   10、利用SQL完成数据库内的操作   我要向大家介绍的最重要的经验是充分利用SQL的面向集合的方法来解决数据库处理需求,而不是使用Java等过程化的编程语言。   如果编程人员要在一个表中查找许多行,结果中的每个行都会查找其他表中的数据,最后,编程人员创建了独立的UPDATE命令来成批地更新第一个表中的数据。与此类似的任务可以通过在set子句中使用多列子查询而在一个UPDATE命令中完成。当能够在单一的SQL命令中完成任务,何必要让数据在网上流来流去的?我建议用户认真学习如何最大限度地发挥SQL的功能。 1、查找驱动程序 MySQL目前提供的java驱动程序为Connection/J,可以从MySQL官方网站下载,并找到mysql-connector-java-3.0.15-ga-bin.jar文件,此驱动程序为纯java驱动程序,不需做其他配置。 2、动态指定classpath 如果需要执行时动态指定classpath,就在执行时采用-cp方式。否则将上面的.jar文件加入到classpath环境变量中。 3、加载驱动程序 try{ Class.forName(com.mysql.jdbc.Driver); System.out.println(Success loading Mysql Driver!); }catch(Exception e) { System.out.println(Error loading Mysql Driver!); e.printStackTrace(); } 4、设置连接的url jdbc:mysql://localhost/databasename[?pa=va][&pa=va] 三、以下列出了在使用JDBC来连接Oracle数据库时可以使用的一些技巧,这些技巧能够使我们更好地发挥系统的性能和实现更多的功能(系转载)。   1、在客户端软件开发中使用Thin驱动程序   在开发Java软件方面,Oracle的数据库提供了四种类型的驱动程序,二种用于应用软件、applets、servlets等客户端软件,另外二种用于数据库中的Java存储过程等服务器端软件。在客户机端软件的开发中,我们可以选择OCI驱动程序或Thin驱动程序。OCI驱动程序利用Java本地化接口(JNI),通过Oracle客户端软件与数据库进行通讯。Thin驱动程序是纯Java驱动程序,它直接与数据库进行通讯。为了获得最高的性能,Oracle建议在客户端软件的开发中使用OCI驱动程序,这似乎是正确的。但我建议使用Thin驱动程序,因为通过多次测试发现,在通常情况下,Thin驱动程序的性能都超过了OCI驱动程序。   2、关闭自动提交功能,提高系统性能   在第一次建立与数据库的连接时,在缺省情况下,连接是在自动提交模式下的。为了获得更好的性能,可以通过调用带布尔值false参数的Connection类的setAutoCommit()方法关闭自动提交功能,如下所示:   conn.setAutoCommit(false);   值得注意的是,一旦关闭了自动提交功能,我们就需要通过调用Connection类的commit()和rollback()方法来人工的方式对事务进行管理。   3、在动态SQL或有时间限制的命令中使用Statement对象   在执行SQL命令时,我们有二种选择:可以使用PreparedStatement对象,也可以使用Statement对象。无论多少次地使用同一个SQL命令,PreparedStatement都只对它解析和编译一次。当使用Statement对象时,每次执行一个SQL命令时,都会对它进行解析和编译。这可能会使你认为,使用PreparedStatement对象比使用Statement对象的速度更快。然而,我进行的测试表明,在客户端软件中,情况并非如此。因此,在有时间限制的SQL操作中,除非成批地处理SQL命令,我们应当考虑使用Statement对象。   此外,使用Statement对象也使得编写动态SQL命令更加简单,因为我们可以将字符串连接在一起,建立一个有效的SQL命令。因此,我认为,Statement对象可以使动态SQL命令的创建和执行变得更加简单。   4、利用helper函数对动态SQL命令进行格式化   在创建使用Statement对象执行的动态SQL命令时,我们需要处理一些格式化方面的问题。例如,如果我们想创建一个将名字O'Reilly插入表中的SQL命令,则必须使用二个相连的“''”号替换O'Reilly中的“'”号。完成这些工作的最好的方法是创建一个完成替换操作的helper方法,然后在连接字符串心服用公式表达一个SQL命令时,使用创建的helper方法。与此类似的是,我们可以让helper方法接受一个Date型的值,然后让它输出基于Oracle的to_date()函数的字符串表达式。   5、利用PreparedStatement对象提高数据库的总体效率   在使用PreparedStatement对象执行SQL命令时,命令被数据库进行解析和编译,然后被放到命令缓冲区。然后,每当执行同一个PreparedStatement对象时,它就会被再解析一次,但不会被再次编译。在缓冲区中可以发现预编译的命令,并且可以重新使用。在有大量用户的企业级应用软件中,经常会重复执行相同的SQL命令,使用PreparedStatement对象带来的编译次数的减少能够提高数据库的总体性能。如果不是在客户端创建、预备、执行PreparedStatement任务需要的时间长于Statement任务,我会建议在除动态SQL命令之外的所有情况下使用PreparedStatement对象。   6、在成批处理重复的插入或更新操作中使用PreparedStatement对象   如果成批地处理插入和更新操作,就能够显著地减少它们所需要的时间。Oracle提供的Statement和 CallableStatement并不真正地支持批处理,只有PreparedStatement对象才真正地支持批处理。我们可以使用addBatch()和executeBatch()方法选择标准的JDBC批处理,或者通过利用PreparedStatement对象的setExecuteBatch()方法和标准的executeUpdate()方法选择速度更快的Oracle专有的方法。要使用Oracle专有的批处理机制,可以以如下所示的方式调用setExecuteBatch(): PreparedStatement pstmt3D null; try { ((OraclePreparedStatement) pstmt).setExecuteBatch(30); ... pstmt.executeUpdate(); }   调用setExecuteBatch()时指定的值是一个上限,当达到该值时,就会自动地引发SQL命令执行,标准的executeUpdate()方法就会被作为批处理送到数据库中。我们可以通过调用PreparedStatement类的sendBatch()方法随时传输批处理任务。   7、使用Oracle locator方法插入、更新大对象(LOB)   Oracle的PreparedStatement类不完全支持BLOB和CLOB等大对象的处理,尤其是Thin驱动程序不支持利用PreparedStatement对象的setObject()和setBinaryStream()方法设置BLOB的值,也不支持利用setCharacterStream()方法设置CLOB的值。只有locator本身中的方法才能够从数据库中获取LOB类型的值。可以使用PreparedStatement对象插入或更新LOB,但需要使用locator才能获取LOB的值。由于存在这二个问题,因此,我建议使用locator的方法来插入、更新或获取LOB的值。   8、使用SQL92语法调用存储过程   在调用存储过程时,我们可以使用SQL92或Oracle PL/SQL,由于使用Oracle PL/SQL并没有什么实际的好处,而且会给以后维护你的应用程序的开发人员带来麻烦,因此,我建议在调用存储过程时使用SQL92。   9、使用Object SQL将对象模式转移到数据库中   既然可以将Oracle的数据库作为一种面向对象的数据库来使用,就可以考虑将应用程序中的面向对象模式转到数据库中。目前的方法是创建Java bean作为伪装的数据库对象,将它们的属性映射到关系表中,然后在这些bean中添加方法。尽管这样作在Java中没有什么问题,但由于操作都是在数据库之外进行的,因此其他访问数据库的应用软件无法利用对象模式。如果利用Oracle的面向对象的技术,可以通过创建一个新的数据库对象类型在数据库中模仿其数据和操作,然后使用JPublisher等工具生成自己的Java bean类。如果使用这种方式,不但Java应用程序可以使用应用软件的对象模式,其他需要共享你的应用中的数据和操作的应用软件也可以使用应用软件中的对象模式。

81,092

社区成员

发帖
与我相关
我的任务
社区描述
Java Web 开发
社区管理员
  • Web 开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧