Java数据库连接(JDBC)

JDBC(Java DataBase Connectivity)

JDBC概念

JDBC   就是使用Java语言操作关系型数据库的一套API

全称:( Java DataBase Connectivity ) Java 数据库连接

JDBC本质

* 官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口
* 各个数据库厂商去实现这套接口,提供数据库驱动jar包
* 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类

JDBC好处

* 各数据库厂商使用相同的接口,Java代码不需要针对不同数据库分别开发
* 可随时替换底层数据库,访问数据库的Java代码基本不变

在这里插入图片描述

JDBC快速入门

Java操作数据库流程

第一步:编写Java代码

第二步:Java代码将SQL发送到MySQL服务端

第三步:MySQL服务端接收到SQL语句并执行该SQL语句

第四步:将SQL语句执行的结果返回给Java代码

编写代码具体步骤

1.创建工程,导入jar包

	1.创建新的空的项目
	2.定义项目的名称,并指定位置
	3.对项目进行设置,JDK版本、编译版本
	4.创建模块,指定模块的名称及位置
	5.导入驱动包,将mysql的驱动包放在模块下的lib目录(随意命名)下,并将该jar包添加为库文件
	在添加为库文件的时候,有如下三个选项
		Global Library  : 全局有效
   	    Project Library :   项目有效
	    Module Library : 模块有效
    6.在src下创建类

2.编写代码

/**
 * JDBC快速入门
 */
public class JDBCDemo {

    public static void main(String[] args) throws Exception {
        //1. 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接
        String url = "jdbc:mysql://127.0.0.1:3306/db1";
        String username = "root";
        String password = "1234";
        Connection conn = DriverManager.getConnection(url, username, password);
        //3. 定义sql
        String sql = "update account set money = 2000 where id = 1";
        //4. 获取执行sql的对象 Statement
        Statement stmt = conn.createStatement();
        //5. 执行sql
        int count = stmt.executeUpdate(sql);//受影响的行数
        //6. 处理结果
        System.out.println(count);
        //7. 释放资源
        stmt.close();
        conn.close();
    }
}

API详解

DriverManager

作用:1.注册驱动     Class.forName("com.mysql.jdbc.Driver");
        MySQL 5之后的驱动包,可以省略注册驱动的步骤,自动加载jar包中的META-INF/services/java.sql.Driver文件中的驱动类
        
      2.获取数据库连接   static Connection     getConnection(String url,String user,String password)
        参数:url:连接路径
             语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2… 
             细节:1. 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数键值对
			       2.配置 useSSL=false 参数,禁用安全连接方式,解决警告提示

Connection

作用:1.获取执行SQL的对象
     	* 普通执行SQL对象  Statement createStatement()
	    * 预编译SQL的执行SQL对象:防止SQL注入   PreparedStatement  prepareStatement(sql)
        * 执行存储过程的对象  CallableStatement prepareCall(sql)

      2.管理事务,Connection接口中定义了3个对应的方法
      	开启事务:setAutoCommit(boolean autoCommit):true:自动提交事务;false:手动提交事务,即为开启事务
      	提交事务:commit()
      	回滚事务:rollback()
/**
 * JDBC API 详解:Connection
 */
public class JDBCDemo3_Connection {

    public static void main(String[] args) throws Exception {
        //1. 注册驱动
        //Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
        String url = "jdbc:mysql:///db1?useSSL=false";
        String username = "root";
        String password = "1234";
        Connection conn = DriverManager.getConnection(url, username, password);
        //3. 定义sql
        String sql1 = "update account set money = 3000 where id = 1";
        String sql2 = "update account set money = 3000 where id = 2";
        //4. 获取执行sql的对象 Statement
        Statement stmt = conn.createStatement();

        try {
            // ============开启事务==========
            conn.setAutoCommit(false);
            //5. 执行sql
            int count1 = stmt.executeUpdate(sql1);//受影响的行数
            //6. 处理结果
            System.out.println(count1);
            int i = 3/0;
            //5. 执行sql
            int count2 = stmt.executeUpdate(sql2);//受影响的行数
            //6. 处理结果
            System.out.println(count2);

            // ============提交事务==========
            //程序运行到此处,说明没有出现任何问题,则需求提交事务
            conn.commit();
        } catch (Exception e) {
            // ============回滚事务==========
            //程序在出现异常时会执行到这个地方,此时就需要回滚事务
            conn.rollback();
            e.printStackTrace();
        }

        //7. 释放资源
        stmt.close();
        conn.close();
    }
}

Statement

作用:执行SQL语句
	  int executeUpdate(sql):执行DML、DDL语句
	  	返回值:(1)DML语句影响的行数
	  		    (2)DDL语句执行后,执行成功也可能返回0
	  ResultSet  executeQuery(sql):执行DQL语句
	  	返回值:ResultSet结果集对象
/**
  * 执行DML语句
  * @throws Exception
  */
@Test
public void testDML() throws  Exception {
    //1. 注册驱动
    //Class.forName("com.mysql.jdbc.Driver");
    //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
    String url = "jdbc:mysql:///db1?useSSL=false";
    String username = "root";
    String password = "1234";
    Connection conn = DriverManager.getConnection(url, username, password);
    //3. 定义sql
    String sql = "update account set money = 3000 where id = 1";
    //4. 获取执行sql的对象 Statement
    Statement stmt = conn.createStatement();
    //5. 执行sql
    int count = stmt.executeUpdate(sql);//执行完DML语句,受影响的行数
    //6. 处理结果
    //System.out.println(count);
    if(count > 0){
        System.out.println("修改成功~");
    }else{
        System.out.println("修改失败~");
    }
    //7. 释放资源
    stmt.close();
    conn.close();
}
/**
  * 执行DDL语句
  * @throws Exception
  */
@Test
public void testDDL() throws  Exception {
    //1. 注册驱动
    //Class.forName("com.mysql.jdbc.Driver");
    //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
    String url = "jdbc:mysql:///db1?useSSL=false";
    String username = "root";
    String password = "1234";
    Connection conn = DriverManager.getConnection(url, username, password);
    //3. 定义sql
    String sql = "drop database db2";
    //4. 获取执行sql的对象 Statement
    Statement stmt = conn.createStatement();
    //5. 执行sql
    int count = stmt.executeUpdate(sql);//执行完DDL语句,可能是0
    //6. 处理结果
    System.out.println(count);

    //7. 释放资源
    stmt.close();
    conn.close();
}

ResultSet(结果集对象)

作用:封装DQL查询语句结果
	ResultSet stmt.executeQuery(sql) 执行DQL语句,返回ResultSet对象
	
*获取查询结果
	boolean next():1.将光标从当前位置向前移动一行  2.判断当前行是否是有效行
		返回值:true:有效行,当前行有数据  false:无效行,当前行没有数据
	xxx getXxx(参数):获取数据
		xxx:数据类型;比如:int getInt(参数);String getString(参数)
		参数:
			*int:列的编号,从1开始
			*String:列的名称

*使用步骤
	(1)游标向下移动一行,并判断该行是否有数据:next()
	(2)获取数据:getXxx(参数)
 while(rs.next()){
            int id = rs.getInt(1);
            String name = rs.getString(2);
            System.out.println(id);
            System.out.println(name);
           }
案例
需求:查询account账户表数据,封装为Account对象中,并且存储到ArrayList集合中

实体类Account

package com.zyl.pojo;

public class Account {
    private int id;
    private String name;
    private double money;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", money=" + money +
                '}';
    }
}
/**
  * 查询account账户表数据,封装为Account对象中,并且存储到ArrayList集合中
  * 1. 定义实体类Account
  * 2. 查询数据,封装到Account对象中
  * 3. 将Account对象存入ArrayList集合中
  */
@Test
public void testResultSet2() throws  Exception {
    //1. 注册驱动
    //Class.forName("com.mysql.jdbc.Driver");
    //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
    String url = "jdbc:mysql:///db1?useSSL=false";
    String username = "root";
    String password = "1234";
    Connection conn = DriverManager.getConnection(url, username, password);

    //3. 定义sql
    String sql = "select * from account";

    //4. 获取statement对象
    Statement stmt = conn.createStatement();

    //5. 执行sql
    ResultSet rs = stmt.executeQuery(sql);

    // 创建集合
    List<Account> list = new ArrayList<>();
   
    // 6.1 光标向下移动一行,并且判断当前行是否有数据
    while (rs.next()){
        Account account = new Account();

        //6.2 获取数据  getXxx()
        int id = rs.getInt("id");
        String name = rs.getString("name");
        double money = rs.getDouble("money");

        //赋值
        account.setId(id);
        account.setName(name);
        account.setMoney(money);

        // 存入集合
        list.add(account);
    }

    System.out.println(list);

    //7. 释放资源
    rs.close();
    stmt.close();
    conn.close();
}

PreparedStatement

作用:预编译SQL语句并执行:预防SQL注入问题
	*SQL注入:通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法

演示SQL注入

@Test
public void testLogin() throws  Exception {
    //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
    String url = "jdbc:mysql:///db1?useSSL=false";
    String username = "root";
    String password = "1234";
    Connection conn = DriverManager.getConnection(url, username, password);

    // 接收用户输入 用户名和密码
    String name = "sjdljfld";
    String pwd = "' or '1' = '1";
    String sql = "select * from tb_user where username = '"+name+"' and password = '"+pwd+"'";
    // 获取stmt对象
    Statement stmt = conn.createStatement();
    // 执行sql
    ResultSet rs = stmt.executeQuery(sql);
    // 判断登录是否成功
    if(rs.next()){
        System.out.println("登录成功~");
    }else{
        System.out.println("登录失败~");
    }

    //7. 释放资源
    rs.close();
    stmt.close();
    conn.close();
}

防止SQL注入 PrepareStatement

1. 获取 PreparedStatement 对象
// SQL语句中的参数值,使用?占位符替代
String sql = "select * from user where username = ? and password = ?";
// 通过Connection对象获取,并传入对应的sql语句
PreparedStatement pstmt = conn.prepareStatement(sql);
2.设置参数值
	  上面的sql语句中参数使用 ? 进行占位,在之前之前肯定要设置这些 ?  的值。
	  PreparedStatement对象:setXxx(参数1,参数2):给 ? 赋值
 	  * Xxx:数据类型 ; 如 setInt (参数1,参数2)
	   * 参数:
    	 * 参数1: ?的位置编号,从1 开始
         * 参数2: ?的值
3. 执行SQL语句
   executeUpdate();  执行DDL语句和DML语句
   executeQuery();  执行DQL语句
   注意:
  * 调用这两个方法时不需要传递SQL语句,因为获取SQL语句执行对象时已经对SQL语句进行预编译了。
 @Test
public void testPreparedStatement() throws  Exception {
    //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
    String url = "jdbc:mysql:///db1?useSSL=false";
    String username = "root";
    String password = "1234";
    Connection conn = DriverManager.getConnection(url, username, password);

    // 接收用户输入 用户名和密码
    String name = "zhangsan";
    String pwd = "' or '1' = '1";

    // 定义sql
    String sql = "select * from tb_user where username = ? and password = ?";
    // 获取pstmt对象
    PreparedStatement pstmt = conn.prepareStatement(sql);
    // 设置?的值
    pstmt.setString(1,name);
    pstmt.setString(2,pwd);
    // 执行sql
    ResultSet rs = pstmt.executeQuery();
    // 判断登录是否成功
    if(rs.next()){
        System.out.println("登录成功~");
    }else{
        System.out.println("登录失败~");
    }
    //7. 释放资源
    rs.close();
    pstmt.close();
    conn.close();
}

PrepareStatement原理

PreparedStatement 好处:
	* 预编译SQL,性能更高
	* 防止SQL注入:将敏感字符进行转义

Java代码操作数据库流程:
	1.将sql语句发送到MySQL服务器端
	2.MySQL服务端会对sql语句进行如下操作
	3. 检查SQL语句
	    检查SQL语句的语法是否正确。
	4.编译SQL语句。将SQL语句编译成可执行的函数。
		检查SQL和编译SQL花费的时间比执行SQL的时间还要长。如果我们只是重新设置参数,那么检查SQL语句和编译SQL语句将不需要重复执行。这样就提高了性能。
	 5. 执行SQL语句

演示PrepareStatement原理

1.开启预编译功能
	在代码中编写url时需要加上以下参数。而我们之前根本就没有开启预编译功能,只是解决了SQL注入漏洞。
	useServerPrepStmts=true
2.配置MySQL执行日志(重启mysql服务后生效)
	在mysql配置文件(my.ini)中添加如下配置:
	log-output=FILE
	general-log=1
	general_log_file="D:mysql.log"
	slow-query-log=1
	slow_query_log_file="D:mysql_slow.log"
	long_query_time=2
3.java测试代码如下:
/**
   * PreparedStatement原理
   * @throws Exception
   */
@Test
public void testPreparedStatement2() throws  Exception {

    //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
    // useServerPrepStmts=true 参数开启预编译功能
    String url = "jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true";
    String username = "root";
    String password = "1234";
    Connection conn = DriverManager.getConnection(url, username, password);

    // 接收用户输入 用户名和密码
    String name = "zhangsan";
    String pwd = "' or '1' = '1";

    // 定义sql
    String sql = "select * from tb_user where username = ? and password = ?";

    // 获取pstmt对象
    PreparedStatement pstmt = conn.prepareStatement(sql);

    Thread.sleep(10000);
    // 设置?的值
    pstmt.setString(1,name);
    pstmt.setString(2,pwd);
    ResultSet rs = null;
    // 执行sql
    rs = pstmt.executeQuery();

    // 设置?的值
    pstmt.setString(1,"aaa");
    pstmt.setString(2,"bbb");
    // 执行sql
    rs = pstmt.executeQuery();

    // 判断登录是否成功
    if(rs.next()){
        System.out.println("登录成功~");
    }else{
        System.out.println("登录失败~");
    }

    //7. 释放资源
    rs.close();
    pstmt.close();
    conn.close();
}
4.执行SQL语句,查看 `D:mysql.log` 日志

==小结:==
	* 在获取PreparedStatement对象时,将sql语句发送给mysql服务器进行检查,编译(这些步骤很耗时)
	* 执行时就不用再进行这些步骤了,速度更快
	* 如果sql模板一样,则只需要进行一次检查、编译

数据库连接池

简介

1. 数据库连接池是个容器,负责分配、管理数据库连接(Connection)
2.它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;
3. 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
	* 好处
	  * 资源重用
	  * 提升系统响应速度
 	 * 避免数据库连接遗漏
 	
 连接池是在一开始就创建好了一些连接(Connection)对象存储起来。用户需要连接数据库时,
 不需要自己创建连接,而只需要从连接池中获取一个连接进行使用,使用完毕后再将连接对象归还给连接池;
 这样就可以起到资源重用,也节省了频繁创建连接销毁连接所花费的时间,从而提升了系统响应的速度。

实现

 * 标准接口:DataSource
  官方(SUN) 提供的数据库连接池标准接口,由第三方组织实现此接口。该接口提供了获取连接的功能: Connection getConnection()
那么以后就不需要通过 `DriverManager` 对象获取 `Connection` 对象,而是通过连接池(DataSource)获取 `Connection` 对象。

* 常见的数据库连接池
 * DBCP
 * C3P0
 * Druid
 
	* Druid(德鲁伊)
 	 * Druid连接池是阿里巴巴开源的数据库连接池项目 
	  * 功能强大,性能优秀,是Java语言最好的数据库连接池之一

使用

1.导入jar包 druid-1.1.12.jar
	先将druid的jar包放到项目下的lib下并添加为库文件
2. 定义配置文件
配置文件 druid.properties放在src目录下
	driverClassName=com.mysql.jdbc.Driver
	url=jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true
	username=root
	password=1234
	# 初始化连接数量
	initialSize=5
	# 最大连接数
	maxActive=10
	# 最大等待时间
	maxWait=3000
	
3.加载配置文件
4.获取数据库连接池对象
5.获取连接

具体实现:

/**
 * Druid数据库连接池演示
 */
public class DruidDemo {

    public static void main(String[] args) throws Exception {
        //1.导入jar包
        //2.定义配置文件
        //3. 加载配置文件
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        //4. 获取连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);

        //5. 获取数据库连接 Connection
        Connection connection = dataSource.getConnection();
        System.out.println(connection); //获取到了连接后就可以继续做其他操作了

        //System.out.println(System.getProperty("user.dir"));
    }
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>