细谈浅析Mysql在Java项目中使用C3P0包和DBUtils对数据库的操作

前言,本篇博客适合初学的java开发者阅读,了解一些底层的原理和思想。实际在工作中这些东西并不会再接触(除C3P0),但是万变不离其宗!底层原理原理和思想非常重要!如需更正请详细指出,相互学习,感谢。

所需jar包

  1. 要在Java项目中连接数据库,必须需要mysql驱动包.
  • mysql-connector-java-5.1.39-bin.jar
  1. C3P0.
  • c3p0-0.9.1.2.jar
  1. DBUtils.
  • commons-dbutils-1.4.jar

原生五步法

要使用Java代码连接数据库,并操作数据库,就要导入msyql包,原生操作数据库分为以下五步:

// 1.注册驱动
DriverManager.registerDriver(new Driver());
// TODO 数据库路径
String url = "jdbc:mysql://localhost:3306/shopmall";
// TODO 用户名与密码
String user = "root";
String password ="123";
// 2.获取连接
Connection connection = (Connection) DriverManager.getConnection(url, user, password);
// 3.创建执行SQL语句的对象
Statement statement = connection.createStatement();
String sql = "select * from orders";
// 4.处理执行SQL语句的结果
ResultSet result = statement.executeQuery(sql);
while (result.next()) {
    System.out.println(result.getObject("ordertime") + "-" + result.getObject("address"));
}
// 5.关闭资源
if (result != null) {
    result.close();
}
if (statement != null) {
    statement.close();
}
if (connection != null) {
    connection.close();
}

分析

第一步:

// 1.注册驱动
DriverManager.registerDriver(new Driver());

我们在register驱动时,是new Driver(); 但当我们进入到源码却发现,在静态代码块中已经注册了一次,此时会想,我们在写代码时已经注册了一次,再写一次相当于注册了两次,这样肯定是不合理的,根据源码我们可以发现DriverManage.registerDriver(new Driver); 是写在静态代码块中!那么怎么让这个静态代码块执行一次,不就注册了吗?而且是注册一次!这里牵扯到Java程序运行时先将.Java文件打包成.class文件,然后类加载器处理,所以我们这里只需要将msyql jar 包下的 Driver类加载一次就行了,代码如下:

Class.forName("com.mysql.jdbc.Driver"); // 1.注册驱动

mysql 包下Driver源码如下:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
	public Driver() throws SQLException {
	}
	static {
		try {
			DriverManager.registerDriver(new Driver());
		} catch(SQLException E) {
			throw new RuntimeException("Can't register driver!");
		}
	}
}

第二步:

在配置连接MySQL参数时,直接是写在源码中,在实际开发中如果不使用框架也不会直接写在源码中,而会写在配置文件中,并将其封装成一个utils工具类。写入配置文件,即:.properties结尾的文件,示例如下:

url=jdbc:mysql://localhost:3306/shopmall
user=root
password=root
driver=java.sql.Driver
  • 注意事项:.properties配置文件中,可以理解为以键值对的形式书写、不能有空格、键值对直接必须以‘=’连接

下面我给大家介绍两种实用的读取配置文件的方式:JDBCUtils是自己封装的一个工具类,蓝色标记处即为读取配置文件方式

  • 方式一:通过I/O流读取,其中有直接通过传入相对路径文件和使用类加载器两种方式
public class JDBCUtil {
    private static String password;
    private static String user;
    private static String url;
    private static String driverClassName;
    static{
        //从配置文件中读取这些成员变量的值,并设置好
        Properties properties = new Properties();
        //让properties对象和jdbcinfo.properties文件发生关系
        InputStream inStream = null;
        try {
            /*//第一种:将文件转换成字节输入流
            InputStream inStream = new FileInputStream("src/jdbcinfo.properties");*/
            
            //第二种:使用类加载器
            //1.获取类加载器对象
            Class clazz = JDBCUtil.class;
            ClassLoader classLoader = clazz.getClassLoader();
            inStream = classLoader.getResourceAsStream("jdbcinfo.properties");
            //将字节输入流加载到properties对象中
            properties.load(inStream);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                inStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 读取配置文件
        password = properties.getProperty("password");
        user = properties.getProperty("user");
        url = properties.getProperty("url");
        driverClassName = properties.getProperty("driverClassName");
        //1.注册驱动,在静态代码块中进行注册,防止了重复注册驱动。
        try {
            Class.forName(driverClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection() throws Exception{
        //2.获得连接
        //新创建一个连接
        Connection conn = DriverManager.getConnection(url, user, password);
        return conn;
    }
    public static void close(Connection conn,Statement statement,ResultSet resultSet) throws SQLException{
        if (resultSet != null) {
            resultSet.close();
        }
        if (statement != null) {
            statement.close();
        }
        if (conn != null) {
            
            conn.close();
        }
    }
}
  • 方式二:使用ResourceBundle类,相比于I/O流还是简单一些
public class JDBCUtils {
    static String url;
    static String user;
    static String password;
    static String driver;
    static {
        // 读取配置文件
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
        url = bundle.getString("url");
        user = bundle.getString("user");
        password = bundle.getString("password");
        driver = bundle.getString("driver");
        System.out.println(url + user + password + driver);
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    // 获得连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }
    // 关闭资源
    public static void release(ResultSet resultSet, Statement statement, Connection connection) throws SQLException {
        if (resultSet != null) {
            resultSet.close();
        }
        if (statement != null) {
            statement.close();
        }
        if (connection != null) {
            connection.close();
        }
    }
}

第三步:

在将注册驱动、读取配置文件和获取连接封装成一个工具类之后再看一下连接、操作数据库的代码,这里有个SQL语句注入的问题,所以我将Statement改成类PreparedStatement。PreparedStatement是先记住了SQL语句的格式,然后再通过setString方式传入参数,而传入参数是以1开始:

public class JDBCDemo01 { 
        @Test
    public void test01() throws Exception{
        //1.注册驱动:无需再写
        //2.获得连接对象
        Connection conn = JDBCUtil.getConnection();
        String sql = "select * from orders where oid=?";
        //3.创建执行SQL语句的对象
        PreparedStatement prepareStatement = conn.prepareStatement(sql);
                prepareStatement.setString(1,"1");
        //4.执行SQL语句,如果有结果集则处理结果集
        ResultSet resultSet = prepareStatement.executeQuery();
        while(resultSet.next()){
            System.out.println(result.getObject("ordertime") + "-" + result.getObject("address"));
        }
        //5.关闭资源
        JDBCUtil.close(conn, prepareStatement, resultSet);
    }
}

C3P0

这个jar包是对连接数据的一个优化库,内部主要重点是连接池的概念,对连接数据库、操作数据库在性能上有了一定的提升.

  1. 相信C3P0是大多数Java程序员操作数据库所使用的第一个jar包吧,c3p0包中重点是理解连接池的概念。在操作数据库中,sun公司制定了一套规范,说白了就是接口,c3p0就是实现了DataSource接口。
  2. 在不是用c3p0包会出现什么问题呢? 每次操作数据库都要获取一个连接对象吗? 假如有10万个用户同时访问数据库,我们需要同时创建10万个connection吗?显然是NO!!!所以就有了连接池的概念,在程序运行时就创建若干个连接对象并放入集合中(LinkedList集合),当我们需要使用时就从集合中拿到第一个连接对象(connection),用完后就还回到集合中(addLast()),这样就大大的减少了内存提高了性能。这时会有一个问题,当访问数大于连接池的count时,What do I do?这时我们就设置一个等待时间,当等待时间都过去了就重新获取一个新的连接,这样就解决了。
  3. C3P0里面还使用了一个设计模式:装饰者模式
  4. 在使用C3P0时,有两种方式,一种也是通过配置文件读取连接数据库的参数,另一种是写一个c3p0-config.xml文件,c3p0内部会自己解析此文件:
    • 方式一:通过配置文件读取连接数据库的参数
public class C3P0Util {
    public static Connection getConnection() throws SQLException, PropertyVetoException{
        //从C3P0连接池对象中获取连接
        //1.创建c3p0连接池对象
         ComboPooledDataSource cpds = new ComboPooledDataSource();
        // 读取配置文件
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
        String url = bundle.getString("url");
        String user = bundle.getString("user");
        String password = bundle.getString("password");
        String driver = bundle.getString("driver");
         //配置连接池,要将用户名、密码、url等等设置一下
         cpds.setUser(user);
         cpds.setInitialPoolSize(10);
         cpds.setPassword(password);
         cpds.setJdbcUrl(url);
         //设置驱动类的类名
         cpds.setDriverClass(driver);
        //2.从这个连接池对象中获取连接
         Connection connection = cpds.getConnection();
         return connection;
    }
    public static void close(ResultSet resultSet,Statement statement,Connection connection) throws SQLException{
        if (resultSet != null) {
            resultSet.close();
        }
        if (statement != null) {
            statement.close();
        }
        if (connection != null) {
            connection.close();
        }
    }
}
  • 方式二:配置c3p0-config.xml文件,这里我是直接写死在xml中,工作中推荐把这些配置信息单独写到一个.properties文件中
    配置c3p0-config.xml文件
public class C3P0Utils {
    private static  ComboPooledDataSource dataSource;
    static{
         //从C3P0连接池中获取连接对象
        dataSource = new ComboPooledDataSource();
    }
    public static Connection getConnection() throws SQLException, PropertyVetoException{
         return dataSource.getConnection();
    }
    public static void close(ResultSet resultSet,Statement statement,Connection connection) throws SQLException{
        if (resultSet != null) {
            resultSet.close();
        }
        if (statement != null) {
            statement.close();
        }
        if (connection != null) {
            connection.close();
        }
    }
}

DBUtils

DBUtils 主要功能是减少了使用JDBC对SQL数据操作的代码,这里仅仅是对代码的优化,在性能上没有提升,DBUtils内部实现原理使用到了反射机制. 对于DBUtils的使用,内部核心我认为是反射机制和内省机制,大家可以去了解一个反射机制与内省机制,你也可以写一个DBUtils出来!这篇博客先贴出如何使用DBUtils包(这里需要与刚刚封装的C3P0Utils类一起使用)

public void test01() throws SQLException{
    //从t_user表中删除id=6的数据
    //1.创建QueryRunner对象,传入一个连接池对象
    QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
    //2.创建并执行SQL语句
    String sql = "delete from t_user where id=?";
    runner.update(sql, 6);
}

public void test02() throws SQLException{
    //更新id为2的数据的密码为888888
    //1.创建QueryRunner对象
    QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
    //2.创建并执行SQL语句
    String sql = "update t_user set password=? where id=?";
    Object[] params = {"888888",2};
    runner.update(sql, params);
}

public void test03() throws SQLException{
    //查询id为2的数据,并将该条数据存放到user对象中
    //1.创建QueryRunner对象
    QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
    //2.创建并执行SQL语句
    String sql = "select * from t_user where id=?";
    //第一种ResultSetHandler接口的实现类,叫做BeanHandler能够将一条数据封装到JavaBean对象中
    //要求这个JavaBean对象的成员变量的变量名和这条数据的字段名一模一样
    //你要将这条数据封装到哪个JavaBean对象中,就传入该类的字节码文件对象
    User user = runner.query(sql, new BeanHandler<>(User.class), 2);
    System.out.println(user);
}

public void test04() throws SQLException{
    //查询出t-user表中的所有数据,并将每一条数据存放到user对象中,再将每一个user对象存放list集合中
    //1.创建QueryRunner对象
    QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
    //2.创建并执行SQL语句
    String sql = "select * from t_user";
    //处理多条数据,将每条数据存放到JavaBean对象中,然后将每个JavaBean对象存放到list集合中,就使用BeanListHandler
    List<User> users = runner.query(sql, new BeanListHandler<>(User.class));
    System.out.println(users.size());
}

猜你喜欢

转载自blog.csdn.net/qq_34382367/article/details/86634790