Java-JDBC+C3P0连接池

1.JDBC

Java DataBase Connector

JDBC是Java数据库连接技术的简称,提供连接各种常用数据库的能力

2.JDBC编程模板

1、加载JDBC驱动

​ Class.forName(JDBC驱动类);

  1. 与数据库创建连接

    Connection con=DriverManager.getConnection(URL,数据库用户名,密码);

    ​ URL: 数据库类型、数据库服务所在计算记得ip地址、服务端口号、默认数据仓库、配置参数

  2. 通过连接对象创建statement对象发送SQL语句,并得到返回结果

  3. 处理结果集,查询的结果集是ResultSet,增删改查返回的就是一个int值是表达受影响的行数

  4. 释放资源

1. 如何下载jdbc驱动包

  1. https://mvnrepository.com/
  2. 在搜索框中输入mysql
  3. 找到名字: MySQL Connector/J
  4. 选择对应版本
  5. 点击版本号,进入可以选择jar进行下载或者其他的方法引入

2.案例代码

import java.sql.*;

public class Test {

    public static void main(String[] args) {

        //1. 注册驱动
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.err.println("mysql驱动加载异常!");
            e.printStackTrace();
        }

        //2. 连接数据库
        String url = "jdbc:mysql://localhost:3306/javatest?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai";
        String userName = "root";
        String pwd = "cxk666";
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(url,userName,pwd);
        } catch (SQLException e) {
            System.err.println("数据库连接出现异常!");
            e.printStackTrace();
        }

        //3. 操作数据库
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            statement = connection.createStatement();
            //查询返回结果集
            resultSet = statement.executeQuery("select * from student");
        } catch (SQLException e) {
            System.err.println("操作数据库异常!");
            e.printStackTrace();
        }

        //4. 处理数据
        try{
            while(resultSet.next()){
                String s_id = resultSet.getString("s_id");
                System.out.print(s_id+"\t");

                String s_name = resultSet.getString("s_name");
                System.out.print(s_name+"\t");
                System.out.println();
            }
        }catch (SQLException e){
            System.err.println("数据处理异常");
            e.printStackTrace();
        }


        //5. 关闭资源
        try {
            resultSet.close();
            statement.close();
            connection.close();
        } catch (SQLException e) {
            System.err.println("连接关闭异常");
            e.printStackTrace();
        }

    }

}

方法名说 明
ResultSet executeQuery(String sql)执行SQL查询并获取到ResultSet对象
int executeUpdate(String sql)可以执行插入、删除、更新等操作,返回值是执行该操作所影响的行数
boolean execute(String sql**)**可以执行任意SQL语句,然后获得一个布尔值,表示是否返回****ResultSet

3. JDBC-execute方法执行增删改查语句

//execute 方法执行:增、删、改返回的都是false,执行查询语句返回的是true
boolean execute = statement.execute(sql);//一般不用于查询
/* 注册驱动、创建连接的方法和2.代码案例中一致 **/

//3. 插入数据
     String sql = "INSERT INTO `student` ( `s_id`, `s_name`, `s_birth`, `s_sex` )\n" +
             "VALUES\n" +
             "\t( '09', '旭宝宝', '1990-01-01', '男' );\n";

     //3.1 获取statement对象
     Statement statement = null;
     try {
         statement = connection.createStatement();

         // execute 方法执行:增、删、改返回的都是false,执行查询语句返回的是true
         boolean execute = statement.execute(sql);

         System.out.println(execute);

     } catch (SQLException e) {
         System.err.println("数据插入错误!");
         e.printStackTrace();
     }

/* 关闭连接操作省略 */

4.JDBC-executeUpdate执行增删改语句

// executeUpdate:返回值是受影响的行数,可以使用在增、删、改语句,不能使用于查询语句
int i = statement.executeUpdate(sql);
//3. 更新数据
        String sql = "update student set s_name='卢本伟' where s_sex='人妖';";

        //3.1 获取statement对象
        Statement statement = null;
        try {
            statement = connection.createStatement();

            // executeUpdate:返回值是受影响的行数,可以使用在增、删、改语句,不能使用于查询语句
            int i = statement.executeUpdate(sql);

            System.out.println(i);

        } catch (SQLException e) {
            System.err.println("数据插入错误!");
            e.printStackTrace();
        }

5. JDBC-查询语句-executeQuery方法

// executeQuery:只能运用于查询语句,返回的是查询结果集
ResultSet resultSet = statement.executeQuery(sql);
 //3. 操作数据库
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            statement = connection.createStatement();
            //查询返回结果集
            resultSet = statement.executeQuery("select * from student");
        } catch (SQLException e) {
            System.err.println("操作数据库异常!");
            e.printStackTrace();
        }

        //4. 处理数据
        try{
            while(resultSet.next()){
                String s_id = resultSet.getString("s_id");
                System.out.print(s_id+"\t");

                String s_name = resultSet.getString("s_name");
                System.out.print(s_name+"\t");
                System.out.println();
            }
        }catch (SQLException e){
            System.err.println("数据处理异常");
            e.printStackTrace();
        }

5.1 ResultSet对象操作方法

方法名说 明
boolean next()将游标从当前位置向下移动一行
boolean previous()游标从当前位置向上移动一行
void close()关闭****ResultSet 对象

获取字段的操作方法:

​ get数据类型(字段名\字段序号值)

取值方法:
1.字段顺序:1代表第一列

​ 2.字段名称取值:查询结果中投影怎么写,字段名就怎么写

常见的mysql数据类型匹配jdbc方法:

getInt : int\tinyint\bigint

getDouble: float\double\deccamial

getString: char\varchar\text\longText

getDate: date\time\datetime\timestamp (java.sql.Date extends java.util.Date)

while(resultSet.next()){
    
                String string = resultSet.getString("s_name");

                System.out.println(string);
            }

3.SQL 注入问题

sql注入是什么?

​ 通过标点符号或特殊符号注入到参数中,使后端java运行sql语句异常

存在的安全问题:

1. 数据泄露和遗失
1. 会让数据库操作全部崩溃
// 输入用户名和密码
        String sys_userName = "yyy";
        String sys_pwd = "' or 1=1 or ''='";

        //3. 操作数据库
        Statement statement = null;
        ResultSet resultSet = null;

        String sql = "select * from sys_user where userName = '"+sys_userName+"' and pwd='"+sys_pwd+"'";
        
        //select * from sys_user where userName = 'yyy' and pwd='' or 1=1 or ''=''
        
        System.out.println("sql:"+sql);
        try {
            statement = connection.createStatement();
            //查询返回结果集
            resultSet = statement.executeQuery(sql);
        } catch (SQLException e) {
            System.err.println("操作数据库异常!");
            e.printStackTrace();
        }

SQL注入问题解决方案

1. 方案-一刀切

禁止在系统中允许用户输入:空白、制表符、or、标点符号等特殊字符

2.方案-PreparedStatement 接口

使用方法:

  1. connection对象调用prepareStatement方法创建PreparedStatement对象:注意,调用prepareStatement方法把sql提交给对象进行预编译
  2. 参数插入时要保证sql字符串中有?占位符,根据站位符的顺序添加参数
  3. 调用set方法规则: set数据类型(占位符编号,参数数值) 注意: 根据数据库的数据类型调用对应的set方法,占位符的编号从左往右从1开始
  4. 执行sql:如果是增、删、改,调用的是无参executeUpdate或execute方法;查询调用无参executeQuery方法
// 输入用户名和密码
        String sys_userName = "yyy";
        String sys_pwd = "' or 1=1 or ''='";

        //3. 操作数据库
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        String sql = "select * from sys_user where userName = ? and pwd= ?";
        try {
            //1. 创建 PreparedStatement 对象- 把sql提交给对象进行预编译
            statement = connection.prepareStatement(sql);

            //2. 参数插入
            statement.setString(1,sys_userName);// 给第一个站位符填值
            statement.setString(2,sys_pwd);// 给第二个站位符填值

            //查看sql
            String s = statement.toString();
            System.out.println("sql:"+s);

            //查询返回结果集 - 调用无参方法-不需要传入sql
            resultSet = statement.executeQuery();
        } catch (SQLException e) {
            System.err.println("操作数据库异常!");
            e.printStackTrace();
        }

4.在开发过程中使用的是PreparedStatement 接口,不会使用Statement 接口方法

5.JDBC工具封装

java代码

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * JDBC工具类
 */
public class MyJDBCUtil {

    private static String userName;//数据库用户名
    private static String pwd;//数据库密码
    private static String connectionURL;//数据库连接URL地址

    private MyJDBCUtil(){}

    static{
        //1.驱动注册
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.err.println("mysql驱动加载异常!");
            e.printStackTrace();
        }

        //2. 从配置文件中读取用户名和密码配置
        InputStream is = MyJDBCUtil.class.getResourceAsStream("database.properties");
        Properties properties = new Properties();
        try {
            properties.load(is);
        } catch (IOException e) {
            System.err.println("mysql配置加载异常!");
            e.printStackTrace();
        }

        String p_userName = (String) properties.get("userName");
        String p_pwd = (String) properties.get("pwd");
        String p_mysqlURL = (String) properties.get("mysqlURL");

        userName = p_userName;
        pwd = p_pwd;
        connectionURL = p_mysqlURL;
    }

    /**
     * 获取数据库连接
     * @return 数据库连接对象 Connection
     */
    public static Connection getConnection(){

        Connection connection = null;
        try {
            connection = DriverManager.getConnection(connectionURL, userName, pwd);
        } catch (SQLException e) {
            System.err.println("mysql连接异常!");
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * 查询之后的关闭操作
     * @param statement 操作对象
     * @param resultSet 查询结果集
     * @param connection 连接对象
     */
    public static void close(ResultSet resultSet,Statement statement,Connection connection){
        try {
            resultSet.close();
            statement.close();
            connection.close();
        } catch (SQLException e) {
            System.err.println("连接关闭异常");
            e.printStackTrace();
        }
    }

    /**
     * 增删改之后关闭
     * @param statement 操作对象
     * @param connection 连接对象
     */
    public static void close(Statement statement,Connection connection){
        try {
            statement.close();
            connection.close();
        } catch (SQLException e) {
            System.err.println("连接关闭异常");
            e.printStackTrace();
        }
    }

}

配置文件

userName = root
pwd = cxk666
mysqlURL = jdbc:mysql://localhost:3306/javatest?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai

6. 企业级开发包结构要求

区分的目的:为了让开发者清晰项目的内容资源(工具、业务代码、接口、相关依赖)

src
    -- 项目根包:一般是公司域名反写
    	-- 业务包1
    		-- 代码功能包
    	-- 业务包2
    
src
    com.huawei   -- 根目录包结构
    	sys  -- 系统管理业务包
    		utils -- 系统管理业务中的工具类包
    		service -- 系统管理业务中的接口
    			impl -- 系统管理业务中的接口的实现类
    	student
    		...

7. VO\DTO\BO\PO 对象

VO(View Object):视图对象,作用是保存视图组件传递的数据或者给视图传递数据的对象

DTO(Data Transfer Object):万金油,适用于所有服务间数据转发过程

BO(Bussiness Object):业务对象,使用在java代码业务之间传递值

PO(Persistent Object):持久化对象,该对象是和数据库字段一一对应,改对象描述的就是数据库的表数据

/* 对应数据库student表的po对象 */
package com.huawei.sys.POs;

import java.io.Serializable;
import java.util.Date;

public class StudentPO implements Serializable {

    private String sID;
    private String sName;
    private Date sBirth;
    private String sSex;

    public String getsID() {
        return sID;
    }

    public void setsID(String sID) {
        this.sID = sID;
    }

    public String getsName() {
        return sName;
    }

    public void setsName(String sName) {
        this.sName = sName;
    }

    public Date getsBirth() {
        return sBirth;
    }

    public void setsBirth(Date sBirth) {
        this.sBirth = sBirth;
    }

    public String getsSex() {
        return sSex;
    }

    public void setsSex(String sSex) {
        this.sSex = sSex;
    }
}

8. JDBC-连接池

8.1 手工连接池

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

/**
 * JDBC工具类
 */
public class MyJDBCUtil {

    private static String userName;//数据库用户名
    private static String pwd;//数据库密码
    private static String connectionURL;//数据库连接URL地址

    private static LinkedList<Connection> connectorPool; // 连接池

    private MyJDBCUtil(){}

    static{
        //1.驱动注册
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.err.println("mysql驱动加载异常!");
            e.printStackTrace();
        }

        //2. 从配置文件中读取用户名和密码配置
        InputStream is = MyJDBCUtil.class.getResourceAsStream("database.properties");
        Properties properties = new Properties();
        try {
            properties.load(is);
        } catch (IOException e) {
            System.err.println("mysql配置加载异常!");
            e.printStackTrace();
        }

        String p_userName = (String) properties.get("userName");
        String p_pwd = (String) properties.get("pwd");
        String p_mysqlURL = (String) properties.get("mysqlURL");
        int initPoolSize = Integer.valueOf((String) properties.get("initPoolSize"));

        userName = p_userName;
        pwd = p_pwd;
        connectionURL = p_mysqlURL;

        //初始化连接池
        connectorPool = (LinkedList<Connection>) Collections.synchronizedList(new LinkedList<Connection>());
        for(int i=0;i<initPoolSize;i++){
            Connection connection = null;
            try {
                connection = DriverManager.getConnection(connectionURL, userName, pwd);
            } catch (SQLException e) {
                System.err.println("mysql连接异常!");
                e.printStackTrace();
            }
            connectorPool.addLast(connection);
        }

        //启动一个线程监测每个连接是否正常能够访问数据库
        new Thread(()->{
            
            while(true){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                for(int i=0;i<initPoolSize;i++){
                    Connection connection = connectorPool.get(i);
                    Statement statement = null;
                    try {
                        statement = connection.createStatement();
                        statement.execute("select count(*) from dual");
                    } catch (SQLException e) {
                        Connection newConnection = null;
                        try {
                            newConnection = DriverManager.getConnection(connectionURL, userName, pwd);
                        } catch (SQLException e2) {
                            System.err.println("mysql连接异常!");
                            e2.printStackTrace();
                        }
                        connectorPool.remove(i);
                        connectorPool.addLast(newConnection);
                    }
                    
                }
            }
            
        },"poolThread").start();
        // 使用sql:select count(*) from dual; 监测与服务器的连接是否正常,以及测试速度

    }

    /**
     * 获取数据库连接
     * @return 数据库连接对象 Connection
     */
    public static synchronized Connection getConnection(){
        
        Connection last = connectorPool.getLast();
        if(last == null){
            System.err.println("连接池连接暂无,通过创建连接获取新接连");
            Connection connection = null;
            try {
                connection = DriverManager.getConnection(connectionURL, userName, pwd);
            } catch (SQLException e) {
                System.err.println("mysql连接异常!");
                e.printStackTrace();
            }
            return connection;
        }

        return last;

    }

    /**
     * 查询之后的关闭操作
     * @param connection 连接对象
     */
    public static synchronized void close(Connection connection){
        connectorPool.add(connection);
    }

}

8.2 C3P0连接池-mysql版本使用

1.首先下载c3p0的jar包 需要:mchange-commons-java包和c3p0包

2.初始化连接池数据源对象:

​ ComboPooledDataSource dataSource = new ComboPooledDataSource();

3.使用C3P0

8.2.1 在静态代码块中初始化连接池(不推荐使用)

package com.huawei.sys.utils;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * C3P0连接池工具类
 */
public class MyC3P0Util {
    // 连接池数据源对象
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

    private MyC3P0Util(){}

    static{
        //1. 注册驱动
        try {
            dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        } catch (PropertyVetoException e) {
            System.err.println("驱动注册异常");
            e.printStackTrace();
        }
        //2. 设置url 用户名 密码
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/javatest?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai");
        dataSource.setUser("root");
        dataSource.setPassword("cxk666");
    }

    /**
     * C3P0连接池-获取连接的方法
     * @return
     */
    public static Connection getConnection(){
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            System.err.println("获取连接异常!");
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * C3P0归还连接的方法
     */
    public static void close(Connection con){
        try {
            con.close();// C3P0中并不会关闭,是将连接归还到连接池(代理模式:mchange-commons)
        } catch (SQLException e) {
            System.err.println("归还连接异常!");
            e.printStackTrace();
        }
    }

}

8.2.2 使用properties配置文件配置C3P0连接池初始化(推荐使用)

优点: 配置和代码分离,且c3p0是自动完成配置不需要手动编写任何初始化代码

配置文件内容

​ 注意: 配置文件名字必须叫:c3p0.properties ,要放在src目录下,不要放到包结构中

c3p0.driverClass=com.mysql.cj.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/javatest?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
c3p0.user=root
c3p0.password=cxk666
# 初始化连接池的大小
c3p0.initialPoolSize=3  
# 最大连接时间
c3p0.maxIdleTime=60 
# 最大连接池大小
c3p0.maxPoolSize=20 

java工具类代码内容

package com.huawei.sys.utils;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * C3P0连接池工具类
 */
public class MyC3P0Util {
    // 连接池数据源对象
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

    private MyC3P0Util(){}

    //不再需要静态代码块了,当项目启动,数据源对象初始化时会自动找到配置文件初始化连接池

    /**
     * C3P0连接池-获取连接的方法
     * @return
     */
    public static Connection getConnection(){
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            System.err.println("获取连接异常!");
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * C3P0归还连接的方法
     */
    public static void close(Connection con){
        try {
            con.close();// C3P0中并不会关闭,是将连接归还到连接池(代理模式:mchange-commons)
        } catch (SQLException e) {
            System.err.println("归还连接异常!");
            e.printStackTrace();
        }
    }

}

8.2.3 多数据源情况使用-xml配置文件

多数据源?

​ 可能一个项目同时需要多种数据库配置灵活应变

注意:使用properties的配置方法和使用xml配置方法只能选择其中一个使用

​ 使用xml配置:文件名必须叫:c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!-- 默认使用的数据源配置 -->
    <default-config>
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/javatest?useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai
        </property>
        <property name="user">root</property>
        <property name="password">cxk666</property>
    </default-config>

    <!-- 多数据源-其他数据源的配置方法 -->
    <!-- 其他数据源一定要指定name属性 -->
    <named-config name="tengxunyun" >
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://XXXXXXX:3306/XXXX?useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai
        </property>
        <property name="user">XXXXX</property>
        <property name="password">XXXX</property>
    </named-config>

</c3p0-config>

多数据源初始化数据源对象时:

 // 构造器中指定一个数据源的名称,对应的是xml文件中   named-config 标签的 name属性值
private static ComboPooledDataSource dataSource = new ComboPooledDataSource("tengxunyun");

使用连接池案例

public static void main(String[] args) throws SQLException {

        //1. 调用编写的工具类通过C3P0获取连接
        Connection connection = MyC3P0Util.getConnection();

        //2. 创建操作对象查询数据
        Statement statement = connection.createStatement();

        ResultSet set = statement.executeQuery("select s_name from student");

        while(set.next()){
            String s_name = set.getString("s_name");
            System.out.println(s_name);
        }

        //3. 归还连接(代理完成归还,并不是关闭连接)
        connection.close();//归还连接

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神秘的高先生

编写不易,感谢大佬的赏赐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值