jdbc封装与多并发的共鸣

欢迎来到:

代码的封装是一门艺术,封装得好,不但给自己便利,还可以给自己的维护提供帮助;同时,封装得好,还可以给看自己代码的人以赏心悦目的感觉,团队之间的合作可以得到更良好的沟通;封装,不但要给人以便利,还要把自己的需求目的达到,这是一门相当有味的艺术。在此,博主一直热衷于代码的封装,有点经验,共享出来,写得不好之处希望指出,以便改过,觉得写得好的给个赞,有疑问之处请留言,在此先行拜谢! 数据库的使用,现在已经是随处可见,此博文着重于jdbc的封装。

(一)封装的要点: 首先,我们知道jdbc是java数据库连接,数据库的连接,咱们应该让它方便跨数据库。 其次,方便咱们进行获取java.sql.Connection的连接与关闭,以便进行数据库操作。 再其次,咱们应该让它方便我们的事务处理。 最后,也是本博文最关注的一点,便是伴随着数据库封装之后出现的多并发的问题。

(二)封装:第一: 跨数据库的封装,在此使用配置文件:jdbc.properties存放数据库连接的相关信息,然后封装一个获取数据库连接信息的类:JdbcPropertiesUtil以便信息的读取。 我的数据库为mysql,jdbc.properties文件信息如下:

jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8 jdbc.username=root jdbc.password=root

我的JdbcPropertiesUtil.java文件如下:

import java.io.IOException;import java.io.InputStream;import java.util.Properties;/** * 获取配置文件中的数据类 * @author observer * */public class JdbcPropertiesUtil {private Properties properties = new Properties();/*** 定义一个制定配置文件名的构造函数* @param propertiesName 需要获取的配置文件名,不包括后缀*/public JdbcPropertiesUtil(String propertiesName) {try {InputStream inputStream =Thread.currentThread().getContextClassLoader().getResourceAsStream(propertiesName+”.properties”);properties.load(inputStream);inputStream.close();} catch (IOException e) {e.printStackTrace();}}/*** 获取配置文件中数据库的加载class* @return 加载class的字符串*/public String getDriverClassName(){return properties.getProperty(“jdbc.driverClassName”);}/*** 获取配置文件中数据库连接的url* @return url的字符串*/public String getUrl(){return properties.getProperty(“jdbc.url”);}/*** 获取配置文件中数据库连接的用户名* @return 用户名的字符串*/public String getUsername(){return properties.getProperty(“jdbc.username”);}/*** 获取配置文件中数据库连接的密码* @return 密码的字符串*/public String getPassword(){return properties.getProperty(“jdbc.password”);}}

在此,如果想进一步封装的话,可以将获取inputStream的参数放在一个固定的配置文件properties中,如:a.properties中配置database.pathname=jdbc,然后根据文件中的参数得到"jdbc",然后再创建inputStream。这样的好处就是,在换数据库时,直接修改database.pathname的值,然后创建其相应名称的properties文件并配置就可以。如将mysql换成oracle,将a.properties中配置database.pathname=oracle;然后创建oracle.pathname并配置好即可。要是想要换回去,不用再创建,直接换database.pathname即可。在这里,小型系统一旦确定数据库,极少会再更改,所以这里就不再过分封装了。

第二: 封装数据库连接类DatabaseUtil。 首先因为数据库驱动的加载只要在该类第一次被访问时加载一次即可,所以在此把数据库驱动加载代码放到了一段静态代码中。 然后就是封装Connection,Connection是数据库的一个连接类,数据库的操作离不开它,楼主的第一个反应就是将该类的对象设置为一个静态变量,这样就可以一劳永逸了,不用再每次需要进行数据库操作时都对数据库连接一次,而且可以节省每次连接数据库的时间。但是,没有happy多久,这个想法就被虐杀在摇篮中,因为有事务处理与多并发带来的各种问题。举个很经典的例子,一个银行账户,在一台计算机上进行转账,在另一台计算机上进行网购,当转账转到一半时(此时刚好将来源账户的钱扣除,但是没有将钱打进目标账户),另一台计算机进行网购,然后将事务commit了;因为从始至终只有一个Connection,所以网购完成了,同时这边的转账出现异常也在中途结束了,最终的结果就是转账失败了,但是钱却没了。 通常的情况下,就是不设任何Connection,这样事情当然也就没有了。但是,这样做的话,与数据库打交道的类中(如DAO,以下均称DAO),每一项DAO的方法都不会知道后面的应用中是否会需要进行事物处理,也就是说DAO的每一个方法都需要传进一个Connection参数。同时,每一次需要用到DAO时,不管是否需要进行事务处理,都要在业务逻辑中得到Connection,然后才能调用DAO。这显然用着体验不是很好,起码楼主是这样觉得。 这个问题,曾经也困扰着楼主一段时间,直到某一天发现了一个很有趣的API:java.lang.ThreadLocal。该类有一个特性,那就是如果将该类的对象设为static,那么不同的线程访问它时,它都会生成一个局部变量,它独立于变量的初始化副本;意思就是说,每一个线程第一次访问它时都是不同的一个副本,而同一个线程再次访问时,访问的还是上一次访问的副本,祥情可以产看java的API。这么有意思的API,简直是相见恨晚!就跟为事务处理与多并发封装量身定制的一样。 既然有这么好用的一个API,那么封装条件就已经具备了,咱们说做就做,先上代码再解释:

import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;/** * 数据库连接工具类 * @author observer */public class DatabaseUtil {private static JdbcPropertiesUtil jpu = new JdbcPropertiesUtil(“jdbc”);// private static final ThrL threadLocal = new ThrL();private static final ThreadLocal<AutoCommit> threadLocal =new ThreadLocal<AutoCommit>();static {try {Class.forName(jpu.getDriverClassName());} catch (ClassNotFoundException ex) {System.out.println(“数据库驱动加载失败”);}}// static class ThrL{//public static AutoCommit autoCommit= null;;//public AutoCommit get(){//return this.autoCommit;//}// //public void set(AutoCommit autoCommit){//this.autoCommit = autoCommit;//}// //public void remove(){//autoCommit = null;//}// }/*** 事务管理辅助类*/private static class AutoCommit{private Connection conn = null;private boolean autoCommit = true;public void close(){if (conn != null) {try {conn.close();conn = null;autoCommit = true;} catch (SQLException e1) {e1.printStackTrace();}}}}/*** 如果threadLocal中已经存在AutoCommit(如调用过begin方法),这返回AutoCommit中的Connection,* 否则新建一个AutoCommit,然后创建一个Connection放到AutoCommit中,* 然后把AutoCommit放到threadLocal中,并返回Connection* @return 连接好数据库的java.sql.Connection*/public static Connection getConnection() {AutoCommit autoCommit = threadLocal.get();if(autoCommit==null){autoCommit = new AutoCommit();try {autoCommit.conn = DriverManager.getConnection(jpu.getUrl(),jpu.getUsername(), jpu.getPassword());threadLocal.set(autoCommit);} catch (SQLException e) {autoCommit.close();threadLocal.remove();e.printStackTrace();}}return autoCommit.conn;}/*** 如果threadLocal中的AutoCommit不为null,* 这创建一个AutoCommit并且创建数据库连接Connection并打开事务管理* 然后将AutoCommit放到threadLocal中*/public static void begin(){AutoCommit autoCommit = threadLocal.get();if(autoCommit==null){autoCommit = new AutoCommit();threadLocal.set(autoCommit);try {autoCommit.conn = DriverManager.getConnection(jpu.getUrl(),jpu.getUsername(), jpu.getPassword());autoCommit.conn.setAutoCommit(false);autoCommit.autoCommit = false;} catch (SQLException e) {autoCommit.close();threadLocal.remove();e.printStackTrace();}}}/*** 关闭java.sql.Connection*/public static void colseConn() {AutoCommit autoCommit = threadLocal.get();if (autoCommit.autoCommit && autoCommit.conn!=null) {autoCommit.close();threadLocal.remove();}}/*** 关闭java.sql.Statement* @param stml 需要关闭的java.sql.Statement*/public static void colseStml(Statement stml) {if (stml != null) {try {stml.close();} catch (SQLException e1) {e1.printStackTrace();}}}/*** 关闭java.sql.ResultSet* @param rs 关闭java.sql.ResultSet*/public static void colseRs(ResultSet rs) {if (rs != null) {try {rs.close();} catch (SQLException e1) {e1.printStackTrace();}}}/*** 当事务管理启动后,调用此方法进行提交事物,并且关闭java.sql.Connection*/public static void commit(){AutoCommit autoCommit = threadLocal.get();if(!autoCommit.autoCommit && autoCommit.conn!=null){try {autoCommit.conn.commit();} catch (SQLException e) {e.printStackTrace();}finally{autoCommit.close();threadLocal.remove();}}}/*** 当事务处理失败时调用此方法进行事务回滚,并且关闭java.sql.Connection*/public static void rollback(){AutoCommit autoCommit = threadLocal.get();if(!autoCommit.autoCommit && autoCommit.conn!=null){try {autoCommit.conn.rollback();} catch (SQLException e) {e.printStackTrace();}finally{autoCommit.close();threadLocal.remove();}}}}一定要记得挺身而出,即便帮不了忙,安慰也是最大的支持.

jdbc封装与多并发的共鸣

相关文章:

你感兴趣的文章:

标签云: