JdbcHelper

来源:互联网 发布:mac电脑pdf如何编辑 编辑:程序博客网 时间:2024/05/21 21:34


// *由于之前在学习和书写.Net程序时,有一个国外高手写的处理数据库操作的开源工具类非常
// *好用,叫做 SqlHelper, 学习jdbc时发现java对jdbc的一些方法处理比不上ADO.NET那
// *样人性化,编程时多有不便,遂模仿C#版的SqlHelper,编写了一个类似的工具类,就叫做JdbcHelper吧
// *
// *由于技术有限,个中不足,望大家见谅和指正
// *
// * 切记:
// * 不要忘记导入驱动的jar包
// * 在使用此工具类之前,应该先把'dbinfo.properties'文件配置好,
// * 如果是在Eclipse或者MyEclipse下,可以将配置文件dbinfo.properties放在与
// * 此类的源文件相同的目录中,若在其它编译环境下,应将配置文件放在与编译好的.class文件同一目录中

package jdbchelper;

import java.io.BufferedInputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;


/**
 * @author LZY
 * liu-zi-yi@foxmail.com
 *
 * 版本:v2.10.8.12
 *
 * 日期:2010/08/12
 *
 * 此工具类实现了常用的JDBC增删改查
 * 此类所有方法全部为静态方法,方便直接调用
 * 使用时不需要创建连接,不需要考虑关闭连接,系统会自动处理连接的打开与回收
 * 通过此类的方法返回的ResultSet,在使用完毕后可以直接调用ResultSet对象的close()方法,系统会自
 * 动将其关闭,并将其对应的Statement关闭,而且将其对应 的Connection对象自动放入系统连接池。
 * 在做增删改查操作时,不需要创建连接,虽然本类中有getConnection()方法,可以创建并返回一个连接,但仅供不时之需,一般不建议使用。
 * 即便使用了getConnection()方法,其返回的连接在使用完毕后,如果调用它的close()方法,系统也会自动将其存入系统连接池予 以管理
 *
 *
 * 在使用此工具类之前,应该先把'dbinfo.properties'文件配置好,
 * 如果是在Eclipse或者MyEclipse下,可以将配置文件dbinfo.properties放在与
 * 此类的源文件相同的目录中,若在其它编译环境下,应将配置文件放在与编译好的.class文件同一目录中
 *
 *
 *
 * 由于暂时没有想到很好的方法去解决所有带返回值的存储过程或函数的执行方法
 * 所以此工具类暂时没有实现执行过程和函数的方法,如果要使用可调用getConnection()创建连接,手动书写,万分抱歉……
 * ------------------------------------------------------------------------------------------
 *
 * 有些比较旧版本的数据库驱动关于ParameterMetaData
 * 的方法没有实现,虽然我在JdbcHelper代码中已经做过相应处理,不会有太大问题,但还是建议使用较新版本的驱动
 * 
 */
public class JdbcHelper
{
 private JdbcHelper(){}
 
 private static String user="";
 private static String password;
 private static String url="";
 private static String driver="";
 
 //存放Connection对象的连接池
 private static List<Connection> connSet=new LinkedList<Connection>();
 //允许建立的最大连接数(暂未用)
 private static int max_connections=0;
 //当前已经创建的连接数(暂未用)
 private static int currentConns=0;
 
 
 static
 { 
  Properties prot=new Properties();
  try
  {
   //从属性配置文件<dbinfo.properties>中读取连接属性,此文件应放在与该类相同的路径下
   BufferedInputStream bis=new BufferedInputStream(JdbcHelper.class.getResourceAsStream("dbinfo.properties"));
   prot.load(bis);
   bis.close();
   user=prot.getProperty("user");
   password=prot.getProperty("password");
   url=prot.getProperty("url");
   driver=prot.getProperty("driver");
   max_connections=Integer.parseInt(prot.getProperty("max_connections"));
   Class.forName(driver);
  }
  catch(Exception ex)
  {
   ex.printStackTrace();
   throw new ExceptionInInitializerError(ex);
  }
 }
  
 
 /**
  * InvocationHandler接口的实现类(静态内部类),供Proxy动态代理类调用
  * 之所以不用匿名内部类实现,是因为将要访问外围类资源,就要将一些比较大的资源的引用设置成final类型,那样这些资源就不会轻易变成垃圾被析构
  * 遂采用静态内部类实现,为了不让外围类访问到此内部类,将其设置成private
  */
 private static class Handler implements InvocationHandler
 {

  private Object obj;
  
  public Handler(ResultSet rs)
  {
   this.obj=rs;
  }
  public Handler(Connection conn)
  {
   this.obj=conn;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
  {
   if("close".equals(method.getName()))
   {
    if(obj instanceof ResultSet)
     JdbcHelper.close((ResultSet)obj);
    else
     JdbcHelper.close((Connection)obj);
    
    return null;
   }
   else
   {
    return method.invoke(obj,args);
   }
  }
 
 }
 
 /**
  * private 方法
  * 封装一个ResultSet结果集对象,并返回封装之后的ResultSet结果集对象
  * @param rs - 期望被封装的ResultSet对象
  * @return 封装过的ResultSet结果集对象
  */
 private static ResultSet getFixedResultSet(ResultSet rs)
 {
  ResultSet fixedRs=(ResultSet)Proxy.newProxyInstance(
               JdbcHelper.class.getClassLoader(),
               new Class[]{ResultSet.class},
               new Handler(rs)
                 );
  return fixedRs;
 }
 
 /**
  * 获取一个封装过的Connection连接对象
  * 在此对象 上调用close()方法会将此对象放入系统连接池
  * @return 封装过的Connection连接对象
  * @throws SQLException
  */
 synchronized public static Connection getConnection() throws SQLException
 {
  Connection conn = DriverManager.getConnection(url,user,password);
  Connection fixedConn=(Connection)Proxy.newProxyInstance(
                JdbcHelper.class.getClassLoader(),
                new Class[]{ResultSet.class},
                new Handler(conn)
                     );
  return fixedConn;
 }
 
 /**
  * private方法
  * 从系统连接池获取一个Connection对象
  * 该对象使用完毕后应该调用JdbcHelper.close(Connection conn) 方法放回到系统连接池
  * @return 未被封装过的Connection对象
  * @throws SQLException
  */
 synchronized private static Connection newConnection() throws SQLException
 {
  if(connSet.size()==0)
  {
   Connection conn = DriverManager.getConnection(url,user,password);
   return conn;
  }
  else
  {
   return connSet.remove(0);
  }
 }
 
 /**
  *private方法
  *供封装ResultSet结果集使用
  *调用此方法来关闭ResultSet,从而可以顺带关闭与其关联的Statement,并将其连接放回到连接池
  * @param rs
  * @throws SQLException
  */
 private static void close(ResultSet rs) throws SQLException
 {
  Statement stmt;
  Connection conn;
  if(rs!=null)
  {
   stmt=rs.getStatement();
   rs.close();
   if(stmt!=null)
   {
    conn=stmt.getConnection();
    stmt.close();
    
    close(conn);
   }
  }
 }
 
 /**
  * private方法
  * 调用此方法来处理Connection,从而将连接放回到连接池
  * @param conn
  * @throws SQLException
  */
 synchronized private static void close(Connection conn) throws SQLException
 {
  if(conn!=null&&!conn.isClosed())
   connSet.add(conn);
 }
 
 /**
  * 执行一条SQL查询语句,并返回重新封装过的结果集
  * @param selectSql - SQL查询语句
  * @return 返回重新封装过的查询结果集,用户使用完毕后可以调用它的close()方法关闭,系统将自动关闭与其对应的Statement,并将其对应的Connection放入系统连接池予以管理
  * @throws SQLException
  */
 public static ResultSet executeQuery(String selectSql) throws SQLException
 {
  Connection conn=newConnection();
  Statement stmt=conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
  ResultSet rs=stmt.executeQuery(selectSql);
  return getFixedResultSet(rs);
 }
 
 /**
  * 执行一条含有参数的SQL查询语句,并返回重新封装过的结果集
  * @param sql 可能包含一个或多个 '?' 参数占位符的 SQL 查询语句
  * @param args 使用给定对象设置指定参数的值,值类型是一个或多个参数,或一个Object[]数组
  * 注意:如果传入的操作参数的个数与SQL命令中'?'占位符的个数不相等,则会抛出SQLException
  * @return 返回重新封装过的查询结果集,用户使用完毕后可以调用它的close()方法关闭,系统将自动关闭与其对应的Statement,并将其对应的Connection放入系统连接池予以管理
  * @throws SQLException
  */
 public static ResultSet executeQuery(String sql,Object... args) throws SQLException
 {
  Connection conn=newConnection();
  PreparedStatement pstmt=conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
  
  int count=JdbcHelper.getParameterCount(pstmt,sql);
  if(count<=0||args==null||args.length==0||args.length!=count)
   throw new SQLException("Count of the parameters is invalid!");
  
  for(int i=0;i<count;i++)
  {
   pstmt.setObject(i+1, args[i]);
  }
  
  ResultSet rs= pstmt.executeQuery();
  return getFixedResultSet(rs);
 }
 
 /**
  * 执行一条dml操作sql命令
  * @param dmlSql 一条dml操作SQL命令字符串
  * @return 受影响的行数
  * @throws SQLException
  */
 public static int executeUpdate(String dmlSql) throws SQLException
 {
  Connection conn=newConnection();
  Statement stmt=conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
  int returnValue = stmt.executeUpdate(dmlSql);
  stmt.close();
  close(conn);
  return returnValue;
 }
 
 /**
  * 批量 执行传入的一条或多条异构sql语句,或者执行传入的一个异构sql语句数组
  * 等同于 JdbcHelper.executeBatch(String... dmlSql)方法
  * @param dmlSqls 一条或多条sql语句,或一个sql语句数组
  * @return 包含每个sql命令的的更新计数所组成的数组。数组的元素根据将命令添加到批中的顺序排序。
  * @throws SQLException
  */
 public static int[] executeUpdate(String... dmlSqls) throws SQLException
 {
  if(dmlSqls==null||dmlSqls.length==0)
   throw new SQLException("Count of the parameters is invalid!");
  Connection conn=newConnection();
  Statement stmt=conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
  for(String str:dmlSqls)
  {
   stmt.addBatch(str);
  }
  int[] returnValues = stmt.executeBatch();
  stmt.close();
  close(conn);
  return returnValues;
 }
 
 /**
  * 批量执行多条同构sql语句,sql命令应该是dml操作
  * @param dmlSqlTemp  可能包含一个或多个 '?' 参数占位符的 SQL 语句
  * @param args 使用给定对象设置指定参数的值,值类型是一个或多个参数,或一个Object[]数组
  * 注意:如果传入的操作参数的个数不是sql中'?'占位符个数的正整数倍,则会抛出SQLException
  * @return 包含每个sql命令的的更新计数所组成的数组。数组的元素根据每一组操作参数的执行顺序排序。
  * @throws SQLException
  */
 public static int[] executeUpdate(String dmlSqlTemp,Object... args) throws SQLException
 {
  int[] returnValues=null;
  Connection conn=newConnection();
  PreparedStatement pstmt=conn.prepareStatement(dmlSqlTemp, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
  
  int count=JdbcHelper.getParameterCount(pstmt,dmlSqlTemp);
  if(count<=0||args==null||args.length==0||args.length%count!=0)
   throw new SQLException("Count of the parameters is invalid!");
  
  int items=args.length/count;
  for(int i=0;i<items;i++)
  {
   for(int j=0;j<count;j++)
   {
    pstmt.setObject(j+1, args[i*count+j]);
   }
   pstmt.addBatch();
  }
  returnValues= pstmt.executeBatch();
  pstmt.close();
  close(conn);
  return returnValues;
 }
 
 /**
  * 批量执行多条同构sql语句,sql命令应该是dml操作
  * @param dmlSqlTemp  可能包含一个或多个 '?' 参数占位符的 SQL 语句
  * @param args 使用给定对象设置指定参数的值,值类型是个Object[][]数组,
  * 每次要执行的参数先放入一个Object数组中,然后将所有的Object数组再放入一个新的Object数组中,从而构成一个Object[][]二为数组
  * 注意:如果传入的操作参数的个数(Object[]中的每个Object数组的个数)不是sql中'?'占位符个数的正整数倍,则会抛出SQLException
  * @return 包含每个sql命令的的更新计数所组成的数组。数组的元素根据每一组操作参数的执行顺序排序。
  * @throws SQLException
  */
 public static int[] executeUpdate(String dmlSqlTemp,Object[][] args) throws SQLException
 {
  int[] returnValues=null;
  Connection conn=newConnection();
  PreparedStatement pstmt=conn.prepareStatement(dmlSqlTemp, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
  
  int count=JdbcHelper.getParameterCount(pstmt,dmlSqlTemp);
  if(count<=0||args==null||args.length==0)
   throw new SQLException("Count of the parameters is invalid!");
  for(int i=0;i<args.length;i++)
  {
   if(args[i]==null||args[i].length==0||args[i].length%count!=0)
    throw new SQLException("Count of the parameters is invalid at row:"+i+"!");
  }
  
  int items=args.length;
  for(int i=0;i<items;i++)
  {
   for(int j=0;j<count;j++)
   {
    pstmt.setObject(j+1, args[i][j]);
   }
   pstmt.addBatch();
  }
  returnValues= pstmt.executeBatch();
  pstmt.close();
  close(conn);
  return returnValues;
 }
 
 
 /**
  * 批量执行多条同构sql语句,sql命令应该是dml操作
  * @param dmlSqlTemp  可能包含一个或多个 '?' 参数占位符的 SQL 语句
  * @param args 使用给定对象设置指定参数的值,值类型是包含一个或多个参数的List
  * 注意:如果传入的List中的元素的个数不是sql中'?'占位符个数的正整数倍,则会抛出SQLException
  * @return 包含每个sql命令的的更新计数所组成的数组。数组的元素根据每一组操作参数的执行顺序排序。
  * @throws SQLException
  */
 public static int[] executeUpdate(String dmlSqlTemp,List<Object> args) throws SQLException
 {
  int[] returnValues=null;
  Connection conn=newConnection();
  PreparedStatement pstmt=conn.prepareStatement(dmlSqlTemp, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
  
  int count=JdbcHelper.getParameterCount(pstmt,dmlSqlTemp);
  if(count<=0||args==null||args.size()==0||args.size()%count!=0)
   throw new SQLException("Count of the parameters is invalid!");
  
  int items=args.size()/count;
  for(int i=0;i<items;i++)
  {
   for(int j=0;j<count;j++)
   {
    pstmt.setObject(j+1, args.get(i*count+j));
   }
   pstmt.addBatch();
  }
  returnValues= pstmt.executeBatch();
  pstmt.close();
  close(conn);
  return returnValues;
 }
 
 
 
 /**
  * 批量 执行传入的一条或多条异构sql语句,或者执行传入的一个异构sql语句数组
  * 等同于 JdbcHelper.executeUpdate(String... dmlSql)方法
  * 建议调用JdbcHelper.executeUpdate(String... dmlSql)
  * @param dmlSqls 一条或多条sql语句,或一个sql语句数组
  * @return 包含每个sql命令的的更新计数所组成的数组。数组的元素根据将命令添加到批中的顺序排序。
  * @throws SQLException
  */
 public static int[] executeBatch(String... dmlSqls) throws SQLException
 {
  return executeUpdate(dmlSqls);
 }
 
 /**
  *
  * @param sql语句
  * @return 如果结果为 ResultSet对象,则返回 true;如果其为更新计数或者不存在任何结果,则返回 false
  * @throws SQLException
  */
 public static boolean execute(String sql) throws SQLException
 {
  Connection conn=newConnection();
  Statement stmt=conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
  boolean resultFlag=stmt.execute(sql);
  stmt.close();
  close(conn);
  return resultFlag;
 }
 
 
 /**
  * private方法
  * 发现部分老版本的数据库驱动不支持和ParameterMetaData有关的相关方法,会抛出错误,遂
  * 将自己写的方法放在catch块中,但不很完善,所以,还是建议使用较新版本的数据库驱动
  * @param pstmt PreparedStatement对象
  * @param sql 传入的带有sql占位符的sql语句
  * @return 操作数个数
  */
 private static int getParameterCount(PreparedStatement pstmt,String sql)
 {
  try
  {
   ParameterMetaData pmd = pstmt.getParameterMetaData();
   int count=pmd.getParameterCount();
   return count;
  }
  catch (Throwable e)
  {
   int count=0;
   for(int i=0;i<sql.length();i++)
   {
    if(sql.charAt(i)=='?')
    {
     count++;
    }
   }
   return count;
  }
 }

}

//-------------------------------------------------------------------------------------------------------

/*--------------------------------------------------------------------------------------------------------

#配置文件“dbinfo.properties”中内容示例

user=lzy
password=lzy
driver=oracle.jdbc.driver.OracleDriver
url=jdbc/:oracle/:thin/:@127.0.0.1/:1521/:xe
max_connections=100

*/

原创粉丝点击