03-MySQL之JDBC【jdbc使用过程、自定义JdbcUtil类、jdbc事务、sql注入、PreparedStatement类、c3p0和Druid连接池】

本文详细介绍了JDBC的基本使用,包括注册驱动、连接数据库、操作数据库和数据查询,强调了PreparedStatement在防止SQL注入中的作用。此外,文章讨论了自定义JDBCUtil工具类以提高代码复用性和可维护性,并深入讲解了JDBC事务管理和SQL注入问题。最后,文章对比了C3P0和Druid两种连接池,分析了连接池的工作原理和优势,以及它们的配置和使用方法。
摘要由CSDN通过智能技术生成

一、JDBC

1、JDBC简介

jdbc是Sun公司抽取出来的一套操纵数据库的接口规范, 由不同的数据库服务器厂商按照接口规范实现

结论: JDBC就是java连接数据库的桥梁

1、使用相对简单

  • 开发者只需会调用JDBC接口中的方法即可,使用简单;

  • 面向接口编程

2、访问接口实现与开发解耦

  • 底层实现交给具体的数据库厂商来完成,与开发者解耦;

  • 开发者只需按照流程开发即可,无需关注与数据库交互细节;

3、移植性好

  • 对于不同 的数据库,开发者使用同一套Java代码,进行少量的修改就可以访问其他JDBC支持的数据库

  • 一致性相对较好

在这里插入图片描述

2、JDBC核心API使用

在这里插入图片描述

# JDBC相关包
    1) java.sql:`JDBC访问数据库的基础包`,在JavaSE中的包。如:java.sql.Connection
    2) javax.sql:`JDBC访问数据库的扩展包` (x表示extension,扩展`)
    3) `数据库的驱动`,各大数据库厂商来实现。如:MySQL的驱动:com.mysql.jdbc.Driver

# JDBC四个核心对象
 DriverManager()`数据库驱动管理类`。
作用: 1) 注册驱动; 
 	  2) 创建java代码和数据库之间的连接,即获取Connection接口;
 	  
 Connection(接口)`建立数据库连接的一个接口`。
作用:建立数据库和java代码之间的连接。表示与数据库创建的连接对象。

 Statement(接口)`数据库操作接口`,向数据库发送sql语句。执行SQL语句的对象。

 PreparedStatement(接口):继承Statement (`解决安全隐患问题,比如sql注入的问题`)。 

 ResultSet(接口)`是结果集或一张虚拟表`。Statement 发送sql语句,返回的结果 封装在 ResultSet 中。
  1. DriverManager注册驱动程序;

  2. 创建和数据库的连接对象Connection

  3. 由客户端发送SQL语句给服务器执行,SQL语句封装成Statement对象

  4. 查询到的结果集封装成ResultSet对象

  5. 在客户端可以从ResultSet中取出数据,处理结果集;

  6. 释放资源,关闭连接对象

01)JDBC注册驱动
显式注册驱动

通过DriverManager类注册驱动程序。

static void registerDriver(Driver driver);

@Test
public void test1() throws SQLException {
    //1. 显式注册方式
    DriverManager.registerDriver(new Driver());
    
    //2.获取所有注册的驱动 发现mysql的驱动类注册的2次
    Enumeration<java.sql.Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()){
        System.out.println(drivers.nextElement().toString()); 
    }
}

使用显式注册驱动,数据库驱动被注册了2次,是什么原因呢?

查看源码:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
	// Register ourselves with the DriverManager
    static {
        try {
            //在类被加载的时候,静态代码块就注册了一次了
            java.sql.DriverManager.registerDriver(new Driver());
         } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
         }
    }

因为Mysql驱动类Driver在被加载时,静态代码块中已经注册了一次,

如果显式调DriverManager.registerDriver()方法会导致驱动类注册2次,浪费内存。

隐式注册驱动

通过获得Driver的Class对象

Class.forName(“com.mysql.jdbc.Driver”);

@Test
public void test2() throws SQLException, ClassNotFoundException {
    //1. 隐式注册方式
    Class.forName("com.mysql.jdbc.Driver");
    
    //2.获取所有注册的驱动 发现mysql的驱动类注册的1次
    Enumeration<java.sql.Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()){
   	 	System.out.println(drivers.nextElement().toString());
    }
}
02)JDBC连接数据库
java.sql.DriverManager类中有如下方法获取数据库连接
static Connection getConnection(String url, String user, String password);

参数说明:
1.  String url :连接数据库的URL,用于说明连接数据库的位置
2.  String user :数据库的账号
3.  String password :数据库的密码

连接数据库的URL:
     URL是sun公司与数据库厂商之间的一种协议
     格式 : 协议:子协议:/IP:端口号 数据库
	示例: jdbc:mysql://localhost:3306/数据库名称
03)JDBC操作数据库

IDEA与数据库的SQL交互要使用Statement对象完成,

在java.sql.Connection接口中有如下方法获取到Statement对象:

Statement的API

// 获取发送sql语句的对象
Statement createStatement() throws SQLException;
// 执行增删改操作
int executeUpdate(String sql) throws SQLException;
// 执行查询操作
ResultSet executeQuery(String sql) throws SQLException;
04)JDBC数据查询

JDBC通过statement执行查询的sql后,会将查询的结果封装到ResultSet的对象中

ResultSet的原理
    1. ResultSet内部有一个指针, 刚开始记录开始位置;
    2. 调用next方法, ResultSet内部指针会移动到下一行数据;
    3. 我们可以通过ResultSet得到一行数据 getXxx得到某列数据;

例如:
ResultSet rs = stm.executeQuery(select);
rs.next()  // 存在下一条记录,返回true,否则返回false
    id    int 
    name  varchar 
    score double

rs.getXxx获取字段值
    rs.getInt(1);	或者rs.getInt(“id”);		------>1
    rs.getString(2);或者rs.getString(“name”);	------>张三
    rs.getDouble(3);或者rs.getDouble(“score”);------>80

ResultSet获取数据的API是有规律的,在get后面加数据类型

小结
1. jdbc查询使用步骤?
    1) 注册驱动
    2) 获取连接对象
    3) 获取发送sql的对象Statement;
    4) 发送sql,获取结果集; ResultSet rs= stm.executeQuery(sql);
    5) 解析结果集,获取数据
    6) 关闭资源,释放连接(3个close)
        
2. ResultSet如何获取数据?
    1) 调用next()方法获取某一行的记录;
    2) 通过getXxx(字段索引位);getXxx(字段名称);
@Test
public void test7() throws ClassNotFoundException, SQLException {
    //1.注册驱动
    Class.forName("com.mysql.jdbc.Driver");
    
    //2.获取连接对象
    String url="jdbc:mysql://127.0.0.1:3306/daname";
    String user="root";
    String pwd="1234";
    Connection conn = DriverManager.getConnection(url, user, pwd);
    
    //3.获取发送sql的对象
    Statement sm = conn.createStatement();
    
    //4.发送查询sql语句,获取结果集
    String select="select * from user";
    ResultSet rs = sm.executeQuery(select);
    
    //5.解析结果集,获取数据
    while (rs.next()){
        int id = rs.getInt(1);
        String name = rs.getString("username");
        String password = rs.getString(3);
        System.out.println(id+name+password);
     }
    
    //6 释放资源
    rs.close();
    sm.close();
    conn.close();
}
3、自定义JDBCUtil工具类

问题

 之前的jdbc代码存在大量重复

 数据库的用户名、密码等连接参数发生改变,对我们后期的维护是非常繁琐的

解决方案

把jdbc操作数据库的一些常用的方法抽出来,放到一个外部的工具类中

jdbc.properties文件

将配置文件存储在外部文件中,通过java io读取配置文件,这样既可以实现几种配置管理,又避免了java类因为参数的修改的问题,反复进行编译的问题

##配置参数
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.dburl=jdbc:mysql://127.0.0.1:3306/java127_jdbc01
jdbc.userName=root
jdbc.password=1234

JdbcUtil类

public class JdbcUtil {
    private static String driverClass;
    private static String dburl;
    private static String userName;
    private static String password;

    //使用静态代码块,注册驱动,只注册一次即可
    static {
        // 通过Properties load方法
        Properties properties = new Properties();
        try (InputStream in =         	JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");) {
            properties.load(in);

            //1.初始化连接数据库的4大核心参数
            driverClass = properties.getProperty("jdbc.driverClass");
            dburl = properties.getProperty("jdbc.dburl");
            userName = properties.getProperty("jdbc.userName");
            password = properties.getProperty("jdbc.password");
            //2.注册驱动
            try {
                Class.forName(driverClass);
            } catch (ClassNotFoundException e) {
                System.out.println(e.getMessage());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接对象
     */
    public static Connection getConnection() {
        //2.获取连接
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(dburl, userName, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }


    /**
     * 关闭资源
     * @param rs
     * @param stm
     * @param conn
     */
    public static void close(ResultSet rs, Statement stm, Connection conn) {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            System.out.println("rsError:" + e.getMessage());
        }
        try {
            if (stm != null) {
                stm.close();
            }
        } catch (SQLException e) {
            System.out.println("stmError:" + e.getMessage());
        }
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            System.out.println("connError:" + e.getMessage());
        }
    }
}
4、JDBC事务

Connection接口中与事务相关的核心API

// autoCommit为  true:自动提交  flase:手动提交
void setAutoCommit(boolean autoCommit) throws SQLException;
// 提交事务
void commit() throws SQLException;
// 回滚事务
void rollback() throws SQLException;

注意:
 在jdbc事务操作中,事务的控制都是通过Connection对象完成的,当一个完整的业务操作前,我们首先使用conn.setAutoCommit(false)来设置事务手动提交。
默认情况下是true的,表示自动提交事务那么一条sql语句就是一个事务,默认提交事务。如果设置为false,那么表示开启事务,所有的sql语句就会都在一个事务中。
 当业务操作完成之后,如果整个操作没有问题,我们需要使用conn.commit()来提交事务。当然了,如果出现了异常,我们需要使用conn.rollback()撤销所有的操作所以出现异常,需要进行事务的回滚。

准备数据

-- demo:转账业务:a给b转账100

# 创建账号表
create table account(
    id int primary key auto_increment,
    name varchar(20),
    money double
);

# 初始化数据
insert into account values (null,'a',1000);
insert into account values (null,'b',1000);

调用自定义JdbcUtil类测试方法完成事务

@Test
public void test8() {
    Connection conn = JdbcUtil.getConnection();
    Statement stm =null;
    try {
        //1.设置事务手动提交
        conn.setAutoCommit(false);
        //2.获取发送sql的对象Statement
        stm = conn.createStatement();
        //2.a扣款100
        String subMoney="update account set money=money-100 where name='a'";
         stm.executeUpdate(subMoney);
        //b入账100
        String toMoney="update account set money=money+100 where name='b'";
         stm.executeUpdate(toMoney);
        //事务提交
        conn.commit();
     } catch (SQLException e) {
        System.out.println(e.getMessage());
        if(conn!=null){
        try {
            //事务回滚
            conn.rollback();
         } catch (SQLException throwables) {
            throwables.printStackTrace();
         } finally {
            JdbcUtil.close(null,stm,conn);
         }
     }
}
5、SQL注入

 由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL 关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。

 简单来说就是:用户在页面提交数据的时候人为的添加一些特殊字符,使得sql语句的结构发生了变化,最终可以在没有用户名或者密码的情况下进行登录。

调用自定义JdbcUtil类测试方法完成用户登陆

@Test
public void test10() throws SQLException {
    Connection conn = JdbcUtil4.getConnection();
    //获取发送sql的对象
    Statement stm = conn.createStatement();
    ---------------------------------- 
    正确的用户名 + 密码登陆
    /*
    String user="张三";
    String password="1234";
    String loginSql="select * from user where username='"+user+"' and 
    password='"+password+"'";
    */
    //select * from user where username='张三' and password='1234'
   ----------------------------------
    特殊字符拼接【改变原本查询语句的本意】获取信息
    //发送登录的sql查询用户信息
    String user="xxx' or 1=1 -- ";
    String password="xxx";
    String loginSql="select * from user where username='"+user+"' and 		password='"+password+"'";
   
    // 查询变为 select * from user 【得到表中所有数据】
    // select * from user where username='xxx' or 1=1 -- ' and password='1234'
     ----------------------------------
    
    ResultSet rs = stm.executeQuery(loginSql);
    if(rs.next()){
        String username = rs.getString("username");
        String pwd = rs.getString(3);
        System.out.println(username+pwd);
        System.out.println("恭喜:"+username+"登录成功!");
    }else{
    	System.out.println("您输入的用户名或者密码错误!");
    }
    
    //关闭资源
    JdbcUtil4.close(rs,stm,conn);
}
6、PreparedStatement解决SQL注入

PreparedStatement是Statement的子接口,可以防止sql注入问题。

可以通过Connection接口中的**prepareStatement(sql)**方法获得PreparedStatement的对象。

Statement与PreparedStatement的区别

statement 执行 sql不进行预编译,有sql注入的风险

PreparedStatement 执行 sql进行预编译,防止sql注入

步骤1:PreparedStatement  pstmt =  conn.prepareStatement(sql); //需要事先传递sql模板。如果sql需要参数,使用?进行占位

步骤2:设置参数(执行sql之前)
pstmt.setXxx(int index, 要放入的值) 
//根据不同类型的数据进行方法的选择。第一个参数index表示的是?出现的位置。1开始计数,有几个问号,就需要传递几个参数

方法的参数说明:
第一个参数:int index  表示的是问号出现的位置。 问号是从1开始计数
第二个参数:给问号的位置传入的值

步骤3:执行(不需要在传递sql了)
pstmt.executeQuery(); //执行select
pstmt.executeUpdate(); //执行insert,delete,update

再次调用自定义JdbcUtil类测试方法完成用户登陆【防sql注入】

使用PreparedStatement实现用户登录
@Test
public void testLogin() throws SQLException {
     Connection conn = JdbcUtil.getConnection();
     //1.获取发送sql的预编译对象
     String sql = "select * from user where username=? and password=?";
     PreparedStatement pstm = conn.prepareStatement(sql);
    
     //2.设置参数 
     //pstm.setString(1,"liuyan");
     pstm.setString(1, "xxx' or 1=1 -- ");
     pstm.setString(2, "xxx");
    
     // 3.发送参数,执行sql,获取查询结果集
     ResultSet rs = pstm.executeQuery();
     System.out.println(rs.getRow());
     // 4.关闭资源
     JdbcUtil.close(null, pstm, conn);
}
7、JDBC调用存储过程(了解)

JDBC调用存储过程API

1)获取连接Connection的对象

2)使用连接Connection的对象调用连接Connection的对象接口中的方法获取CallableStatement接口对象

JDBC调用存储过程API
1.获取发送存储过程语句的Statement
• Connection中定义了获取存储过程的Statement方法API;
• CallableStatement prepareCall(String sql) throws SQLException;
• 其中SQL模板格式:String sql="{call 存储过程名(?,?, ...)}";
    
2.IN入参设置方法
void setString(int parameterIndex, String x) throws SQLException;
• Eg: void setString(int parameterIndex, String x) throws SQLException;
•说明:parameterIndex表示SQL模板中参数索引位,第二个参数是传入的具体值;
    
3.OUT出参设置
void registerOutParameter(int parameterIndex, int sqlType) throws SQLException;
•说明:第一个参数表示SQL模板中参数索引位,第二个参数表示出参的类型,可使用java.sql.Types类获取指定类型

4.调用存储过程
boolean execute() throws SQLException;

5.获取出参
int getInt(int parameterIndex) throws SQLException;
•说明:paramterIndex表示出参在SQL模板中的索引位;

JDBC 存储过程

delimiter $
CREATE PROCEDURE transfer (
	IN fromSub VARCHAR (20),
	IN toSub VARCHAR (20),
	IN m FLOAT,
	OUT flag INT
)
BEGIN
	DECLARE
		update_line1 INT DEFAULT 0 ; DECLARE
			update_line2 INT DEFAULT 0 ; -- 开启事务
			START TRANSACTION ; -- fromSub扣款
			UPDATE account SET money = money - m WHERE NAME = fromSub ; -- 获取扣款受影响的行数
			SELECT row_count() INTO update_line1 ; -- toSub收款
			UPDATE account SET money = money + m WHERE NAME = toSub ; 
      SELECT row_count() INTO update_line2 ;
			IF update_line1 > 0 AND update_line2 > 0 THEN
					-- 事务提交
					COMMIT ;
				SET flag = 1 ;
			ELSE
					-- 事务回滚
					ROLLBACK ;
				SET flag = 0 ;
			END IF; 
end$

##调用存储过程
call transfer('tom','rose',500,@flag);
select @flag;

代码演示

public class TransferProcedure { 
    public static void main(String[] args) throws SQLException { 
    	//1.获取连接对象 
        Connection conn = JDBCUtil2.getConnection(true); 
        //2.获取操纵存储过程的预编译对象 
        String sql="{call transfer(?,?,?,?)}";
        CallableStatement cstm = conn.prepareCall(sql); 
        //3.设置参数 
        //3.1 设置入参 
        cstm.setString(1,"a");
        cstm.setString(2,"b"); 
        cstm.setFloat(3,100f); 			
        //3.2注册出参
        cstm.registerOutParameter(4, Types.INTEGER); 
        //4.发送参数,执行存储过程 
        cstm.execute(); 
        //5.获取出参 
        int flag = cstm.getInt(4); 
        System.out.println(flag); 
        //6.关闭资源 
        JDBCUtil2.close(null,cstm,conn); 
    } 
}
8、三层开发业务分析

在这里插入图片描述

`1、开发中,常使用分层思想
    1) 不同的层次结构分配不同的解决过程,各个层次间组成严密的封闭系统
    2) 不同层级结构彼此平等
    3) 分层的目的是:
        a:解耦,就是降低代码之间的依赖关系。
        b:可维护性,哪一层出现问题,直接维护哪一层。
        c:可扩展性,哪一层需要添加代码,直接添加即可。
        d:可重用性,一个方法可以被其它层重复调用。
        
`2、不同层次,使用不同的包表示
    1)com.testdemo.web web层 公司域名倒写。和前端页面直接交互。
    2)com.testdemo.service service层。也叫做业务层。专门用来处理业务的,比如事务。
    3)com.testdemo.dao dao层。数据处理层。
    操作数据库的代码都书写在这一层。直接和数据库交互。
    4)com.testdemo.domain/entity/bean/pojo javabean 存放实体类。临时存放数据
    5)com.testdemo.utils 存放工具类。

`三层架构好处: 
    1)各个层,比如web,service,dao层解耦; 
    2) 提高了代码的复用性;
    3)提高了可维护性;
    
`jdbc属于那一层
    dao层(持久层)
9、执行DQL查询结果封装成集合的操作

在实际开发中,中小型项目都遵循三层架构开发,

jdbc从数据库持久层获取数据后,都会封装成一个对象返还给逻辑层应用
在这里插入图片描述

# 1. 定义 User实体类
public class User { 
    private Integer id; 
    private String userName; 
    private String password; 
    //setter and getter and toString() 
    
    # 2. 查询结果封装成集合
    @Test 
    public void test5() throws ClassNotFoundException, SQLException { 
        //1)注册驱动 
        Class.forName("com.mysql.jdbc.Driver"); 
        //2)获取连接 
        String url="jdbc:mysql://localhost:3306/dbname"; 
        String user="root"; 
        String pwd="1234"; 
        Connection conn = DriverManager.getConnection(url, user, pwd); 
        //3)获取发送sql的对象
        Statement Statement stm = conn.createStatement(); 
        //4)发送select查询语句 
        String sql="select * from user"; ResultSet rs = stm.executeQuery(sql); 			
        //5)解析rs结果集,获取数据
        ArrayList<User> users = new ArrayList<>();
       
        while (rs.next()){ 
            int id = rs.getInt(1); 
            String username = rs.getString("username"); 
            String password = rs.getString("password"); 
            User us = new User(); 
            us.setId(id); 
            us.setUserName(username); 
            us.setPassword(password); 
            users.add(us);
        }
        
        // users.forEach(u-> System.out.println(u.toString()));     
        System.out.println(users); 
        
        //6)释放资源 
        rs.close(); 
        stm.close(); 
        conn.close();
    }
}

10、连接池
01)单次创建连接的问题

1、conn底层基于tcp/ip,连接非常耗时;

2、每次连接完毕,资源都要释放,性能开销大;

3、连接资源的复用性差;

02)连接池解决现状问题的原理

1、操作数据库都需要创建连接,操作完成还需要关闭连接

2、创建连接和关闭连接需要可能比执行sql需要的时间都长

3、一个网站需要高频繁的访问数据库,如果短时间频繁的访问数据库服务器,

就容易造成服务器的宕 机,即死机。

04)连接池好处

连接池中保存了一些数据库连接,这些连接是可以重复使用的。节省数据库的资源消耗

`1.连接池的好处
    1)提高了【连接对象的复用性】
    2)避免了数据库连接对象反复的创建与销毁带来的【性能开销】
    3)提高了【数据库的性能】 
    4)从客户端角度看【避免了oom】(out of memory error) 
    
`2.连接池的原理
    1) 初始化一定数量的数据库连接对象; 
    2)客户端不需要自己创建连接对象,直接从连接池中获取连接对象,直接使用即可; 
    3)客户端使用完毕后,归还连接对象到连接池;
05)常用连接池的介绍

javax.sql.DataSource 表示数据库连接池

DataSource本身只是Sun公司提供的一个接口,没有具体的实现,

它的实现由连接池的数据库厂商去实现。只需要学习这个工具如何使用即可。

public interface DataSource { Connection getConnection(); }

06)常用的连接池实现组件

德鲁伊Druid连接池:

Druid是阿里巴巴开源平台上的项目,整个项目由数据库连接池插件框架和SQL解析器组成。

该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求。

C3P0数据库连接池

是一个开源的JDBC连接池,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有

HibernateSpring等C3P0有自动回收空闲连接功能。

DBCP(DataBase Connection Pool)数据库连接池

是Apache上的一个Java连接池项目。dbcp没有自动回收空闲连接的功能。

07)C3P0连接池

C3P0 是一个开源的JDBC连接池,目前spring 和 hibernate框架对C3P0是支持的。

使用c3p0数据库连接池之前,首先需要在资料中找到如下的jar包,加载到项目中。

常用的配置参数解释

在这里插入图片描述

API:ComboPooledDataSource类

com.mchange.v2.c3p0.ComboPooledDataSource

表示C3P0的连接池对象,常用2种创建连接池的方式

1.无参构造,使用默认配置 ,

2.有参构造,使用命名配置

// 无参构造使用默认配置(使用xml中default-config标签中对应的参数) 
public ComboPooledDataSource() 

//有参构造使用命名配置(configName:xml中配置的名称,使用xml中named-config标签中对应的参数
public ComboPooledDataSource(String configName) 

// 从连接池中取出一个连接 
public Connection getConnection() throws SQLException 
c3p0使用步骤

1.导入jar包 c3p0-0.9.1.2.jar

2.编写 c3p0-config.xml 配置文件,配置对应参数

3.将配置文件放在src目录下

注意事项

C3P0配置文件名称必须为 c3p0-config.xml

C3P0命名配置可以有多个

4. 创建连接池对象`ComboPooledDataSource`,使用默认配置或命名配置
// 1.创建c3p0连接池对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
5. 从连接池中获取连接对象
// 2.获取连接对象
Connection conn = dataSource.getConnection();
6. 使用连接对象操作数据库,与正常jdbc操纵方式一致
7. 关闭资源,归还连接资源
rs.close();
stm.close();
conn.close(); //将连接对象归还给连接池,并不是关闭连接对象

编写 c3p0-config.xml 配置文件,配置对应参数

2) 在数据源构造器中指定数据库开发环境
//创建c3p0连接池对象,指定数据源名称
ComboPooledDataSource dataSource = new ComboPooledDataSource("dev");
<?xml version="1.0" encoding="utf-8" ?> 
<!--c3p0根标签,所有的配置都要放在这个标签内部-->
<c3p0-config> 
    <!--默认指定的数据源环境-->
	<default-config> 
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///dbname</property> 
        <property name="user">root</property> 
        <property name="password">1234</property> 
        <property name="initialPoolSize">10</property> 
        <property name="maxIdleTime">30</property> 
        <property name="maxPoolSize">100</property> 
        <property name="minPoolSize">10</property> 
    </default-config> 
    
    <!-- 开发环境数据源 -->
    <named-config name="dev">
    	<property name="driverClass">com.mysql.jdbc.Driver</property>
         <property name="jdbcUrl"> jdbc:mysql://127.0.0.1:3306/dbname</property>
         <property name="user">root</property>
         <property name="password">1234</property>
         <property name="initialPoolSize">4</property>
    </named-config>
</c3p0-config>

c3p0连接池常用的配置参数说明:

initialPoolSize : 初始连接数 刚创建好连接池的时候准备的连接数量

maxPoolSize : 最大连接数 连接池中最多可以放多少个连接

checkoutTimeout : 最大等待时间 连接池中没有连接时最长等待时间

maxIdleTime : 最大空闲回收时间 连接池中的空闲连接多久没有使用就会回收

08、Druid连接池

Druid是阿里巴巴开发的号称为监控而生的数据库连接池(可以监控访问数据库的性能),Druid是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。如:一年一度的双十一活动,每年春运的抢火车票。

核心类:DruidDataSourceFactory

获取数据源的方法:使用com.alibaba.druid.pool.DruidDataSourceFactory类中的静态方法:

// 创建一个连接池,连接池的参数使用properties中的数据

public static DataSource createDataSource(Properties properties);

在这里插入图片描述

Druid连接池的开发步骤

1、导包

2、下创建一个properties文件,文件名随意,设置对应参数

Druid连接池在创建的时候需要一个Properties对象来设置参数

Druid连接池的配置文件名称随便,放到src目录或者项目根目录下面。

加载druid.properties文件内容。

# 数据库连接参数
url=jdbc:mysql://localhost:3306/dbname
username=root
password=1234
driverClassName=com.mysql.jdbc.Driver
3、加载properties文件的内容到Properties对象中
InputStream in = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
Properties prop = new Properties();
prop.load(in);

4、创建DRUID连接池,使用配置文件中的参数
//使用DruidDataSourceFactory工厂类构建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);

5) 从DRUID连接池中取出连接
//从连接池对象中获取连接对象
Connection conn = dataSource.getConnection();

6) 执行SQL语句
    
7) 关闭资源,归还连接
rs.close();
stm.close();
conn.close(); //将连接对象归还给连接池
总结
  • JDBC技术

    • jdbc:java数据库连接技术

      • sun公司为了统一,制定jdbc规范(接口)
        • jdbc核心API:
          • DriverManager类(统一管理各数据库的驱动包)
          • Connection接口
          • Statement接口
            • 子接口:PreparedStatement
          • ResultSet接口
      • 各大数据库厂商:
        • 基于sun公司制定的jdbc规范,编写实现类(实现接口)
        • 把编写的实现类,打包:jar文件
    • jdbc开发流程:
      1、导入数据库厂商提供的jar文件
      2、注册驱动
          Class.forName("驱动类");//驱动类统一由DriverManager管理
      3、连接数据库
          Connection con=DriverManager.getConnection("连接数据库的url","登录名","密码");
      4、创建给数据库发送sql语句的对象
          Statement stmt = con.createStatement();
      5、给数据库发送sql语句, 并接收sql执行的结果
          int i = stmt.executeUpdate("insert ...  /  update....   / delete ....");
          ResultSet rs  = stmt.executeQuery("select .....");
      6、处理结果
          while(rs.next())//获取结果集的下一行记录。有记录:true
          {
             int id =  rs.getInt("表中字段名");
             String name = rs.getString("表中字段名");
             ......
          }
      7、关闭资源  
          rs.close();//关闭结果集
          stmt.close();//关闭发送sql的对象
          conn.close();//关闭连接
      
    • //CRUD:增删改查
      //Statement对象存在的弊端: 存在sql注入问题
        //解决方案: 使用子接口PreparedStatement (发送sql语句的对象)
                    //特殊之处:创建对象时需要指定sql语句(功能:预编译sql语句)
                    //提供的额外功能:占位符   ?
      
      //注册驱动
      Class.forName("驱动类");
      //连接数据库
      Connection con=DriverManager.getConnection("连接数据库的url","登录名","密码");
      
      //创建给数据库发送sql语句的对象
      PreparedStatement pstmt = con.prepareStatement("delete from 表名 where id=?");
      
      //给sql语句中的占位符,赋值:
      pstmt.setInt(1,100);//给第1个占位符赋值为100
      
      //给数据库发送sql语句, 并接收sql执行的结果
      int i = pstmt.executeUpdate();
      
      
      //关闭资源  
      rs.close();//关闭结果集
      stmt.close();//关闭发送sql的对象
      conn.close();//关闭连接
      
    • //事务操作
      //默认jdbc事务:自动提交
      //手动方式: setAutoCommit(false);
      
      //注册驱动
      Class.forName("驱动类");
      //连接数据库
      Connection con=DriverManager.getConnection("连接数据库的url","登录名","密码");
      
      /*设置事务为手动提交*/
      con.setAutoCommit(falase);
      
      //创建给数据库发送sql语句的对象
      PreparedStatement pstmt = con.prepareStatement("delete from 表名 where id=?");
      
      //给sql语句中的占位符,赋值:
      pstmt.setInt(1,100);//给第1个占位符赋值为100
      
      //给数据库发送sql语句, 并接收sql执行的结果
      int i = pstmt.executeUpdate();
      
      /* 事务提交 或 事务的回滚 */
      if(i>0){
          con.commit();
      }
      
      //关闭资源  
      rs.close();//关闭结果集
      stmt.close();//关闭发送sql的对象
      cone.close();//关闭连接
      
  • 连接池技术

    • 核心思想:提前创建好一些Connection对象,存放到一个容器中

      • 使用时从容器中获取一个Connection对象,用完归还到容器中(复用性)
    • 连接池技术:

      • C3P0

        • 1、导入C3P0需要的jar文件
          2、在src目录下创建:c3p0-config.xml (文件名是固定名称,不能改变)
          3、在xml文件中编写配置参数 (驱动、url、用户名、密码, 初始化连接数、....4、创建连接池对象
              javax.sql.DataSource ds = new ComboPooledDataSource();
          
      • Druid

        • 1、导入Druid需要的jar文件
          2、在src目录下创建xxx.properties文件(文件名任意)
          3、创建Properties对象,加载xxx.properties文件
          4、创建连接池对象
             javax.sql.DataSource ds = DruidDataSourceFactory.createDateSource(properties对象) 
          
    • //利用连接池对象,获取一个Connection对象
      Connection con = 连接池对象.getConnection();
      
      //创建给数据库发送sql语句的对象
      PreparedStatement pstmt = con.prepareStatement("delete from 表名 where id=?");
      
      //给sql语句中的占位符,赋值:
      pstmt.setInt(1,100);//给第1个占位符赋值为100
      
      //给数据库发送sql语句, 并接收sql执行的结果
      int i = pstmt.executeUpdate();
      
      //关闭资源  
      rs.close();//关闭结果集
      stmt.close();//关闭发送sql的对象
      conn.close();//关闭连接(把Connection对象归还到连接池中)
      
1)jdbc开发流程:
     1.注册驱动
       Class.forName(驱动类的全限定名称);
     2.获取连接对象
       conn=DriverManager.getConnection(url,user,pwd);
     3.获取发送sql的对象
       pstm= conn.prepareStatement(sql模板);//防止sql注入
       stm=conn.createStatement();//会有sql注入的问题
       CallableStatement cstm=conn.prepareCall("{call 存储过程名称(?,?,...)}");
      4.发送参数或者sql
        1)发送sql: 
        executeQuery(sql); // select
        executeUpdate(sql);// insert delete update
        execute();
		2)发送参数
        //设置参数
		比如:pstm.setXX(参数索引位,值)executeQuery();
		executeUpdate();
       5.获取结果
           1)返回受影响的行数
           2)返回结果集ResultSet
            next();//true/false
            getXX(字段索引位,从1开始);
            getXX(字段名称);
       6.释放资源,关闭连接
2)jdbc之事务
  jdbc默认事务自动提交;
  conn.setAutoCommit(false);//设置事务手动提交
  conn.commit();
  conn.rollback();
  
3)连接池
常用的参数:
    1)数据库的4大参数‐‐ url  driverclass  user  pwd
    2) 初始化大小 最大连接数 最小连接数 等待时间等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

编程小栈

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值