JDBC辅助组件的开发

要点:

静态代码块
数据库驱动的加载
单例模式
数据库连接池
获取数据库连接
禁止使用硬编码(导入Constants接口)
实现增删改查+ 批量执行

单例模式介绍
我们自己定义的类,其实默认情况下,都是可以让外界的代码随意创建任意多个实例的,但是有些时候,我们不希望外界来随意创建实例,而只是希望一个类,在整个程序运行期间,只有一个实例,任何外界代码,都不能随意创建实例,这时就需用到单例模式。
那么,要实现单例模式,有几个要点:
1. 如果不想让外界可以随意创建实例,那么类的构造方法就必须用private修饰,必须是私有的;
2. 既然类的构造方法被私有化了,外界代码要想获取类的实例,不能够随意地去创建,那么就只能通过调用类的静态方法,去获取类的实例;
3. 所以类必须有一个静态方法getInstance()来提供获取唯一实例的功能,getInstance()方法必须保证类的实例创建,且仅创建一次,返回一个唯一的实例。

单例模式的应用场景有哪几个呢?
1. 配置管理组件,可以在读取大量的配置信息之后,用单例模式的方式,就将配置信息仅仅保存在一个实例的实例变量中,这样可以避免对于静态不变的配置信息,反复多次的读取;
2. JDBC辅助组件,全局就只有一个实例,实例中持有了一个内部的简单数据源使用了单例模式之后,就保证只有一个实例,那么数据源也只有一个,不会重复创建多次数据源(数据库连接池)

JDBC原理介绍
JDBC代表了JDK提供的一套面向数据库的一套开发接口,大部分仅仅是接口而已,Java应用程序光有JDBC是操作不了数据库的,更不用谈增删改查等功能。JDBC真正的意义在于通过接口统一了java程序对各种数据库的访问规范。
数据库厂商会提供JDBC驱动:JDBC Driver,在这套实现类中,不同的数据厂商就实现了针对自己数据库的一套连接、执行SQL语句等等实际的功能。
本次实验项目主要使用的是MySQL数据库

配置管理组件介绍
• 配置管理组件可以复杂,也可以很简单,对于简单的配置管理组件来说,只要开发一个类,可以在第一次访问它的时候,就从对应的properties文件中,读取配置项,并提供外界获取某个配置key对应的value的方法;
• 如果是特别复杂的配置管理组件,那么可能需要使用一些软件设计中的设计模式,比如单例模式、解释器模式,可能需要管理多个不同的properties,甚至是xml类型的配置文件;
• 我们这里的话,就是开发一个简单的配置管理组件,就可以了。
静态代码块介绍
在Java中,每一个类第一次使用的时候,就会被Java虚拟机(JVM)中的类加载器从磁盘上的.class文件中加载出来,然后为每个类都会构建一个Class对象,就代表了这个类。
每个类在第一次加载的时候,都会进行自身的初始化,那么类初始化的时候,会执行哪些操作的呢?
这就由每个类内部的static {}构成的静态代码块决定,我们自己可以在类中开发静态代码块。类第一次使用的时候,就会加载,加载的时候,就会初始化类,初始化类的时候就会执行类的静态代码块。
因此,对于我们的配置管理组件,就在静态代码块中,编写读取配置文件的代码,这样的话,第一次外界代码调用这个ConfigurationManager类的静态方法的时候,就会加载配置文件中的数据, 而且,放在静态代码块中,还有一个好处,就是类的初始化在整个JVM生命周期内,有且仅有一次, 配置文件只会加载一次,然后以后就是重复使用,效率比较高,不用反复加载多次。

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.LinkedList;

import com.ibeifeng.sparkproject.conf.ConfigurationManager;
import com.ibeifeng.sparkproject.constant.Constants;

/** * JDBC 辅助组件 * @author chenc * */
public class JDBCHelper {
    /** * 第一步: * 静态代码块中,直接加载数据库驱动 */
    static {
        try {
            //一:加载驱动
            Class.forName(ConfigurationManager.getProperty(Constants.JDBC_DRIVER));
            // 二:实现JDBCHeleper的单例化:因为内部封装了一个数据库连接池:要保证数据库连接池有且仅有一份


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static JDBCHelper instance = null;

    /** * 第二步: * 获取单例 * @return */
    public static JDBCHelper getInstance() {
        if (instance == null ) {
            synchronized (JDBCHelper.class) {
                if (instance == null) {
                    instance = new JDBCHelper();
                }

            }
        }
        return instance;
    }
    //数据库连接池
    private LinkedList datasource = new LinkedList();
    /** * 第三步:实现单例过程中,创建唯一的数据库连接池 * 私有化构造方法:整个程序运行声明周期中,只会创建一次实例 */
    private JDBCHelper() {
        //获取数据库连接池大小
        int datasourceSize = ConfigurationManager.getInteger(Constants.JDBC_DATASOURCE_SIZE);
        //创建指定数量的数据库连接,并放入数据库连接池中
        for(int i = 0; itry {
                Connection conn = DriverManager.getConnection(url, user, password);
                datasource.push(conn);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /** * 第四步:提供获取数据库连接的方法 * 加入多线程限制 */
    public synchronized Connection getConnection() {
        //有可能获取时,连接被用完,需要编码实现简单等待机制。
        while(datasource.size() == 0) {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return datasource.poll();
    }

    /** * 开发增删改查的方法 * 1. 增删改 * 2. 查询 * 3. 批量执行 */
    public int executeUpdate(String sql, Object[] params) {
        int rtn = 0;
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn=getConnection();
            pstmt = conn.prepareStatement(sql);

            for(int i= 0 ; i1, params[i]); //设置对应参数
            }
            rtn =  pstmt.executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (conn != null) {
                datasource.push(conn);
            }
        }
        return rtn;
    }
    /** * 查询 * @param sql * @param params * @param callback */
    public void executeQuery(String sql,Object[] params, QueryCallback callback) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            for (int i = 0; i1, params[i]);
            }
            rs = pstmt.executeQuery();

            callback.process(rs);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (conn != null) {
                datasource.push(conn);
            }
        }
    }
    /** * 内部类:查询回调接口 * @author chenc * */
    static interface QueryCallback{
        /** * 处理查询结果 * @param rs * @throws Exception */
        void process(ResultSet rs) throws Exception; //此函数的实现在test中实现了
    }
    /** * 批量执行sql语句:一次通过PreparedStatement发送多条SQL语句。 * 执行时也只是编译一次 * @param sql * @param paramsList * @return 每条sql语句影响的行数 */
    public int[] executeBatch(String sql,List paramsList) {
        int[] rtn = null;
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = getConnection();
            //第一步:使用Connection对象取消自动提交
            conn.setAutoCommit(false);

            pstmt = conn.prepareStatement(sql);
            //第二步:使用PreparedStatement.addBatch()方法加入批量参数
            for(Object[] params: paramsList) {
                for (int i = 0; i1, params[i]);
                }
                pstmt.addBatch();
            }
            //第三步:使用PreparedStatement.executeBatch()执行批量SQL语句
            rtn = pstmt.executeBatch();

            //第四步:使用Connection对象,提交批量SQL语句
            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

测试代码如下:


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.ibeifeng.sparkproject.jdbc.JDBCHelper;

public class JDBCHelperTest {
    public static void main(String[] args) throws Exception{
        JDBCHelper jdbcHelper = JDBCHelper.getInstance();
// jdbcHelper.executeUpdate("insert into test_user(name,age) values(?,?)",
// new Object[] {"wanger",28});

        final Map testUser = new HashMap();

        // 设计一个内部接口QueryCallback
        // 那么在执行查询语句的时候,我们就可以封装和指定自己的查询结果的处理逻辑
        // 封装在一个内部接口的匿名内部类对象中,传入JDBCHelper的方法
        // 在方法内部,可以回调我们定义的逻辑,处理查询结果
// // 并将查询结果,放入外部的变量中
// jdbcHelper.executeQuery(
// "select name,age from test_user where id=?", 
// new Object[]{3}, 
// new JDBCHelper.QueryCallback() {
// 
// @Override
// public void process(ResultSet rs) throws Exception {
// if(rs.next()) {
// String name = rs.getString(1);
// int age = rs.getInt(2);
// 
// // 匿名内部类的使用,有一个很重要的知识点
// // 如果要访问外部类中的一些成员,比如方法内的局部变量
// // 那么,必须将局部变量,声明为final类型,才可以访问
// // 否则是访问不了的
// testUser.put("name", name);
// testUser.put("age", age);
// }
// }
// 
// });
// 
// System.out.println(testUser.get("name") + ":" + testUser.get("age")); 

        // 测试批量执行SQL语句
        String sql = "insert into test_user(name,age) values(?,?)"; 

        List paramsList = new ArrayList();
        paramsList.add(new Object[]{"麻子", 30});
        paramsList.add(new Object[]{"王五", 35});

        jdbcHelper.executeBatch(sql, paramsList);
    }

}

你可能感兴趣的:(spark项目)