热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JDBC使用经验之谈

writtenbychenshu,郭春晓为什么选择JDBCSSH架构在中国很流行,可是很多人都觉得Hibernate太慢了,所以有了MyBatis.从数据库开发技术上,EJB3大幅度借鉴
 written by chenshu,郭春晓

为什么选择JDBC

SSH架构在中国很流行,可是很多人都觉得Hibernate太慢了,所以有了MyBatis.从数据库开发技术上,EJB3大幅度借鉴了Hibernate技术,因此性能也不高。Spring的trasaction框架和MyBatis结合起来是个非常好的选择,使用简单,程序员可以完全掌控SQL语句。
当我处理1000万数据的时候,我发现还是JDBC性能最高。究其原因,在于MyBatis+Spring框架内部大量使用了reflection机制,那个东西会让你的代码看起来很简单,可惜性能很慢,据<>2nd提到,慢2-50倍。

JDBC使用经验

设计

  JDBC的使用代码很简单,首先读取配置文件里面的数据库链接信息,然后建立链接,然后进行查询或者更新操作。最后可能会提交事务或者回滚。但是如何在一个面向对象的体体系结构里面重用这些代码呢。所以很自然的采用DAO的方式,用一个类提供方法,来封装这些代码。这显然是一个很好的做法。
  不过有一个更好的做法,参考Spring+MyBatis的做法,我们应该将数据访问的行为封装在一个Service类里面,将数访操作的对象封装在Java bean中。看上去和上面DAO做法没什么不同,区别在于Service的概念比DAO广,在复杂的业务中,常常需要和多个存储介质打交道,比如Cache server,MySQL或者MongoDB,甚至通过一个接口和另外的服务程序交互。而至于DAO的坚定的追随者,可以在位于Service的下一层实现自己的对JDBC的DAO封装。
  所以,这里的设计原则是:用Service层提供对数据,业务逻辑的封装,用Java bean封装数据。如有必要(如果应用特别复杂),可以在Service层下面实现自己的DAO封装,DAO层只针对特定的存储。

数据库连接池

  BoneCP显然是目前的最佳选择,比DHCP,C3P0这些有很多年头的产品性能更高,因为采用了Java 5引入的Concurrent包,引入了分区的概念。更重要的是,那些老产品已经不再维护了,停留在Java4之前的那个年代。在服务器程序中,BoneCP还可以帮助我们检查有没有未关闭的数据库连接。

读取primitive类型

代码如下,主要解决下面问题
假如我们直接使用 int age = rs.getInt("age"),如果age字段的值在数据表中为null,age将会被设置为0.这于真实情况不符。我们需要用Integer变量来保存这个值。下面的辅助类提供了解决方法。

public final class IntegerHelper {
private IntegerHelper() {
throw new UnsupportedOperationException("IntegerHelper is a helper class,can't be initated");
}

public static Integer getValue(ResultSet set,int index) throws SQLException{
int value = set.getInt(index);
if(set.wasNull()){
return null;
}
return Integer.valueOf(value);
}

public static Integer getValue(ResultSet set,String columnName) throws SQLException{
int value = set.getInt(columnName);
if(set.wasNull()){
return null;
}
return Integer.valueOf(value);
}

}

关闭statement,resultset和connection

  关键是在调用close函数的时候,还会抛出异常,需要catch.下面的代码假设整个系统都在Logback,负责吞没close抛出的异常,并写日志。


public final class CloseHelper {  private CloseHelper(){    throw new UnsupportedOperationException("CloseHelper is a helper class,can't be initated");  }  public static void close(Connection con , Logger log){    if(con!=null){      try{        con.close();      }catch(SQLException ex){        log.error(ex.getMessage(),ex);      }    }  }  public static void close(ResultSet set , Logger log){    if(set!=null){      try{        set.close();      }catch(SQLException ex){        log.error(ex.getMessage(),ex);      }    }  }  public static void close(Statement statement , Logger log){    if(statement!=null){      try{        statement.close();      }catch(SQLException ex){        log.error(ex.getMessage(),ex);      }    }  }

PreparedStatement中正确使用in clause语句

select * from table where xx in (?,?,?,?);
你可能想用in (?)这样的语句来代替上面的多个问号,然后调用setString(1,"a,b,c,d");
编译器不会报错,运行时大多数情况下是正确的,然后突然你发现有时候不能正确执行你预期的行为。最可怕的是粗心的程序员永远都没有发现这个问题。
如果in()里面的问号很多,数目是动态运算的,可以考虑使用Statement,而不是PreparedStatement,然后通过下面的代码填写。

String sql = select * from table where xx in (%s);String.format(sql, "a,b,c,d")

在你吞没SQLException之前要想一想

实现Service和DAO层的程序员要特别小心,不要轻易的catch SQLException又不再往上抛出(也就是吞没SQLException)。
调用代码可能如下:
updateDB(con);
loadAndSendEmail(con);

上面的代码假定updateDB(con)不抛出异常就正确,结果运行了loadAndSendEmail(con),而如果updateDB(con)抛出异常,第二行是永远不会执行的。

Log SQLException if necessary

可以参考下面的代码写日志。Service和DAO层的开发者一般不需要写日志。

初始化 Logger log = LoggerFactory.getLogger(getClass());
写日志 log.error(ex.getMessage(), ex);

known issues:

MySQL connection query cache

就是在同一个连接中,第一次查询后,查询出来的结果会自己放入缓存中,如果下次遇见相同的查询语句,直接从数据库缓存拿,而不是重新查询数据库。关于缓存是针对某个具体连接的测试代码如下。

问题描述如下,我们建立一个连接去查询数据库,然后用另外一个连接去更改数据库,然后结果仍然是更改以前的数据。测试代码如下.

public class ConnectionTest {    public static void main(String[] args) throws InterruptedException, SQLException {        Connection con = null,con1 = null;        PreparedStatement pst = null;        try {            con = MysqlConnectionFactory.getConnection();            String sql = "SELECT SQL_NO_CACHE level from wwere_game_angel where medalid = 1 and accountid = 587621";            pst = con.prepareStatement(sql);            ResultSet rs = pst.executeQuery();            if (rs.next()) {                System.out.println(rs.getInt(1));            }            con1 = MysqlConnectionFactory.getConnection();            String sql1 = "update wwere_game_angel set level = 2 where medalid = 1 and accountid = 587621";            pst = con1.prepareStatement(sql1);            pst.executeUpdate();            con.commit();            // 更新完毕重新查找一次            System.out.println("after  the update");            pst = con.prepareStatement("SELECT  level one from wwere_game_angel where medalid = 1 and accountid = 587621");            ResultSet rs1 = pst.executeQuery();            if (rs1.next()) {                System.out.println(rs1.getInt(1));            }        } catch (SQLException ex) {        } finally {        //这里自己测试没使用关闭帮助类,建议大家使用帮助类            pst.close();            con.close();            con1.close();        }    }}


目前按照官方的文档里面的解决方案,http://dev.mysql.com/doc/refman/5.0/en/query-cache-in-select.html没有解决,得到的结果仍然是更改之前的数据。


推荐阅读
  • 本文介绍了在使用Laravel和sqlsrv连接到SQL Server 2016时,如何在插入查询中使用输出子句,并返回所需的值。同时讨论了使用CreatedOn字段返回最近创建的行的解决方法以及使用Eloquent模型创建后,值正确插入数据库但没有返回uniqueidentifier字段的问题。最后给出了一个示例代码。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • Java学习笔记之使用反射+泛型构建通用DAO
    本文介绍了使用反射和泛型构建通用DAO的方法,通过减少代码冗余度来提高开发效率。通过示例说明了如何使用反射和泛型来实现对不同表的相同操作,从而避免重复编写相似的代码。该方法可以在Java学习中起到较大的帮助作用。 ... [详细]
  • 本文介绍了如何使用Java代码通过SimpleDateFormat类来获得只有年月日的Date对象,并解释了在日期格式化和解析过程中需要注意的问题。同时提供了示例代码和异常处理方法。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 怎么在PHP项目中实现一个HTTP断点续传功能发布时间:2021-01-1916:26:06来源:亿速云阅读:96作者:Le ... [详细]
  • React项目中运用React技巧解决实际问题的总结
    本文总结了在React项目中如何运用React技巧解决一些实际问题,包括取消请求和页面卸载的关联,利用useEffect和AbortController等技术实现请求的取消。文章中的代码是简化后的例子,但思想是相通的。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
author-avatar
奇异果产出国_706
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有