jdbc大略
一、概述JDBC
    JDBC从物理结构上说就是Java语言访问数据库的一套接口集合。
    从本质上来说就是调用者(程序员)和实现者(数据库厂商)之间的协议。
    JDBC API:
		使得开发人员可以使用纯Java的方式来连接数据库,并进行操作。
    ODBC:基于C语言的数据库访问接口。
    JDBC:是Java版的ODBC。
    JDBC 特性:高度的一致性、简单性(常用的接口只有4、5个)。
驱动程序按照工作方式分为四类:
    1、JDBC-ODBC bridge + ODBC 驱动
       JDBC-ODBC bridge桥驱动将JDBC调用翻译成ODBC调用,再由ODBC驱动翻译成访问数据库命令。
       优点:可以利用现存的ODBC数据源来访问数据库。
       缺点:从效率和安全性的角度来说的比较差。不适合用于实际项目。
    2、基于本地API的部分Java驱动
       我们应用程序通过本地协议跟数据库打交道。然后将数据库执行的结果通过驱动程序中的Java部分返回给客户端程序。
       优点:效率较高。
       缺点:安全性较差。
    3、纯Java的网络驱动
       (中间协议)            (本地协议)
       app    JDBC     纯Java                 中间服务器               DB
       缺点:两段通信,效率比较差
       优点:安全性较好
    4、纯Java本地协议:通过本地协议用纯Java直接访问数据库。
       特点:效率高,安全性好。
二、JDBC 编程的步骤
        import java.sql.*;
    0.参数化
        String driverName = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/test"; //协议;库或服务器名称;服务器IP,端口
        String username = "root";
        String password = "";
        // Oracle的连接
        String driverName = "oracle.jdbc.driver.OracleDriver";
        String url = "jdbc:oracle:thin:@192.168.0.23:1521:ora10g";
        // SQL Server 的连接
        String driverName = "com.microsoft.jdbc.sqlserver.SQLServerDriver";
        String url = "jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=test";
        //以下这些都需要写在有异常的代码块里,所以需要提取出来。
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;       // 建议用PreparedStatement
    1.加载和注册数据库驱动
        Class.forName(driverName);//自动注册;需要把驱动的jar包导进来;需处理异常
            /*方法二:实例化具体的Driver驱动,这写法一般不用(不能参数化驱动名,不够灵活)
            Driver driver = new com.mysql.jdbc.Driver();
            DriverManager.registerDriver(driver); //将驱动交于DriverManager托管*/
            /*方法三:Dos运行时,java -Djdbc.drives = oracle.jdbc.driver.OracleDriver; --可多个 */
    2.连接数据库
        conn = DriverManager.getConnection(url, username, password);//需处理异常
        //Connection返回数据库连接,如:“com.mysql.jdbc.Connection@1ffb8dc”;连接不成功则返回 null
    3.创建Statement对象
        stmt = conn.createStatement();//需处理异常,返回其生成结果的对象
        /*为了类型安全和批量更新的效率,建议用PreparedStatement
         String sql = "insert into tableName values(?,?)"; // "?"占位符
         stmt = conn.prepareStatement(sql); */
    4.操作数据库,执行SQL语句
        String sql = "select * from tableName";//SQL语句里不需要写分号
        rs = stmt.executeQuery(sql); //executeQuery(sqlString) 查询 返回查询结果集
            /* String sql = "insert into tableName values(?,?)"; // "?"占位符
            int number = stmt.executeUpdate(sql);//更新,再返回int(更新、修改影响的条数)
            //executeUpdate(sql) 执行给定 SQL 语句,如 INSERT、UPDATE 或 DELETE 等不返回任何内容的语句*/
       //用PreparedStatement时,已经有语句: rs = stmt.executeQuery();
    5.处理数据(游标)
        StringBuffer sb = new StringBuffer(); //缓存;用它可提高读取速度。当然,不用也可以。
        ResultSetMetaData md = rs.getMetaData(); //ResultSetMetaData可获取列的类型和属性信息
        int col = md.getColumnCount(); //获取列的数目
        while(rs.next()){ //rs.next()使游标下移一位,返回boolean,没有下一个结果则返回false
            for(int i=1; i<=col;i++){ // index(JDBC 的下标从1开始)
                sb.append(md.getColumnName(i)+"="+rs.getString(i)+"  ");
            } sb.append("\n");
        }System.out.println(sb);
            //1.游标的初始位置在第一条记录的前面,使第一次调用next()后,刚好拿到第一个结果。
            //2.游标的最终位置在最后一条记录的后面(结果集的前面和后面留空,真正存在)
    6.释放资源,断开与数据库的连接
        //先判断是否有引用资源,再释放(释放空资源会抛异常);注意顺序
        if(rs!=null)try{rs.close();}catch(Exception e){e.printStackTrace();}
        if(stmt!=null)try{stmt.close();}catch(Exception e){e.printStackTrace();}
        if(conn!=null)try{conn.close();}catch(Exception e){e.printStackTrace();}
        //这些异常没法处理,处理只为方便调试。所以这些异常处理也只是打印。
        /*要按先ResultSet结果集,后Statement,最后Connection的顺序关闭资源,
        因为ResultSet需要Statement和Connection连接时才可以用的;Statement也需要Connection才可用;
        结束Statement之后有可能其它的Statement还需要连接,因此不能先关闭Connection。ResultSet同理。*/
    步骤 1、2、6 每次都一样,可以重构。
       因为加载驱动是个一次性工作,所以可以采用静态初始化块来加载驱动;
       连接数据库的方法应该自己负责,获取数据库连接信息和驱动的信息,并处理相关异常;
       释放数据库资源的方法要考虑到ResultSet、Statement、Connection的不同情况,并处理相关异常。
       使用 preparedStatement 的例子:
/*************************************/
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class test{
     public static void main(String[] args) {
            String driverName = "sun.jdbc.odbc.JdbcOdbcDriver";
            String url = "jdbc:odbc:URIAGE";
            String username = "root";
            String password = "root";
            Connection con = null;
            PreparedStatement stmt = null;
        try{
            Class.forName(driverName);
            con = DriverManager.getConnection(url,username,password);
            String query = "insert into tableName(col1,col2,col3) values (?,?,?)";
            stmt = con.prepareStatement(query);
            //在数组中增加数据
            String col1[] = {"10","20","30"};
            String col2[] = {"01","02","03"};
            int col3[] = {100,200,300};
            //添加数据
            for(int i=0; i<col1.length; i++){
                stmt.setString(1, col1[i]);
                stmt.setString(2, col2[i]);
                stmt.setInt(3, col3[i]);
                stmt.executeUpdate();
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            if(stmt!=null)try{stmt.close();}catch(Exception e){e.printStackTrace();}
            if(con!=null)try{con.close();}catch(Exception e){e.printStackTrace();}
}}}
/*************************************/
三、JDBC几个重要接口重点讲解
    在JDBC 中包括了两个包:java.sql和javax.sql。
        ① java.sql   基本功能。
           这个包中的类和接口主要针对基本的数据库编程服务,如生成连接、执行语句以及准备语句和运行批处理查询等。
           同时也有一些高级的处理,比如批处理更新、事务隔离和可滚动结果集等。
        ② javax.sql  扩展功能。
           它主要为数据库方面的高级操作提供了接口和类。
           如为连接管理、分布式事务和旧有的连接提供了更好的抽象,它引入了容器管理的连接池、分布式事务和行集等。
        API                        说明
    Connection            与特定数据库的连接(会话)。能够通过getMetaData方法获得数据库提供的信息、
                          所支持的SQL语法、存储过程和此连接的功能等信息。代表了数据库。
    Driver                每个驱动程序类必需实现的接口,每个数据库驱动程序也都应该提供一个实现Driver接口的类。
    DriverManager(Class)  管理一组JDBC驱动程序的基本服务。作为初始化的一部分,此接口会尝试加载
                          在”jdbc.drivers”系统属性中引用的驱动程序。只是一个辅助类,是工具。
    Statement             用于执行静态SQL语句并返回其生成结果的对象。
    PreparedStatement     继承Statement接口,表示预编译的SQL语句的对象,SQL语句被预编译并且存储
                          在PreparedStatement对象中。然后可以使用此对象高效地多次执行该语句。
    CallableStatement     用来访问数据库中的存储过程。它提供了一些方法来指定语句所使用的输入/输出参数。
    ResultSet             指的是查询返回的数据库结果集。
    ResultSetMetaData     可用于获取关于ResultSet对象中列的类型和属性信息的对象。
    注:除了标出的Class,其它均为接口。每个都是“java.sql.”包下的。
    1. Statement  —— SQL语句执行接口
       代表了一个数据库的状态,在向数据库发送相应的SQL语句时,都需要创建Statement接口或PreparedStatement接口。
       在具体应用中,Statement主要用于操作不带参数(可以直接运行)的SQL语句,比如删除语句、添加或更新。
    2. PreparedStatement:预编译的Statement
        第一步:通过连接获得PreparedStatement对象,用带占位符(?)的sql语句构造。
            PreparedStatement  pstm = con.preparedStatement(“select * from test where id=?”);
        第二步:设置参数
            pstm.setString(1,“ganbin”);//第一个字段是“ganbin”;需一个个字段写
        第三步:执行sql语句
            Rs  =  pstm.excuteQuery();
        statement发送完整的Sql语句到数据库不是直接执行而是由数据库先编译,再运行。每次都需要编译。
        而PreparedStatement是先发送带参数的Sql语句,由数据库先编译,再发送一组组参数值。(同构时不需重复编译)
        如果是同构的sql语句,PreparedStatement的效率要比statement高。而对于异构的sql则两者效率差不多。
        一般都用PreparedStatement代替Statement,因为它是类型安全的。Statement对参数类型不作检查,故不够安全。
            同构:两个Sql语句可编译部分是相同的,只有参数值不同。
            异构:整个sql语句的格式是不同的
        注意点:1、使用预编译的Statement编译多条Sql语句一次执行
              2、可以跨数据库使用,编写通用程序
              3、能用预编译时尽量用预编译
              4、如果第二个SQL语句与前一个是异构的,需要再次编译“ps = con.prepareStatement(sql);“
    3. ResultSet —— 结果集操作接口
       ResultSet接口是查询结果集接口,它对返回的结果集进行处理。ResultSet是程序员进行JDBC操作的必需接口。
    4. ResultSetMetaData —— 元数据操作接口
       ResultSetMetaData是对元数据进行操作的接口,可以实现很多高级功能。
       Hibernate运行数据库的操作,大部分都是通过此接口。可以认为,此接口是SQL查询语言的一种反射机制。
       ResultSetMetaData接口可以通过数组的形式,遍历数据库的各个字段的属性,对于开发者来说,此机制的意义重大。
       JDBC通过元数据(MetaData)来获得具体的表的相关信息,例如,可以查询数据库中有哪些表,表有哪些字段,以及字段的
       属性等。MetaData中通过一系列getXXX将这些信息返回给我们。
       数据库元数据 Database MetaData 用connection.getMetaData()获得;包含了关于数据库整体元数据信息。
       结果集元数据 ResultSet MetaData 用resultSet.getMetaData()获得;比较重要的是获得表的列名,列数等信息。
                结果集元数据对象:ResultSetMetaData meta = rs.getMetaData();
                字段个数:meta.getColomnCount();
                字段名字:meta.getColumnName();
                字段JDBC类型:meta.getColumnType();
                字段数据库类型:meta.getColumnTypeName();
       数据库元数据对象:DatabaseMetaData dbmd = con.getMetaData();
                数据库名:dbmd.getDatabaseProductName();
                数据库版本号:dbmd.getDatabaseProductVersion();
                数据库驱动名:dbmd.getDriverName();
                数据库驱动版本号:dbmd.getDriverVersion();
                数据库Url:dbmd.getURL();
                该连接的登陆名:dbmd.getUserName();
四、JDBC 中使用Transaction编程(事务编程)
     1. 事务是具备以下特征(ACID)的工作单元:
        原子性(Atomicity)—— 如果因故障而中断,则所有结果均被撤消;
        一致性(Consistency)—— 事务的结果保留不变;
        孤立性(Isolation)—— 中间状态对其它事务是不可见的;
        持久性(Durability)—— 已完成的事务结果上持久的。
        原子操作,也就是不可分割的操作,必须一起成功一起失败。
     2. 事务处理三步曲:(事务是一个边界)
        ① connection.setAutoCommit(false); //把自动提交关闭;在创建Statement对象之前。
        ② 正常的DB操作                       //若有一条SQL语句失败了,自动回滚
        ③ connection.commit()              //主动提交
        和 connection.rollback()            //主动回滚,一般写在catch语句里,而前三个都写在try语句里
/*********事务的代码片段:*************/
try{
    con.setAutoCommit(false);   //step① 把自动提交关闭
    Statement stm = con.createStatement();    //step② 正常的DB操作
    stm.executeUpdate("insert into person(id, name, age) values(520, 'X-Man', 18)");
    stm.executeUpdate("insert into Person(id, name, age) values(521, 'Super', 19)");
    con.commit();               //step③ 成功主动提交
} catch(SQLException e){
    try{con.rollback();        //如果中途发生异常,则roolback;这语句也会抛异常
    }catch(Exception e){e.printStackTrace();}    //step③ 失败则主动回滚
}
/************************************/
     3.JDBC事务并发产生的问题和事务隔离级别(难,建议用例子学习)
     JDBC事务并发产生的问题:
        ① 脏读(Dirty Reads) 一个事务读取了另一个并行事务还未提交的数据。(产生原因:读-写)
        ② 不可重复读(UnPrpeatable Read)一个事务前后两次读取数据时,得到的数据不一致,被另一个已提交的事务修改。
        ③ 幻读(Phantom Read) 一个事务再次查询,记录中的量变化了。(仅对统计有影响)
     为了避免以上三种情况的出现,则采用事务隔离级别:
        TRANSACTION_NONE                不使用事务(不可能用,只是理论的)
        TRANSACTION_READ_UNCOMMITTED    可以读取未提交数据(允许脏读,也不可能)
        TRANSACTION_READ_COMMITTED      只读提交的数据:可防止脏读;大部分数据库的默认隔离级别
        TRANSACTION_REPEATABLE_READ     重复读取;只可以避免脏读
        TRANSACTION_SERIALIZABLE        事务串行化:可以避免脏读,重复读取和幻读,但会降低数据库效率(最常用)
     以上的五个事务隔离级别都是在Connection类中定义的静态常量。隔离级别越高,数据越安全,并发能力越差。
     使用setTransactionIsolation(int level) 方法可以设置事务隔离级别。
        比如:con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
五、JDBC 2.0新特性:
    1、 Scrollability 结果集可滚动
        滚动:可双向支持绝对与相对滚动,对结果集可进行多次迭代。
            Con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
            //上句的 SCROLL 再到 CONCUR;不可以写反,编译器无法检测到,因为他们都是int类型的。
        TYPE_FORWARD_ONLY:(单向,一般不用)该常量指示指针只能向前移动的 ResultSet 对象的类型。
        TYPE_SCROOL_INSENSITIVE:(双向、不敏感)可滚动但不受其他更改影响的 ResultSet 对象的类型。
        TYPE_SCROLL_SENSITIVE:(双向、敏感)该常量指示可滚动并且通常受其他的更改影响的 ResultSet 对象的类型。
        CONCUR_READ_ONLY:(只读)该常量指示只可以读取的 ResultSet 对象的并发模式。
        CONCUR_UPDATABLE:(可更新)该常量指示可以更新的 ResultSet 对象的并发模式。
        绝对定位:boolean absolute(int row)将游标移动到指定位置。(row指记录的序号,没有这位置则返回false)
                void afterLast() 将游标指向最后一条记录的后一位(有这位置,但记录为空)。
                void beforeFirst()将游标指向最前一条记录的前一位。
                boolean first()将游标移动到结果集最前。
                boolean last()将游标移动到结果集末尾。
        相对定位:boolean next()指向下一个。
                boolean previous()指向前一个。
                boolean relative(int) 向next()方向移动 int 位(int 可负)。
        判位函数:boolean isAfterLast() 是否在最后一条的后一位。
                boolean isBeforeFirst() 是否最前一条记录的前一位。
                boolean isFirst() 是否最前位置。
                boolean isLast() 是否最后位置。
    2、 Updatability 结果集可更新。(主要应用于桌面应用)
        更新:rs.updateString(“name”,”Tony”);//前面一个是字段的名字或者序号
        rs.updateInt(1,”122323”);修改
        rs.deleteRow();删除
        rs.updateRow();
     注:只有在必要的时候(如桌面应用)才用结果集更新数据库,因为使用结果集更新数据库效率低下。
        可更新结果集还要看数据库驱动程序是否支持,如Oracle就支持,MySql不支持。
        并且只能针对一张表做结果集更新(不能子查询)。而且不能有join操作。
        必须有主健,必须把所有非空没有默认值的字段查出。
        处理可更新结果集时不能用select *来查询语句,必须指出具体要查询的字段。(不能使用通配符)
    3、 Batch updates 可批量更新。
        将多组对数据库的更新操作发送到数据库统一执行(数据库支持并发执行操作),以提高效率。
        主要是通过减少数据(Sql语句或参数)在网络上传输的次数来节省时间。//数据有两组以上都应该用这批量更新
        (1)对于Statement的批量更新处理:
            stm.addBatch(Sql);
            int[] ResultSet=stm.executeBatch();
        (2)对于PreparedStatement的批量更新处理
             pstm.setInt(1,12);pstm.setString(2,”gaga”);……..
             pstm.addBatch();
             if(i%100==0) int[] ResultSet=pstm.executeBatch();//每个包50~200组数据,包太大也影响速度
        注:int[] 中每一个数表示该Sql语句影响到的记录条数。
        PreparedStatement的更新操作比Statement的更新操作多了一个设置参数的过程。
六、SQL 3.0规范中的新类型:
        Blob,大的二进制数据文件,最多存储2G。
        Clob,大文本文件对象,最多存储2G。
    在使用上述大对象的时候,在使用JDBC插入记录时要先插入一个空的占位对象,
        "insert into tableName valuse(?,?,empty_blob())"//在数据库制造一个空的blob对象字段值
        然后使用"select blobdata from t_blob where id = ? for update"对获得的大对象进行实际的写入操作
        Blod通过getBinaryOutputStream()方法获取流进行写入。
        getBinaryStream()方法获得流来获取Blob中存储的数据。
    Clob的操作也和、Blob相同。
        getAsciiStream()用于读取存储的文本对象,getAsciiOutputStream()方法之获得流用来向文件对象写入的。
    BLOB与CLOB的异同点:
        ① 都可以存储大量超长的数据;
        ② BLOB (Binary Large Object) 以二进制格式保存,特别适合保存图片、视频文件、音频文件、程序文件等;
        ③ CLOB (Character Large Object) 以Character格式保存于数据库中,适合保存比较长的文本文件。
七、JDBC 2.0扩展
    (一)JNDI(命名目录服务器):
        定义:是Java的命名目录服务器。而JDBC是Java的数据库访问接口。
            跟JDBC是平级的关系,是两个独立的JNDI;JDBC存储的数据都是以二维表的接口来大规模存储数据。
            而JNDI存储的是差异性比较大的Java对象。JDBC取数据时用Sql语言访问数据。JNDI只用lookup和bind读写
            JDBC API依赖于驱动程序,而JNDI依赖于服务提供者。
            JDBC一般把数据存储到关系型数据库,而JNDI一般把数据存储到小型数据库、文件、甚至是注册表中。
            JNDI相当于一个电话本。允许程序将一个对象和一个命名绑定到目录树上。
               (JNDI的方法是在javax.naming包下,接口是Context实现类是InitialContext)
        bind(String name, Object obj) 将名称绑定到对象资源,建立指定的字符串和对象资源的关联
        lookup(String name) ,通过指定的字符串获得先前绑定的资源
        /*********以下是将资源和JNDI命名绑定的方法**************/
        public static void bind(String context, Object obj) throws NamingException{
            Properties pro = new Properties();
            //Weblogic的JNDI服务器参数
        pro.put(InitialContext.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
            pro.put(InitialContext.PROVIDER_URL, "t3://localhost:7001");
            Context ctx = new InitialContext(pro);//连接服务器
            ctx.bind(context, obj);//存储
        }
    (二)DataSourse(数据源)
        1、包含了连接数据库所需的信息,可以通过数据源获得数据库连接,
           有时由于某些连接数据库的信息会变更,所以经常使用包含数据库连接信息的数据源。
        2、一个标准的数据库连接工厂,作为DriverManager的替代项,保存与数据库相关的信息,
           可以将数据库的连接信息放在一个共享的空间进行提取,不用在本地安装。
           支持JNDI的绑定,支持连接池,支持分布式服务,用getConnection方法可获得与数据库的连接。
           数据源应该由管理员创建(目的是为了保证数据库的安全)。所以数据源对象一般放在JNDI服务器中。
        /*********通过JNDI获得绑定的资源**************/
        public static Object lookup(String context) throws NamingException{
            Properties pro = new Properties();
            //Weblogic的JNDI服务器参数
        pro.put(InitialContext.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
            pro.put(InitialContext.PROVIDER_URL, "t3://localhost:7001");
            Context ctx = new InitialContext(pro);
            return ctx.lookup(context);//查找;通过指定的字符串获得先前绑定的资源。
        }
    (三)连接池:
        在内存中用来保存一个个数据库连接的对象。
        访问数据库时,建立连接和拆连接需要花费较长时间,通过以连接池直连的方式获取连接,不需要注册驱动程序,可以大量
        的节省销毁和创建连接的资源消耗,提高访问数据库的效率。
     注:通过连接池获得的Connection,当执行con.close()时,不是关闭连接,而是表示将连接释放回连接池。
         连接池是一个很复杂的算法,由服务器厂商实现。
    (四)分布式的事务管理器JTA
        分布式事务是通过多个异地数据库执行一组相关的操作,要保证原子操作的不可分,
        也不用再自己写commit,和rollback,全部都交给中间服务器(TM)来处理。
        (两阶段提交),也就是在中间服务器发送sql语句等待数据库回应,都回应操作成功才提交,否则同时回滚。
                         ----------------   commit
            con1 ------->| TM(事务管理器) | -----------> DB1
            con2 ------->|    commit    | -----------> DB2
                         ----------------   commit
        1、regester
        2、TM->execute()
        3、commit->TM
        4、TM->commit->DB
    (五)RowSet
                行集,这是一个JavaBean(事件机制),它增强了ResultSet的功能,包装了Connection、Statement、
        ResultSet、DriverManage。通过RowSet可以获得数据源,设置隔离级别,也可以发送查寻语句,也实现了离线的操
        作遍历,RowSet也支持预编译的Statement。是结果集的子接口,为快速开发而设(目前还不够成熟,没人用)。
        RowSet中的方法大致上和ResultSet相同,当需要使用时请查阅JAVA API参考文档。
八、JDBC应用的分层(DAO)
    分层就是对功能的隔离,降低层与层之间的耦合性。
    软件的分层初步:
        JSP          Struts
      View(界面) --> Controlle --> Atio ---> Service/Biz --> DAO ---->  DB
       重新封装        可复用       封装信息      懂业务逻辑    数据访问层    数据层
                                  调业务       无技术难度    与业务无关
    谁依赖谁就看谁调用谁。
    软件的分层设计,便于任务的划分、降低层间的耦合。
    结合PMS的设计方法,思考这样分层的好处。
    并且,使代码尽量减少重复,可复用性好,扩展余地加大,而且尽量减少硬编码。
    需求:实现对Person类的数据库持久化基本操作(CRUD)。
    BS架构和CS架构:
    C-S架构:两层体系结构,主要应用于局域网中。
    B-S架构:三层体系结构,表现层+业务逻辑层+数据存储层
         注:层面越多,软件越复杂,但更灵活。分层是必须的但是要有个度。
            层次一但确定,数据必须按层访问,不能跨层访问。
            层与层之间最好时单向依赖(单向调用)。
    纵向划分:按功能划分。分成三层体系结构(也有两层的)。
    横向划分:按抽象划分。分成抽象部分和实现部分。
============================================
一、JDBC异常处理:
   JDBC中,和异常相关的两个类是SQLException和SQLWarning。
      1.SQLException类:用来处理较为严重的异常情况。
        比如:① 传输的SQL语句语法的错误;
             ② JDBC程序连接断开;
             ③ SQL语句中使用了错误的函数。
        SQLException提供以下方法:
          getNextException() —— 用来返回异常栈中的下一个相关异常;
          getErrorCode() —— 用来返回代表异常的整数代码 (error code);
          getMessage() —— 用来返回异常的描述信息 (error message)。
      2.SQLWarning类:用来处理不太严重的异常情况,也就是一些警告性的异常。
        其提供的方法和使用与SQLException基本相似。
    结合异常的两种处理方式,明确何时采用哪种。
      A. throws        处理不了,或者要让调用者知道;
      B. try … catch   能自行处理,就进行异常处理。
二、JavaBean的定义:
    1、是一个普通的Java类
    2、在结构上没有预先的规定,不需要容器,不需要继承类或实现接口
    3、要求必须放在包中,要求实现Serializable接口
    4、要求有一个无参的构造方法.
    5、属性的类型必须保持唯一,get方法返回值必须和set方法参数类型一致
    6、对每个属性要有对应的get和set方法。注:隐藏属性可以没有
    7、可以有外观作为显示控制,事件机制。
三、SQL数据类型及其相应的Java数据类型
   SQL数据类型 ? ? ? ? ? ? Java数据类型 ? ? ? ? ? ? ?说明
??---------------------------------------------------------------------------------------
? ?INTEGER或者INT ? ? ? ? ?  int ? ? ? ? ? ? ? 通常是个32位整数
? ?SMALLINT ? ? ? ? ? ? ?   short ? ? ? ? ? ? 通常是个16位整数
? ?NUMBER(m,n) ? ? ?      Java.sql.Numeric ? ?合计位数是m的定点十进制数,小数后面有n位数
   DECIMAL(m,n)              同上
? ?DEC(m,n) ? ? ? ? ? ? ? Java.sql.Numeric ? ?合计位数是m的定点十进制数,小数后面有n位数
? ?FLOAT(n) ? ? ? ? ? ? ?    double ? ? ? ? ? 运算精度为n位二进制数的浮点数
? ?REAL ? ? ? ? ? ? ? ?    ? float ? ? ? ? ? ?通常是32位浮点数
? ?DOUBLE ? ? ? ? ? ? ?    ? double ? ? ? ? ? 通常是64位浮点数
?  CHAR(n) ? ? ? ? ?         String ? ? ? ? ? 长度为n的固定长度字符串
   CHARACTER(n)              同上
? ?VARCHAR(n) ? ? ? ? ?     ?String ? ? ? ? ? 最大长度为n的可变长度字符串
? ?BOOLEAN ? ? ? ? ? ? ?    ?boolean ? ? ? ? ?布尔值
? ?DATE ? ? ? ? ? ? ? ? ? Java.sql.Date ?  ?  根据具体设备而实现的日历日期
? ?TIME ? ? ? ? ? ? ? ? ? Java.sql.Time ? ? ? 根据具体设备而实现的时戳
? ?TIMESTAMP ? ? ? ? ? ?? Java.sql.Timestamp ?根据具体设备而实现的当日日期和时间
? ?BLOB ? ? ? ? ? ? ? ?   Java.sql.Blob ? ??  二进制大型对象
? ?CLOB ? ? ? ? ? ? ? ?  ?Java.sql.Clob ? ??  字符大型对象
? ?ARRAY ? ? ? ? ? ? ? ?? Java.sql.Array
四、 面向对象的数据库设计
    类的关联,继承在数据库中的体现:
      类定义―――>表定义
      类属性―――>表字段
      类关系―――>表关系
      对  象―――>表记录
    注: Oid(对象id)―――>业务无关
        在数据库中每一条记录都对应一个唯一的id;
        Id通常是用来表示记录的唯一性的,通常会使用业务无关的数字类型
        字段的个数不会影响数据库的性能,表则越多性能越低。
    (一)类继承关系对应表,
        1、 为每一个类建一张表。通过父类的Oid来体现继承关系。
            特点:在子类表中引用父类表的主建作为自己的外建。
            优点:方便查询。属性没有冗余。支持多态。
            缺点:表多,读写效率低。生成报表比较麻烦。
        2、 为每一个具体实现类建一个表
            特点:父类的属性被分配到每一个子类表中。
            优点:报表比较容易
            缺点:如果父类发生改变会引起所有子类表随之更改。并且不支持多态。数据有少量冗余。
        3、 所有的类在一张表中体现,加一个类型辨别字段
            特点:效率高,查询不方便,用于字段不多时。
            优点:支持多态,生成报表很简单。
            缺点:如果任何一个类发生变化,必须改表。字段多,难以维护。
    (二)类关联关系对应表
        1、 一对一关联,类关系对应成表时有两种做法:
            一是引用主键,也就是一方引用另一方的主键既作为外键有作为自身的主键。
            二是外键引用,一方引用另一方的主键作为自身的外键,并且自己拥有主键。
        2、 一对多关联,也就是多端引用一端的主键当作外键,多端自身拥有主键。
        3、 多对多关系,多对多关系是通过中间表来实现的,中间表引用两表的主键当作联合主键,就可以实现多对多关联。
l
// 常用的 JDBC 工具方法
// ******************************* DbUtil.java *************************************
/**
 * <P> Title: 公用類別                                      </P>
 * <P> Description: 資料庫操作工具                          </P>
 * <P> Copyright: Copyright (c) 2010/07/31                  </P>
 * <P> Company:Everunion Tech. Ltd.                         </P>
 */
package com.everunion.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ResourceBundle;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
/**
 * 資料庫操作工具
 * @author Holer W. L. Feng
 * @version 0.1
 */
public final class DbUtil
{
    /**
     * Connection Pool
     */
    private static DataSource dataSource = null;
    
    
    /**
     * 取得 Connection Pool
     * @throws Exception 例外
     */
    private synchronized static void setupDataSource() throws Exception
    {
        try
        {
            if ( dataSource == null )
            {
                // 讀取配置檔(位於: 項目/WEB-INF/classes/database.properties)
                ResourceBundle actionDB = ResourceBundle.getBundle("database");
                // 取得對應的屬性值
                String driverName = actionDB.getString("DriverName");
                String DBUrl = actionDB.getString("DBUrl");
                String Username = actionDB.getString("Username");
                String Password = actionDB.getString("Password");
                
                // 最大等待時間
                String MaxWaitStr = actionDB.getString("MaxWait");
                if ( MaxWaitStr == null || "".equals(MaxWaitStr) )
                    MaxWaitStr = "3000";
                long MaxWait = (new Long(MaxWaitStr)).longValue();
                
                String MaxIdleStr = actionDB.getString("MaxIdle");
                if ( MaxIdleStr == null || "".equals(MaxIdleStr) )
                    MaxIdleStr = "10";
                int MaxIdle = (new Integer(MaxIdleStr)).intValue();
                
                String MaxActiveStr = actionDB.getString("MaxActive");
                if ( MaxActiveStr == null || "".equals(MaxActiveStr) )
                    MaxActiveStr = "100";
                int MaxActive = (new Integer(MaxActiveStr)).intValue();
                // 建立 Connection Pool
                BasicDataSource bds = new BasicDataSource();
                bds.setDriverClassName(driverName);
                bds.setUrl(DBUrl);
                bds.setUsername(Username);
                bds.setPassword(Password);
                bds.setMaxWait(MaxWait);
                bds.setMaxIdle(MaxIdle);
                bds.setMinIdle(2);
                bds.setMaxActive(MaxActive);
                dataSource = bds;
            }
        }
        // 拋出例外
        catch ( Exception e )
        {
            dataSource = null;
            System.out.println("DbUtil.setupDataSource:" + e.toString());
            throw e;
        }
    }
    
    /**
     * 取得 資料庫連結
     * @return 資料庫連結
     * @throws Exception 資料庫開啟連結例外
     */
    public static Connection getConn() throws Exception
    {
        Connection conn = null;
        try
        {
            if ( dataSource == null )
            {
                //取得Connection Pool
                setupDataSource();
            }
            //取得資料庫連結
            conn = dataSource.getConnection();
        }
        //拋出例外
        catch ( Exception e )
        {
            System.out.println("DbUtil.getConn:" + e.toString());
            throw e;
        }
        return conn;
    }
    
    
    /**
     * 取得資料庫連結
     * @param driverName 資料庫驅動名
     * @param url 資料庫連結地址
     * @param username 資料庫登錄名
     * @param password 資料庫登錄密碼
     * @return Connection 資料庫連結
     * @throws Exception 資料庫開啟連結例外
     */ 
    public static Connection getConn(String driverName, String url, String username,
            String password) throws Exception
    {
        Connection conn = null; 
        //取得連結
        try
        {
            Class.forName(driverName);
            conn = DriverManager.getConnection(url, username, password);
        }
        // 拋出例外
        catch ( Exception e )
        {
            System.out.println("DbUtil.getConn:" + e.toString());
            throw e;
        }
        return conn;
    }
    
    /**
     * 還原處理
     * @param conn
     */
    public static void rollback(Connection conn)
    {
        try
        {
            if ( conn != null )
            {
                conn.rollback();
            }
        }
        catch ( Exception e )
        {
            System.out.println("DbUtil.rollback:" + e.toString());
        }
    }
    
    /**
     * 關閉資料庫連結
     * @param conn 資料庫連結
     */
    public static void close(Connection conn)
    {
        try
        {
            if ( conn != null )
            {
                // conn.commit();
                conn.close();
                conn = null;
            }
        }
        catch ( Exception e )
        {
            System.out.println("DbUtil.close:" + e.toString());
        }
    }
    
    /**
     * 關閉資料庫操作連結
     * @param statement 資料庫操作連結
     */
    public static void close(Statement statement)
    {
        try
        {
            if ( statement != null )
            {
                statement.close();
                statement = null;
            }
        }
        catch ( Exception e )
        {
            System.out.println("DbUtil.close:" + e.toString());
        }
    }
    
    /**
     * 關閉結果集連結
     * @param resultSet 結果集連結
     */
    public static void close(ResultSet resultSet)
    {
        try
        {
            if ( resultSet != null )
            {
                resultSet.close();
                resultSet = null;
            }
        }
        catch ( Exception e )
        {
            System.out.println("DbUtil.close:" + e.toString());
        }
    }
    
    /**
     * 關閉連結
     * @param conn 資料庫連結
     * @param stmt 資料庫操作連結
     * @param resultSet 結果集連結
     */
    public static void close(Connection conn, Statement stmt, ResultSet resultSet)
    {
        close(resultSet);
        close(stmt);
        close(conn);
    }
    
    /**
     * 取得資料
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @param valueList 查詢的SQL的參數資料集
     * @return ArrayList 資料集,查不到資料時傳回size為0的ArrayList
     * @throws SQLException 資料庫操作例外
     */ 
    public static ArrayList<HashMap<String,String>> getData( Connection conn,
            String sql, List<Object> valueList ) throws SQLException
    {
        ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
        //資料庫資源
        ResultSet rs = null;
        PreparedStatement pstmt = null;
        try
        {
            //操作資料庫
            pstmt = conn.prepareStatement(sql);
            //如果需要傳參數
            putSQLParams(pstmt, valueList);
            rs = pstmt.executeQuery();
            ResultSetMetaData rsMetaData = rs.getMetaData();
            int col = rsMetaData.getColumnCount();
            //加入資料
            while ( rs.next() )
            {
                HashMap<String,String> map = new LinkedHashMap<String,String>();
                for ( int i = 1; i <= col; i ++ )
                {
                    map.put(rsMetaData.getColumnLabel(i).toLowerCase(), rs.getString(i));
                }
                list.add(map);
            }
        }
        catch ( SQLException e )
        {
            //輸出例外
            System.out.println("DbUtil.getData: " + e.toString());
            System.out.println("DbUtil.getData.sql: " + sql);
            throw e;
        }
        //關閉連結
        finally
        {
            close( rs );
            close( pstmt );
        }
        return list;
    }
    
    /**
     * 取得資料
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @return ArrayList 資料集,查不到資料時傳回size為0的ArrayList
     * @throws SQLException 資料庫操作例外
     */
    public static ArrayList<HashMap<String,String>> getData( Connection conn,
            String sql ) throws SQLException
    {
        return getData( conn, sql, null );
    }
    
    /**
     * 取得資料
     * 建議當僅有一次資料庫操作時使用,需多次操作的建議建立 Connection
     * @param sql 查詢的SQL
     * @param valueList 查詢的SQL的參數資料集
     * @return ArrayList 資料集,查不到資料時傳回size為0的ArrayList; 發生資料例外時傳回 null
     */
    public static ArrayList<HashMap<String,String>> getData( String sql, List<Object> valueList )
    {
        Connection conn = null;
        try
        {
            conn = getConn();
            return getData(conn, sql, valueList);
        }
        //發生資料例外時傳回 null
        catch ( Exception e )
        {
            return null;
        }
        finally
        {
            close(conn);
        }
    }
    
    /**
     * 取得資料
     * 建議當僅有一次資料庫操作時使用,需多次操作的建議建立 Connection
     * @param sql 查詢的SQL
     * @return ArrayList 資料集,查不到資料時傳回size為0的ArrayList; 發生資料例外時傳回 null
     */
    public static ArrayList<HashMap<String,String>> getData( String sql )
    {
        return getData(sql, null);
    }
    
    
    /**
     * 取得資料,number 從1開始
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @param valueList 查詢的SQL的參數資料集
     * @return ArrayList 資料集,查不到資料時傳回size為0的ArrayList
     * @throws SQLException 資料庫操作例外
     */
    public static ArrayList<HashMap<String,String>> getDataByNum( Connection conn,
            String sql, List<Object> valueList ) throws SQLException
    {
        ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
        //資料庫資源
        ResultSet rs = null;
        PreparedStatement pstmt = null;
        try
        {
            //操作資料庫
            pstmt = conn.prepareStatement(sql);
            //如果需要傳參數
            putSQLParams(pstmt, valueList);
            rs = pstmt.executeQuery();
            ResultSetMetaData rsMetaData = rs.getMetaData();
            int col = rsMetaData.getColumnCount();
            //加入資料
            while ( rs.next() )
            {
                HashMap<String,String> map = new LinkedHashMap<String,String>();
                for ( int i = 1; i <= col; i ++ )
                {
                    map.put("" + i, rs.getString(i));
                }
                list.add(map);
            }
        }
        catch ( SQLException e )
        {
            //輸出例外
            System.out.println("DbUtil.getDataByNum: " + e.toString());
            System.out.println("DbUtil.getDataByNum.sql: " + sql);
            throw e;
        }
        //關閉連結
        finally
        {
            close( rs );
            close( pstmt );
        }
        return list;
    }
    
    /**
     * 取得資料,number 從1開始
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @return ArrayList 資料集,查不到資料時傳回size為0的ArrayList
     * @throws SQLException 資料庫操作例外
     */
    public static ArrayList<HashMap<String,String>> getDataByNum( Connection conn, String sql )
        throws SQLException
    {
        return getDataByNum( conn, sql, null );
    }
    
    
    /**
     * 取得資料,number 從1開始
     * 建議當僅有一次資料庫操作時使用,需多次操作的建議建立 Connection
     * @param sql 查詢的SQL
     * @param valueList 查詢的SQL的參數資料集
     * @return ArrayList 資料集,查不到資料時傳回size為0的ArrayList; 發生資料例外時傳回 null
     */
    public static ArrayList<HashMap<String,String>> getDataByNum( String sql, List<Object> valueList )
    {
        Connection conn = null;
        try
        {
            conn = getConn();
            return getDataByNum(conn, sql, valueList);
        }
        //發生資料例外時傳回 null
        catch ( Exception e )
        {
            return null;
        }
        finally
        {
            close(conn);
        }
    }
    
    
    /**
     * 取得資料,number 從1開始
     * 建議當僅有一次資料庫操作時使用,需多次操作的建議建立 Connection
     * @param sql 查詢的SQL
     * @return ArrayList 資料集,查不到資料時傳回size為0的ArrayList; 發生資料例外時傳回 null
     */
    public static ArrayList<HashMap<String,String>> getDataByNum( String sql )
    {
        return getDataByNum( sql, null);
    }
    
    
    /**
     * 取得一筆資料
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @param valueList 查詢的SQL的參數資料集
     * @return HashMap 資料集,查不到資料時傳回size為0的HashMap
     * @throws SQLException 資料庫操作例外
     */
    public static HashMap<String,String> getOneData( Connection conn,
            String sql, List<Object> valueList ) throws SQLException
    {
        //資料庫資源
        ResultSet rs = null;
        PreparedStatement pstmt = null;
        HashMap<String,String> map = new LinkedHashMap<String,String>();
        try
        {
            //操作資料庫
            pstmt = conn.prepareStatement(sql);
            //如果需要傳參數
            putSQLParams(pstmt, valueList);
            rs = pstmt.executeQuery();
            ResultSetMetaData rsMetaData = rs.getMetaData();
            int col = rsMetaData.getColumnCount();
            //加入資料
            if ( rs.next() )
            {
                for ( int i = 1; i <= col; i++ )
                {
                    map.put(rsMetaData.getColumnLabel(i).toLowerCase(), rs.getString(i));
                }
            }
        }
        catch ( SQLException e )
        {
            //輸出例外
            System.out.println("DbUtil.getOneData: " + e.toString());
            System.out.println("DbUtil.getOneData.sql: " + sql);
            throw e;
        }
        //關閉連結
        finally
        {
            close( rs );
            close( pstmt );
        }
        return map;
    }
    
    
    /**
     * 取得一筆資料
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @return HashMap 資料集,查不到資料時傳回size為0的HashMap; 發生資料例外時傳回 null
     * @throws SQLException 資料庫操作例外
     */
    public static HashMap<String,String> getOneData( Connection conn, String sql )
        throws SQLException
    {
        return getOneData( conn, sql, null );
    }
    
    
    /**
     * 取得一筆資料
     * 建議當僅有一次資料庫操作時使用,需多次操作的建議建立 Connection
     * @param sql 查詢的SQL
     * @param valueList 查詢的SQL的參數資料集
     * @return HashMap 資料集,查不到資料時傳回size為0的HashMap; 發生資料例外時傳回 null
     */
    public static HashMap<String,String> getOneData( String sql, List<Object> valueList )
    {
        Connection conn = null;
        try
        {
            conn = getConn();
            return getOneData(conn, sql, valueList);
        }
        //發生資料例外時傳回 null
        catch ( Exception e )
        {
            return null;
        }
        finally
        {
            close(conn);
        }
    }
    
    
    /**
     * 取得一筆資料
     * 建議當僅有一次資料庫操作時使用,需多次操作的建議建立 Connection
     * @param sql 查詢的SQL
     * @return HashMap 資料集,查不到資料時傳回size為0的HashMap; 發生資料例外時傳回 null
     */
    public static HashMap<String,String> getOneData( String sql )
    {
        return getOneData( sql, null);
    }
    
    
    /**
     * 查詢一個欄位
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @param valueList 查詢的SQL的參數資料集
     * @return String 結果;查無資料,以及資料為 null 時,傳回 null
     * @throws SQLException 資料庫操作例外
     */
    public static String queryOne(Connection conn, String sql, List<Object> valueList )
        throws SQLException
    {
        //資料庫資源
        ResultSet rs = null;
        PreparedStatement pstmt = null;
        try
        {
            //操作資料庫
            pstmt = conn.prepareStatement(sql);
            //如果需要傳參數
            putSQLParams(pstmt, valueList);
            rs = pstmt.executeQuery();
            // 只查詢一個欄位
            if ( rs.next() )
                return rs.getString(1);
        }
        catch ( SQLException e )
        {
            //輸出例外
            System.out.println("DbUtil.queryOne: " + e.toString());
            System.out.println("DbUtil.queryOne.sql: " + sql);
            throw e;
        }
        //關閉連結
        finally
        {
            close( rs );
            close( pstmt );
        }
        // 查不到內容時,傳回null
        return null;
    }
    
    
    /**
     * 查詢一個欄位
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @return String 結果; 查無資料,以及資料為 null 時,傳回 null
     * @throws SQLException 資料庫操作例外
     */
    public static String queryOne(Connection conn, String sql) throws SQLException
    {
        return queryOne(conn, sql, null);
    }
    
    
    /**
     * 查詢一個欄位
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @param valueList 查詢的SQL的參數資料集
     * @return String 結果;查無資料,以及資料為 null 時,傳回 null
     * @throws NullPointerException 資料庫操作例外,可以不 catch
     */
    public static String queryOne( String sql, List<Object> valueList )
    {
        Connection conn = null;
        try
        {
            conn = getConn();
            return queryOne(conn, sql, valueList);
        }
        //發生資料例外時,拋出例外,但可以不捕獲
        catch ( Exception e )
        {
            throw new RuntimeException("DbUtil.queryOne: 資料庫操作例外!");
        }
        finally
        {
            close(conn);
        }
    }
    
    
    /**
     * 查詢一個欄位
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @return String 結果;查無資料,以及資料為 null 時,傳回 null
     * @throws NullPointerException 資料庫操作例外,可以不 catch
     */
    public static String queryOne(String sql)
    {
        return queryOne( sql, null );
    }
    
    I Can 
    /**
     * 取得資料, 要求每筆資料只有兩個欄位
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @param valueList 查詢的SQL的參數資料集
     * @return HashMap 資料集,查不到資料時傳回size為0的HashMap
     * @throws SQLException 資料庫操作例外
     */
    public static HashMap<String,String> getMap ( Connection conn,
            String sql, List<Object> valueList ) throws SQLException
    {
        //資料庫資源
        ResultSet rs = null;
        PreparedStatement pstmt = null;
        HashMap<String,String> map = new LinkedHashMap<String,String>();
        try
        {
            //操作資料庫
            pstmt = conn.prepareStatement(sql);
            //如果需要傳參數
            putSQLParams(pstmt, valueList);
            rs = pstmt.executeQuery();
            //加入資料
            while ( rs.next() )
            {
                map.put(rs.getString(1), rs.getString(2));
            }
        }
        catch ( SQLException e )
        {
            //輸出例外
            System.out.println("DbUtil.getMap: " + e.toString());
            System.out.println("DbUtil.getMap.sql: " + sql);
            throw e;
        }
        //關閉連結
        finally
        {
            close( rs );
            close( pstmt );
        }
        return map;
    }
    
    
    /**
     * 取得資料, 要求每筆資料只有兩個欄位
     * @param conn 資料庫連結
     * @param sql 查詢的SQL
     * @return HashMap 資料集,查不到資料時傳回size為0的HashMap
     * @throws SQLException 資料庫操作例外
     */
    public static HashMap<String,String> getMap( Connection conn, String sql )
        throws SQLException
    {
        return getMap( conn, sql, null );
    }
    
    
    /**
     * 取得資料, 要求每筆資料只有兩個欄位
     * 建議當僅有一次資料庫操作時使用,需多次操作的建議建立 Connection
     * @param sql 查詢的SQL
     * @param valueList 查詢的SQL的參數資料集
     * @return HashMap 資料集,查不到資料時傳回size為0的HashMap; 發生資料例外時傳回 null
     */
    public static HashMap<String,String> getMap( String sql, List<Object> valueList )
    {
        Connection conn = null;
        try
        {
            conn = getConn();
            return getMap(conn, sql, valueList);
        }
        //發生資料例外時傳回 null
        catch ( Exception e )
        {
            return null;
        }
        finally
        {
            close(conn);
        }
    }
    
    
    /**
     * 取得資料, 要求每筆資料只有兩個欄位
     * 建議當僅有一次資料庫操作時使用,需多次操作的建議建立 Connection
     * @param sql 查詢的SQL
     * @return HashMap 資料集,查不到資料時傳回size為0的HashMap; 發生資料庫例外時傳回 null
     */
    public static HashMap<String,String> getMap( String sql )
    {
        return getMap( sql, null );
    }
    
    
    /**
     * 執行資料庫操作SQL,如 INSERT、UPDATE 或 DELETE 等
     * @param conn 資料庫連結
     * @param sql 操作資料庫的SQL
     * @param valueList 操作資料庫的SQL的參數資料集
     * @return int 新增、修改或刪除 等所影響的筆數(如果是新增,且主鍵是自動增長的,傳回主鍵)
     * @throws SQLException 資料庫操作例外
     */
    public static int execute( Connection conn, String sql, List<Object> valueList )
        throws SQLException
    {
        //資料庫資源
        PreparedStatement pstmt = null;
        try
        {
            //操作資料庫
            pstmt = conn.prepareStatement(sql);
            //如果需要傳參數
            putSQLParams(pstmt, valueList);
            int affect = pstmt.executeUpdate();
            //新增時傳回主鍵
            if ( affect >= 1 )
            {
                sql = sql.trim().toLowerCase();
                //確定是新增
                if ( sql.startsWith("insert ") )
                {
                    //檢索由於執行此 Statement 物件而建立的所有自動生成的鍵
                    ResultSet rs = pstmt.getGeneratedKeys();
                    if ( rs.next() )
                    {
                        //僅有一列,故取得第一列
                        Long oid = rs.getLong(1);
                        if ( oid > 0 )
                        {
                            return NumberUtil.toInt(oid, affect);
                        }
                    }
                }
            }
            return affect;
        }
        catch ( SQLException e )
        {
            //輸出例外
            System.out.println("DbUtil.execute: " + e.toString());
            System.out.println("DbUtil.execute.sql: " + sql);
            throw e;
        }
        //關閉連結
        finally
        {
            close( pstmt );
        }
    }
    
    /**
     * 執行無傳回值的 SQL,如 INSERT、UPDATE 或 DELETE 等
     * @param conn 資料庫連結
     * @param sql 操作資料庫的SQL
     * @return int 新增、修改或刪除 等所影響的筆數
     * @throws SQLException 資料庫操作例外
     */
    public static int execute( Connection conn, String sql ) throws SQLException
    {
        return execute( conn, sql, null );
    }
    
    
    /**
     * 執行資料庫操作SQL,如 INSERT、UPDATE 或 DELETE 等
     * 建議當僅有一次資料庫操作時使用,需多次操作的建議建立 Connection
     * @param sql 操作資料庫的SQL
     * @param valueList 操作資料庫的SQL的參數資料集
     * @return int 新增、修改或刪除 等所影響的筆數; 發生資料庫例外時傳回-1
     */
    public static int execute( String sql, List<Object> valueList )
    {
        Connection conn = null;
        try
        {
            conn = getConn();
            return execute(conn, sql, valueList);
        }
        //發生資料庫例外時傳回 -1
        catch ( Exception e )
        {
            return -1;
        }
        finally
        {
            close(conn);
        }
    }
    
    
    /**
     * 執行資料庫操作SQL,如 INSERT、UPDATE 或 DELETE 等
     * 建議當僅有一次資料庫操作時使用,需多次操作的建議建立 Connection
     * @param sql 操作資料庫的SQL
     * @return int 新增、修改或刪除 等所影響的筆數; 發生資料庫例外時傳回-1
     */
    public static int execute( String sql )
    {
        return execute( sql, null );
    }
    
    
    /**
     * 驗證欄位的某值是否已經存在(通常用來驗證主鍵是否重複)
     * @param conn 資料庫連結
     * @param tableName 資料庫操作的表名
     * @param key 欄位名
     * @param value 欄位的值
     * @return boolean 驗證結果:欄位此值已經存在時傳回true,不存在時傳回false
     * @throws SQLException 資料庫操作例外
     */
    public static boolean isHave ( Connection conn, String tableName, 
            String key, String value ) throws SQLException
    {
        // 防呆
        if ( tableName != null && key != null )
        {
            //驗證  主鍵 是否已經存在
            List<Object> valueList = new ArrayList<Object>();
            valueList.add(value);
            String sql = "select " + key + " from " + tableName + " where " + key + "= ? ";
            //如果欄位此值已經存在,傳回true
            if ( getOneData(conn, sql, valueList).size() > 0 )
                return true;
        }
        return false;
    }
    
    
    /**
     * 驗證欄位的某值是否已經存在(通常用來驗證主鍵是否重複)
     * 建議當僅有一次資料庫操作時使用,需多次操作的建議建立 Connection
     * @param tableName 資料庫操作的表名
     * @param key 欄位名
     * @param value 欄位的值
     * @return boolean 驗證結果:欄位此值已經存在時傳回true,不存在時傳回false
     * @throws NullPointerException 資料庫操作例外,可以不 catch
     */
    public static boolean isHave( String tableName, String key, String value )
    {
        Connection conn = null;
        try
        {
            conn = getConn();
            return isHave( conn, tableName, key, value );
        }
        //發生資料庫例外時,拋出,可以不捕獲
        catch ( Exception e )
        {
            throw new NullPointerException();
        }
        finally
        {
            close(conn);
        }
    }
    
    
    /**
     * 傳遞參數到 PreparedStatement, 僅供本類內部使用
     * (註:此方法有待改善,目前僅字串且不為空情況下測試過)
     * @param pstmt PreparedStatement
     * @param valueList 參數列表
     * @return PreparedStatement連結
     * @throws SQLException 資料庫操作例外
     */
    private static PreparedStatement putSQLParams ( PreparedStatement pstmt,
            List<Object> valueList ) throws SQLException
    {
        try
        {
            //查詢的參數資料集的筆數
            int valueListSize = (valueList == null) ? 0 : valueList.size();
            for ( int i = 0; i < valueListSize; i++ )
            {
                if ( valueList.get(i) == null )
                    pstmt.setNull(i + 1, java.sql.Types.NULL);
                else if ( valueList.get(i) instanceof String )
                    pstmt.setString(i + 1, "" + valueList.get(i));
                else if ( valueList.get(i) instanceof Integer )
                    pstmt.setInt(i + 1, Integer.parseInt(""+valueList.get(i)));
                else if ( valueList.get(i) instanceof Long )
                    pstmt.setLong(i + 1, Long.parseLong(""+(valueList.get(i))));
                else if ( valueList.get(i) instanceof Double )
                    pstmt.setDouble(i + 1, Double.parseDouble(""+(valueList.get(i))));
                else if ( valueList.get(i) instanceof java.util.Date )
                    pstmt.setDate(i + 1, new java.sql.Date(((java.util.Date)valueList.get(i)).getTime()) );
                else if ( valueList.get(i) instanceof java.sql.Blob )
                    pstmt.setBlob(i + 1, (java.sql.Blob) valueList.get(i) );
                else
                    pstmt.setObject(i + 1, valueList.get(i) );
            }
        }
        catch ( SQLException e )
        {
            //輸出例外
            System.out.println("DbUtil.putSQLParams: " + e.toString());
            throw e;
        }
        return pstmt;
    }
    
    
    /**
     * 增加SQL查詢條件,和對應的值
     * 動作為 like 時,使用模糊查詢,忽略大小寫,空格轉變成匹配任意內容
     * 如果是 oracle資料庫,請在條件的最後面加上“escape'\'”
     * @param name 需要增加的SQL的欄位名稱
     * @param action 需要增加的SQL的對應動作,如 like, =, >= 等
     * @param value 對應的值
     * @param valueList 參數列表
     * @param canNull 空值參數是否加入查詢,為true時""和null都將加入查詢條件,否則不加入查詢條件;預設為false
     * @return SQL條件
     * @example  sql = "select * from tableName a where 1=1 ";
     *           sql += DbUtil.addSQL("a.aporder", "like", request.getParameter("aporder"), valueList, false);
     *           結果: sql = "select * from tableName a where 1=1 and a.aporder like ? "; valueList.add("kk%kk");
     */
    public static String addSqlCondition ( String name, String action, String value,
            List<String> valueList, boolean canNull )
    {
        // 防呆 和 去除前後空格
        name   = (name   == null) ? "" : name.trim();
        action = (action == null) ? "" : action.trim().toLowerCase();
        // 如果 name,action 值為空的話, 不用加上; value為空是有可能的
        if ( "".equals(name) || "".equals(action) )
            return "";
        // 參數為空時,不加入查詢條件
        if ( !canNull && (value == null || "".equals(value)) )
            return "";
        
        //value為null值時
        if ( value == null )
        {
            //等於 null
            if ( "like".equals(action) || "=".equals(action) || "is".equals(action) )
                return (" and " + name + " is null ");
            //不等於null
            else if ( "!=".equals(action) || "<>".equals(action) || "^=".equals(action) || action.contains("not") )
                return (" and " + name + " is not null ");
            else
                return "";
        }
        //刪除前後空格
        value = value.trim();
        
        // 如果動作是"like",則使用模糊查詢
        if ( "like".equals(action) )
        {
            // 如果 value 值為空的話, 不用加上
            if ( "".equals(value) )
                return "";
            // 傳回的字串結果,例如: and UPPER(col) like %value%
            name = "UPPER(" + name + ")";
            // 一個斜杠轉成4個斜杠(MySQL語法)
            value = value.replaceAll("\\\\", "\\\\\\\\\\\\\\\\");
            // 處理特殊符號,這裡用“\”做轉義符號;如果含有特殊符號,Oracle請在條件的最後面加上“escape'\'”
            value = value.replaceAll("%", "\\\\%");
            value = value.replaceAll("_", "\\\\_");
            // 將空格轉變成匹配任意內容
            value = value.replaceAll("\\s", "%");
            value = "%" + value.toUpperCase() + "%";
        }
        // 如果有參數列表
        if ( valueList != null )
        {
            // 為參數列表加上 value 值
            valueList.add( value );
            // 傳回的字串結果,例如: and col = value
            return (" and " + name + " " + action + " ? ");
        }
        // 沒有參數列表,則傳回查詢SQL條件
        value = StringUtil.toSqlStr(value);
        return (" and " + name + " " + action + " '" + value + "' ");
    }
    
    
    /**
     * 增加查詢SQL,和對應的值;如果參數值為空,就不加上去
     * 動作為 like 時,使用模糊查詢,忽略大小寫,空格轉變成匹配任意內容
     * 如果含有特殊符號,請在條件的最後面加上“escape'\'”
     * @param name 需要增加的SQL的欄位名稱
     * @param action 需要增加的SQL的對應動作,如 like, =, >= 等
     * @param value 對應的值
     * @param valueList 參數列表
     * @param canNull 空值參數是否加入查詢,為true時""和null都將加入查詢條件,否則不加入查詢條件;預設為false
     * @return SQL條件
     * @example  sql = "select * from tableName a where 1=1 ";
     *           sql += DbUtil.addSQL("a.aporder", "like", request.getParameter("aporder"), valueList);
     *           結果: sql = "select * from tableName a where 1=1 and a.aporder like ? "; valueList.add("kk%kk");
     */
    public static String addSqlCondition( String name, String action, String value, List<String> valueList )
    {
        return addSqlCondition( name, action, value, null, false );
    }
    
    
    /**
     * 增加查詢SQL,和對應的值;如果值為空的,就不加上去
     * 動作為 like 時,使用模糊查詢,忽略大小寫,空格轉變成匹配任意內容
     * 如果含有特殊符號,請在條件的最後面加上“escape'\'”
     * @param name 需要增加的SQL的欄位名稱
     * @param action 需要增加的SQL的對應動作,如 like, =, >= 等
     * @param value 對應的值
     * @return SQL條件
     * @example  sql = "select * from tableName a where 1=1 ";
     *           sql += DbUtil.addSQL("a.aporder", "like", request.getParameter("aporder"));
     *           結果: sql = "select * from tableName a where 1=1 and a.aporder like 'kk%kk'";
     */
    public static String addSqlCondition( String name, String action, String value )
    {
        return addSqlCondition( name, action, value, null );
    }
    
    
}
 
                    
                     
                    
                 
                    
                
 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号