关于JDBC中Connection的使用的几个details
最近一个小项目dao层涉及对connection的获取,优化的时候顺便了解了一下jdbc中connection使用的几个细节问题,跟大家分享一下:
1. 通常情况下,应在每个方法调用时新建connection。
一般要在每个方法调用时新建connection,因为connection实例并不是线程安全的,下面给出mysql中调用connection实现类的方法(com.mysql.jdbc.preparedstatement中executeupdate())实例:
/** * com.mysql.jdbc.preparedstatement中的executeupdete() * @throws sqlexception */ protected synchronized int executeupdate(byte[][] batchedparameterstrings, inputstream[] batchedparameterstreams, boolean[] batchedisstream, int[] batchedstreamlengths, boolean[] batchedisnull, boolean isreallybatch) throws sqlexception { checkclosed(); //声明mysqlconnection mysqlconnection locallyscopedconn = this.connection; if (locallyscopedconn.isreadonly()) { throw sqlerror.createsqlexception(messages.getstring("preparedstatement.34") //$non-nls-1$ + messages.getstring("preparedstatement.35"), //$non-nls-1$ sqlerror.sql_state_illegal_argument, getexceptioninterceptor()); } if ((this.firstcharofstmt == 's') && isselectquery()) { //$non-nls-1$ throw sqlerror.createsqlexception(messages.getstring("preparedstatement.37"), //$non-nls-1$ "01s03", getexceptioninterceptor()); //$non-nls-1$ } if (this.results != null) { if (!locallyscopedconn.getholdresultsopenoverstatementclose()) { this.results.realclose(false); } } resultsetinternalmethods rs = null; // the checking and changing of catalogs // must happen in sequence, so synchronize // on the same mutex that _conn is using /*注意此处,sychronized(connection实例) catalogs的检查和更改必须按顺序进行,因此在_conn正在使用的synchronized 进行同步*/ synchronized (locallyscopedconn) { buffer sendpacket = fillsendpacket(batchedparameterstrings, batchedparameterstreams, batchedisstream, batchedstreamlengths); string oldcatalog = null; if (!locallyscopedconn.getcatalog().equals(this.currentcatalog)) { oldcatalog = locallyscopedconn.getcatalog(); locallyscopedconn.setcatalog(this.currentcatalog); } // // only apply max_rows to selects // if (locallyscopedconn.usemaxrows()) { executesimplenonquery(locallyscopedconn, "set option sql_select_limit=default"); } boolean oldinfomsgstate = false; if (this.retrievegeneratedkeys) { oldinfomsgstate = locallyscopedconn.isreadinfomsgenabled(); locallyscopedconn.setreadinfomsgenabled(true); } rs = executeinternal(-1, sendpacket, false, false, null, isreallybatch); if (this.retrievegeneratedkeys) { locallyscopedconn.setreadinfomsgenabled(oldinfomsgstate); rs.setfirstcharofquery(this.firstcharofstmt); } if (oldcatalog != null) { locallyscopedconn.setcatalog(oldcatalog); } }
一个connection对象只能被一个线程所使用,如果涉及到多个线程同时调用同一个connection将造成数据的错乱。因此,一般应在一个方法被调用时新建connection。因此多线程情况下,可以使用连接池来提升性能,数据库连接池可以保证connection实例交替被多个线程使用。
2. 使用数据库连接池时,如何保证一个线程中的所有数据库操作使用的是同一个connection。
若想要同一个线程获取到的connection为同一个有两种方法:
其一,可以使用threadlocal保存connection。threadlocal内部是用map来保存数据。其中map键值对的减是当前线程而值即为当前线程对于的connection。这种方法适用于要求每次请求得到的connection都是唯一的,当线程消亡时才会将connection交给数据库连接池。
在网上看到有些文章交代要看数据库连接池本身是否使用了threadlocal然后看了下java.sql包和javax.sql下的connection和数据库连接池都是interface,每个datasourcepool具体可以自己更改实现类,datasourcepool的具体实现需要的话可以采取threadlocal来存放connection。
其二,可以直接交给spring去管理,在srping中每个事务和一个connection绑定,在事务回滚时该connection才会被返回数据库连接池。
自己的浅薄理解,若有异议欢迎提出~