zoukankan      html  css  js  c++  java
  • Java基础(三十二)JDBC(2)连接数据库

      一、连接数据库的过程

      连接数据库的过程:加载数据库驱动程序,不过只需在第一次访问数据库时加载一次,然后在每次访问数据库时创建一个Connection实例,然后执行操作数据库的SQL语句,并返回执行结果,最后在完成此次操作时销毁前面创建的Connection实例,释放与数据库的连接。

      1.加载JDBC驱动程序

        // 加载数据库驱动类,加载失败抛出ClassNotFoundException异常
                Class.forName(Driver);   

      2.创建数据库连接

    // 创建一个数据库连接对象,创建失败会抛出SQLException异常
                Connection conn = DriverManager.getConnection(Url, User, Password);

      3.创建一个Statement对象

                // 通过Connection示例创建Statement实例
                Statement statement = conn.createStatement();

      4.执行SQL语句并获得查询结果

                // 通过Statement实例执行SQL语句并返回执行结果
                ResultSet rs = statement.executeQuery("select * from user");

      5.关闭连接释放资源

      在每次访问数据库后,应该按照下面的顺序,及时销毁这些实例,释放它们占用的所有资源。

                rs.close();
                statement.close();
                conn.close();

      二、Statement实例的三种类型

      Statement接口中,执行executeQuery方法可以返回查询结果到结果集中,执行executeUpdate方法可以插入、删除或者修改数据库记录,并返回一个int型数值,表示影响数据库记录的条数。

      Statement实例分为三种类型:Statement实例、(继承自Statement)PreparedStatement实例和(继承自PreparedStatement)CallableStatement实例。

      (1)Statement实例是最简单的Statement实例,只能用来执行静态的SQL语句

                ResultSet rs_queue = statement.executeQuery("select * from user");
                while (rs_queue.next()) {
                    System.out.println(rs_queue.getInt("id") + " " 
                            + rs_queue.getString("name") + " " 
                            + rs_queue.getString("sex") + "  "
                            + rs_queue.getString("birthday"));
                }
                System.out.println(statement.executeUpdate("update user set sex='女' where id=1"));  // 打印:1
           rs_queue.close();
                statement.close()

      (2)PreparedStatement实例增加了执行动态SQL语句的功能

                String sql = "update user set name = ?, sex = ?, birthday = ?where id =?";
                PreparedStatement predStatement = conn.prepareStatement(sql);
                predStatement.setString(1, "loser");
                predStatement.setString(2, "女");
                predStatement.setDate(3, new Date(System.currentTimeMillis()));
                predStatement.setInt(4, 1);
                System.out.println(predStatement.executeUpdate());    // 打印:1
                predStatement.close();

      (3)CallableStatement实例增加了执行数据库存储过程的功能

      首先在MySQL中创建一个存储过程并测试:

    mysql> select * from user //
    +----+-------+------+------------+
    | id | name  | sex  | birthday   |
    +----+-------+------+------------+
    |  1 | loser || 2018-08-06 |
    |  2 | lsl   || 2017-12-12 |
    |  3 | zgs   || 2016-06-01 |
    +----+-------+------+------------+
    3 rows in set (0.00 sec)
    
    mysql> create procedure proc_count_select_by_sex(IN girl_or_boy VARCHAR(255))
        -> READS SQL DATA
        -> BEGIN
        -> select count(*) from user where sex=girl_or_boy;
        -> END
        -> //
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> call proc_count_select_by_sex('') //
    +----------+
    | count(*) |
    +----------+
    |        2 |
    +----------+
    1 row in set (0.00 sec)
    
    Query OK, 0 rows affected (0.00 sec)

      然后使用CallableStatement对象调用存储过程:

                String sql = "{call proc_count_select_by_sex(?)}";
                CallableStatement cablStat = conn.prepareCall(sql);
                cablStat.setString(1, "女");
                ResultSet rs = cablStat.executeQuery();
                while (rs.next()) {
                    System.out.println(rs.getInt(1));    // 打印:2
                }
    rs.close(); cablStat.
    close();

      三、标准JDBC程序设计

    package jdbc.jun.iplab;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class JDBC {
    
        private static final String Driver = "com.mysql.jdbc.Driver";
        private static final String Url =  "jdbc:mysql://localhost:3306/mysqldb?useSSL=false";
        private static final String User = "root";
        private static final String Password = "bjtungirc";
        
        static {
            try {
                Class.forName(Driver); 
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        
        public static void main(String[] args) {
            try {     
                // 创建一个数据库连接对象,创建失败会抛出SQLException异常
                Connection conn = DriverManager.getConnection(Url, User, Password);
                // 通过Connection示例创建Statement实例
                Statement statement = conn.createStatement();
                // 通过Statement实例执行SQL语句并返回执行结果
                ResultSet rs_queue = statement.executeQuery("select * from user");
                while (rs_queue.next()) {
                    System.out.println(rs_queue.getInt("id") + " " 
                            + rs_queue.getString("name") + " " 
                            + rs_queue.getString("sex") + "  "
                            + rs_queue.getString("birthday"));
                }
                System.out.println(statement.executeUpdate("update user set sex='女' where id=1"));
                rs_queue.close();
                statement.close();
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            } 
        }
    }
    JDBC标准代码设计

       实际工程中使用JDBC的标准写法

    package fileTransfer;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ResourceBundle;
    
    
    public class DBUtils {
    
        private static String driverClass;
        private static String url ;
        private static String userName;
        private static String password;
        private static Connection connection = null;
    
        static{
            //读取配置文件,加载数据库相关信息
            ResourceBundle resourceBundle = ResourceBundle.getBundle("info");
            driverClass = resourceBundle.getString("driverClass");
            url = resourceBundle.getString("url");
            userName = resourceBundle.getString("userName");
            password = resourceBundle.getString("password");
            
            try {
                Class.forName(driverClass);
            } catch (Exception e) {
                System.out.println(e.toString()+"加载驱动失败!");
            }
        }
        
        public static Connection getConnection(){
            
            try {
                connection =  DriverManager.getConnection(url, userName, password);
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                System.out.println(e.toString()+"数据库连接失败!");
            }
            return connection;
        }
        
        public static void CloseAll(ResultSet resultSet, PreparedStatement pStatement, Connection connection){
            if (resultSet!=null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            
            if (pStatement != null) {
                try {
                    pStatement.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(connection != null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    JDBC标准工程写法

      四、JDBC连接池

       实际工程中应该考虑下面的问题:建立数据库连接需要开销,因为数据库连接是有限的资源,如果用户要离开应用一段时间,那么他占用的连接就不应该保持打开状态;另一方面,每次查询都获取连接并在随后关闭它的代价也是相当高的。

      解决上述问题的方法时建立数据库连接池(pool),这意味着数据库连接在物理上并为关闭,而是保留在一个队列中并被反复重用。

      连接池的使用对程序员来说是完全透明的,可以通过获取数据源并调用getConnection方法来得到连接池中的连接。使用完连接后,需要调用close()方法,该方法不再物理上关闭连接,而是只告诉连接池已经使用完该连接,将Connection对象返回到LinkedList对象中。

      1.编写连接池需要实现java.sql.DataSource接口

      2.创建LinkedList对象,并创建“最小连接数”个Connection对象并将这些对象添加到LinkedList对象中

      3.重写getConnection方法,使用动态代理技术管理连接池中的Connection对象

      4.封装getConnection()方法和release()方法

      示例代码

    • 配置文件jdbc.properties
    driver = com.mysql.jdbc.Driver
    url = jdbc:mysql://localhost:3306/mysqldb?useSSL=false
    userName = root
    password = bjtungirc
    
    ConnectionPoolInitSize = 10
    jdbc.properties
    • 连接池类ConnectionPool
    1. 初始化:通过ResourceBundle.getBundle("jdbc")读取jdbc.properties里面的配置内容,然后初始化新建立默认的最小数据库连接对象10个Connection对象,并将这些对象加入到由LinkedList类实现的链表中。
      public class ConnectionPool implements DataSource {
          
          private static String driver;
          private static String url ;
          private static String userName;
          private static String password;
          private static int ConnectionPoolInitSize;
          
          private static LinkedList<Connection> conn_list = new LinkedList<>();
          
          static {
              
              try {
                  ResourceBundle resourceBundle = ResourceBundle.getBundle("jdbc");
                  driver = resourceBundle.getString("driver");
                  url = resourceBundle.getString("url");
                  userName = resourceBundle.getString("userName");
                  password = resourceBundle.getString("password");
                  ConnectionPoolInitSize = Integer.parseInt(resourceBundle.getString("ConnectionPoolInitSize"));
                  Class.forName(driver);
                  for (int i = 0; i < ConnectionPoolInitSize; i++) {
                      Connection conn = DriverManager.getConnection(url, userName, password);
                      conn_list.add(conn);
                  }
              } catch (SQLException e) {
                  e.printStackTrace();
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          @Override
          public Connection getConnection() throws SQLException {
              ...
      }
      }
    2. getConnection()方法的重写:执行该方法会从LinkedList链表中拿出一个Connection对象conn并返回,然后通过动态代理实现:如果拿出来的这个conn对象执行了close方法,就将这个conn对象重新放回到LinkedList链表中。
          @Override
          public Connection getConnection() throws SQLException {
              if (conn_list.size()>0) {
                  final Connection conn = conn_list.removeFirst();
                  System.out.println(1);
                  return (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() {
                      
                      @Override
                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          if (!method.getName().equalsIgnoreCase("close")) {
                              return method.invoke(conn, args);
                          } else {
                              conn_list.add(conn);
                              return null;
                          }
                      }
                  });
              } else {
                  System.out.println("数据库连接失败");
              }
              return null;
          }
    • 封装的JDBC连接类DBUtils类(包括了getConnection方法和closeAll方法)
      package connPool.jun.iplab;
      
      import java.sql.Connection;
      import java.sql.PreparedStatement;
      import java.sql.ResultSet;
      import java.sql.SQLException;
      
      public class Get_jdbc_conn_from_cPool {
      
          private static ConnectionPool cPool = new ConnectionPool();
          
          public static Connection getConnection() throws SQLException{
              return cPool.getConnection();
          }
          
          public static void CloseAll(ResultSet resultSet, PreparedStatement pStatement, Connection connection){
              if (resultSet!=null) {
                  try {
                      resultSet.close();
                  } catch (SQLException e) {
                      e.printStackTrace();
                  }
              }
              
              if (pStatement != null) {
                  try {
                      pStatement.close();
                  } catch (SQLException e) {
                      e.printStackTrace();
                  }
              }
              if(connection != null){
                  try {
                      connection.close();
                  } catch (SQLException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
    • 测试类
      package connPool.jun.iplab;
      
      import java.sql.Connection;
      import java.sql.ResultSet;
      import java.sql.SQLException;
      import java.sql.Statement;
      
      public class CPoolTest {
      
          public static void main(String[] args) throws SQLException {
              
              // 得到数据库连接对象
              Connection conn = DBUtils.getConnection();
              // 数据库操作
              Statement statement = conn.createStatement();
              ResultSet rs = statement.executeQuery("select * from user where sex='男'");
              while (rs.next()) {
                  System.out.println(rs.getInt("id") + " " 
                          + rs.getString("name") + " " 
                          + rs.getString("sex") + "  "
                          + rs.getString("birthday"));
              }
              // 执行这条语句时,conn对象执行了close()方法,因此会将conn对象重新添加到LinkedList集合中
              DBUtils.CloseAll(rs, statement, conn);
          }
      }
    • 输出
      2 lsl 男  2017-12-12
      4 winner 男  2018-08-07
      9 nine 男  2018-08-07
      ResultSet对象已关闭
      Statement对象已关闭
      Connection对象已关闭
  • 相关阅读:
    mybatis typeAlias (别名)说明
    jsp路径
    Jutil 单元测试
    SpringMVC数据绑定
    EL表达式(Exprission language)
    JSTL (标准标签库)
    Introduce Null Object
    Replace Type Code With Class和Replace Type Code With Subclass和Replace Type Code With State/Strategy
    利用QBuffer和QLinkedList做数据块存储队列
    Duplicate Observed Data
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/9431241.html
Copyright © 2011-2022 走看看