written by chenshu,郭春晓
为什么选择JDBC
SSH架构在中国很流行,可是很多人都觉得Hibernate太慢了,所以有了MyBatis.从数据库开发技术上,EJB3大幅度借鉴了Hibernate技术,因此性能也不高。Spring的trasaction框架和MyBatis结合起来是个非常好的选择,使用简单,程序员可以完全掌控SQL语句。
当我处理1000万数据的时候,我发现还是JDBC性能最高。究其原因,在于MyBatis+Spring框架内部大量使用了reflection机制,那个东西会让你的代码看起来很简单,可惜性能很慢,据<<Effective Java>>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没有解决,得到的结果仍然是更改之前的数据。
分享到:
相关推荐
英文版jdbc使用参考手册,很高兴与大家分享。
本文档主要讲解Spring与mybatis与jdbc结合使用的方法和例子。
JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单写法JDBC简单...
c-jdbc的入门教程,以mysql为例,详细讲解安装、配置,最后使用具体的程序实现c-jdbc的使用。
JAVAEE中有关JDBC使用的说明书
jdbc jdbc jdbc jdbc jdbc jdbc jdbc jdbc
了解数据库联接、了解JDBC 掌握JDBC在项目开发中的使用方法 使用JDBC连接数据库 使用JDBC操作数据库 实现单表的增加删除修改
JDBC的使用技巧,包括JDBC中很多的常用操作,好的设计思想。
jdbc教案jdbc教案jdbc教案jdbc教案jdbc教案jdbc教案jdbc教案jdbc教案jdbc教案,JDBC详细使用过程
使用JDBC连接Oracle数据库使用JDBC连接Oracle数据库
JDBC的简单示例,第一次学习时写下的,要导入一个jdbc的jar包,原来的路径要覆盖删掉
commons-utils简化JDBC使用(文档+示例)
JDBC代码 JDBC代码JDBC代码 JDBC代码
JDBC基础JDBC基础JDBC基础JDBC基础JDBC基础JDBC基础JDBC基础JDBC基础JDBC基础
Sharding-JDBC使用案例-分库分表 分库分表
里面有springjdbc的使用,在配置文件中还包含了一份详细的mybatis-generator配置以及自动生成的mybatis代码,加快开发的效率
JDBC笔记 JDBC笔记 JDBC笔记
JDBC是数据库链接驱动,此课件介绍了JDBC的使用。
JDBC详解 JDBC详解JDBC详解 JDBC详解JDBC详解 JDBC详解
JDBC使用频率非常高故推荐上课时用的教材。帮助新手快速入门。