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对象已关闭

     

 

posted @ 2018-08-06 16:57  BigJunOba  阅读(307)  评论(0编辑  收藏  举报