JavaWeb-JDBC

学完了Java SE部分的内容后,继续学习Java EE。学习JavaWeb以及一些主流的开发框架如SSM,SpringBoot,SpringClound等。首先来学习JDBC,一个与数据库连接相关的API。
在这里插入图片描述

一、JDBC

1、概述

1)概念
使用Java语言操作关系型数据库的一套API

JDBC(Java DataBase Connectivity),即Java数据库连接

2)本质

  • 一套操作所有关系型数据库的规则,即接口
  • 由各数据库厂商去实现这套接口,提供数据库驱动jar包(驱动即实现类)
  • 面向接口(JDBC)编程,真正执行代码的是驱动jar包中的实现类

3)好处

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

2、基本原理

2.1 使用步骤
1.创建工程,导入驱动jar包
- 注意要Add as Library将jar包添加到 java项目中,才会生效
2.注册驱动
- class.forName("com.mysql.jdbc.Driver"); (用到反射)
3.获取连接
- Connection conn=DriverManager.getConnecction(url,username,password);
4.定义SQL语句
- String sql="update...";
5.获取执行SQL对象
- Statement stmt=conn.createStatement();
6.执行SQL
- stmt.executeUpdate(sql);
7.处理返回结果
8.释放资源

流程小结:
导入jar包---->注册驱动---->获取连接---->定义SQL---->获取对象---->执行SQL---->返回结果---->释放资源
2.2 代码示例

1)数据库准备
在数据库管理工具Navicat下创建一个数据库db1和表account,表中定义了id,name,money三个字段,查询表中的数据如下
在这里插入图片描述

2)使用Java操作数据库(JDBC数据库连接)
IDEA右键src,new一个Java Class,命名为com.itweb.jdbc.JDBCDemo,这样使层级结构更加清晰,方便管理不同功能的类。

package com.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
/**
 * jdbc操作步骤
 */
public class jdbcDemo {
    public static void main(String[] args) throws Exception {
        // 注册驱动
        Class.forName("com.mysql.jdbc.Driver"); // 将类加载进内存
        // 获取连接
        String url="jdbc:mysql://127.0.0.1:3306/db1";
        String username="root";
        String password="123456";
        Connection conn = DriverManager.getConnection(url, username, password);
        // 定义sql语句
        String sql="UPDATE account SET money=500 WHERE id=1";
        // 获取执行sql对象
        Statement stmt = conn.createStatement();
        // 执行sql
        int count = stmt.executeUpdate(sql); // 受影响的行数
        // 返回处理结果
        System.out.println(count);
        // 释放资源
        stmt.close();
        conn.close(); // 先开后关
    }
}

3)查看运行结果
运行结果如下图
在这里插入图片描述

此时再返回Navicat查询结果
在这里插入图片描述

注:可能有的同学在java那边操作完成后,回来直接通过点击表的方式来查看,发现没变化,这是因为没有刷新。右键选择刷新即可。

在这里插入图片描述

3、相关API

3.1 DriverManager(驱动管理类)

主要有两个作用

1.注册驱动
- Class.forName("com.mysql.jdbc.Driver");  感兴趣的可ctrl+B查看Driver类源码
2.获取数据库连接
DriverManager.getConnection(url, username, password);

相关说明
1)url:连接路径(jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2...)
如:"jdbc:mysql://127.0.0.1:3306/db1",如果连接的是本机mysql服务器(ip为127.0.0.1,也可写为localhost),
且mysql服务默认端口为3306,则以上可以简写为 "jdbc:mysql:///db1";
2)配置useSSL=false参数,禁用安全连接方式,解决警告问题。
3.2 Connection(数据库连接对象)

主要作用

1)获取执行SQL的对象
- 普通执行SQL对象:Statement createStatement()
- 预编译SQL的执行SQL对象,防止SQL注入:PreparedStatement prepareStatement(sql)
- 执行存储过程的对象:CallableStatement prepareCall(sql)
2)管理事务(重点)
- mysql事务管理
    - 开启事务:begin;/start transaction;
    - 提交事务:commit
    - 回滚事务:rollback
- jdbc事务管理:Connection接口中定义了3个对应的方法(使用try-catch异常处理机制)
    - 开启事务:setAutoCommit(Boolean autoCommit)true为自动提交事务;false为手动提交事务,即为开启事务
    - 提交事务:commit()
    - 回滚事务:rollback()
由上,只要单独记一下两者的开启事务,提交和回滚相同
3.3 Statement(执行SQL语句)

主要作用

1int excuteUpdate(sql):执行DMLDDL语句
- 返回值
    - DML语句影响的行数   
    - DDL语句执行后,执行成功也可能返回0
2ResultSet excuteQuery(sql):执行DQL语句
- 返回值
    - ResultSet结果集对象

拓展(DDL和DML):

DML(Data Manipulation Language)数据操作语言-数据库的基本操作,SQL中处理数据等操作统称为数据操纵语言,简而言之就是实现了基本的“增删改查”操作。常用关键字:select、update、delete、insert、merge

DDL(Data Definition Language)数据定义语言-用于定义和管理 SQL 数据库中的所有对象的语言,对数据库中的某些对象(例如,database,table)进行管理。关键字:create、alter、drop、truncate、comment、grant、revoke

区别:① DML操作是可以手动控制事务的开启、提交和回滚的。② DDL操作是隐性提交的,不能rollback!

3.4 ResultSet(结果集对象)

API文档解释

ResultSet对象维护指向其当前数据行的游标最初,光标位于第一行之前。 next方法将光标移动到下一行,并且因为当ResultSet对象中没有更多行时它返回false ,它可以在while循环中用于迭代结果集。

主要作用

1)封装了DQL查询语句的结果:ResultSet  
stmt.executeQuery(sql)
- 执行DQL语句,返回ResultSet对象

2)获取查询结果
boolean next()
- 将光标从当前位置向前移动一行
- 判断行当前是否为有效行(是否有数据)

[数据类型] get[数据类型](参数):获取数据,如 
int getInt(参数); 
String getString(参数)
其中int为列编号(1开始)String为列名称
3.5 PreparedStatement(预编译SQL对象)

API文档解释

是一个接口,继承自Statement,表示预编译SQL语句的对象

主要作用:预编译SQL语句并执行,预防SQL注入问题

SQL注入:通过操作输入来修改事先定义好的SQL语句,来达到执行代码对服务器进行攻击的方法

防止SQL注入步骤(重点)

1)获取PreparedStatement对象
- SQL语句中的参数值用占位符?替代
String sql="SELECT * FROM tb_user WHERE username=? AND password=?";

- 通过Connection对象的获取,并传入对应的sql语句
PreparedStatement pstmt=conn.prepareStatement(sql)

2)设置参数值
PreparedStatement对象:setXxx(参数1,参数2):给?赋值
Xxx:数据类型;如setInt(参数1,参数2)
参数:
- 参数1:?的位置编号,从1开始
- 参数2:?的值

3)执行SQL
executeUpdate(); / executeQuery();  不需要传递sql

防止SQL注入的好处(了解)

1.预编译SQL,性能更高
2.防止SQL注入,将敏感字符进行转义
3.6 代码示例

1)演示Connection

package com.itweb.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
 * jdbc API Connection类
 */
public class JDBCDemo {
    public static void main(String[] args) throws Exception {
        // 注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver"); // 将类加载进内存
        // 获取连接
        String url="jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";
        String username="root";
        String password="123456";
        Connection conn = DriverManager.getConnection(url, username, password);
        // 定义sql语句
        String sql1="UPDATE account SET money=1000 WHERE id=1";
        String sql2="UPDATE account set money=2000 WHERE id=3";
        // 获取执行sql对象
        Statement stmt = conn.createStatement();
        try {
            // 开启事务
            conn.setAutoCommit(false); // 手动提交事务
            // 执行sql
            int count1 = stmt.executeUpdate(sql1); // 受影响的行数
            // 返回处理结果
            System.out.println(count1);
            int i=3/0; // 制造异常
            int count2 = stmt.executeUpdate(sql2); // 受影响的行数
            System.out.println(count2);
            // 提交事务
            conn.commit();
        } catch (SQLException e) {
            // 出现异常则回滚事务
            conn.rollback();
            e.printStackTrace();
        }
        // 释放资源
        stmt.close();
        conn.close(); // 先开后关
    }
}

运行结果

1
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.itweb.jdbc.JDBCDemo.main(JDBCDemo.java:31)

回去刷新数据库表,发现原来的数据没有被更改,这样保证了在任意一个出现异常时都不会提交事务而是回滚。很好理解,比如自己可以思考一下生活中银行转账的例子。
2)演示Statement

package com.itweb.jdbc;

import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
/**
 * jdbc API Statement类
 * 单元测试,执行DML,DDL语句
 */
public class JDBCDemo {
    @Test
    public void testDML() throws Exception {
        // 注册驱动
        //Class.forName("com.mysql.jdbc.Driver"); // 将类加载进内存 (可以省略)
        // 获取连接
        String url="jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";
        String username="root";
        String password="123456";
        Connection conn = DriverManager.getConnection(url, username, password);
        // 定义sql语句
        String sql="UPDATE account SET money=3000 WHERE id=1";
        // 获取执行sql对象
        Statement stmt = conn.createStatement();
        // 执行sql语句
        int count=stmt.executeUpdate(sql); // 执行完DML语句后,返回受影响的行数
        // 返回处理结果
//        System.out.println(count); // 打印结果
        if(count>0){
            System.out.println("修改成功!"); // 以输出提示的方式,易于用户理解
        }
        else {
            System.out.println("修改失败!");
        }
        // 释放资源
        stmt.close();
        conn.close(); // 先开后关
    }
    @Test
    public void testDDL() throws Exception{ // 单元测试,执行DML,DDL语句
        // 注册驱动
        //Class.forName("com.mysql.jdbc.Driver"); // 将类加载进内存 (可以省略)
        // 获取连接
        String url="jdbc:mysql:///db1?useSSL=false";
        String username="root";
        String password="123456";
        Connection conn = DriverManager.getConnection(url, username, password);
        // 定义sql语句
//        String sql="CREATE DATABASE db2"; // 创建好后注释掉,防止再创建
        String sql="DROP DATABASE db2";
        // 获取执行sql对象
        Statement stmt = conn.createStatement();
        // 执行sql语句
        int count=stmt.executeUpdate(sql); // 执行完DML语句后,返回受影响的行数
        // 返回处理结果
//        System.out.println(count); // 打印结果
//        if(count>0){
//            System.out.println("修改成功!"); // 以输出提示的方式,易于用户理解
//        }
//        else {
//            System.out.println("修改失败!");
//        }
        System.out.println(count); // 0
        // 释放资源
        stmt.close();
        conn.close(); // 先开后关
    }
}

运行结果说明
运行testDML()方法前,数据库表的数据
在这里插入图片描述

运行testDML()方法,如下,再回去刷新数据库,发现id为1的用户的money修改成功。
在这里插入图片描述

在这里插入图片描述

运行testDDL()方法,如下,再回去刷新数据库,发现db2已经成功被删除。
在这里插入图片描述

3)演示ResultSet

package com.itweb.jdbc;

import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
 * jdbc API ResultSet类
 * 执行DQL语句
 */
public class JDBCDemo {
    @Test
    public void testResultSet() throws Exception{
        // 注册驱动
        //Class.forName("com.mysql.jdbc.Driver"); // 将类加载进内存 (可以省略)
        // 获取连接
        String url="jdbc:mysql:///db1?useSSL=false";
        String username="root";
        String password="123456";
        Connection conn = DriverManager.getConnection(url, username, password);
        // 定义sql语句
        String sql="SELECT * FROM `account`";
        // 获取执行sql对象
        Statement stmt = conn.createStatement();
        // 执行sql语句
        ResultSet rs=stmt.executeQuery(sql);
        // 处理结果,遍历rs中的所有数据
        while (rs.next()){
//            int id = rs.getInt(1);
//            String name = rs.getString(2);
//            double money = rs.getDouble(3);
            // 使用重载的方法(结果也是一样)
            int id = rs.getInt("id");
            String name = rs.getString("name");
            double money = rs.getDouble("money");
            // 打印结果
            System.out.println(id);
            System.out.println(name);
            System.out.println(money);
            System.out.println("-----------");
        }
        // 释放资源
        rs.close();
        stmt.close();
        conn.close();
    }
}

运行结果
在这里插入图片描述

4)ResultSet综合案例
需求:查询account账户表数据,封装为Account对象,并储存到ArrayList集合中。
首先需要创建一个Account类

package com.itweb.pojo;

public class Account {
    // 封装成员属性,注意数据类型要和数据库定义的一样
    private int id; 
    private String name;
    private double money;

    // 提供对应的get和set方法
    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() { // 提供toString()方法,方便查看
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

然后再编写测试类

package com.itweb.jdbc;

import com.itweb.pojo.Account;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

/**
 * jdbc API ResultSet类
 * 执行DQL语句
 */
public class JDBCDemo {
    @Test
    public void testResultSet() throws Exception{
        // 注册驱动
        //Class.forName("com.mysql.jdbc.Driver"); // 将类加载进内存 (可以省略)
        // 获取连接
        String url="jdbc:mysql:///db1?useSSL=false";
        String username="root";
        String password="123456";
        Connection conn = DriverManager.getConnection(url, username, password);
        // 定义sql语句
        String sql="SELECT * FROM `account`";
        // 获取执行sql对象
        Statement stmt = conn.createStatement();
        // 执行sql语句
        ResultSet rs=stmt.executeQuery(sql);

        // 创建集合,用于存储Account对象中的数据,定义泛型为Account类
        List<Account> list=new ArrayList<>();

        // 处理结果,遍历rs中的所有数据
        while (rs.next()){
            // 创建Account对象
            Account account=new Account();

            int id = rs.getInt("id");
            String name = rs.getString("name");
            double money = rs.getDouble("money");
            // 调用set方法将账户表中的数据传入到Account对象中
            account.setId(id);
            account.setName(name);
            account.setMoney(money);
            // 将Account对象中的数据添加进集合
            list.add(account);
        }
        // 打印集合,输出结果
        System.out.println(list);
        // 释放资源
        rs.close();
        stmt.close();
        conn.close();
    }
}

运行结果
在这里插入图片描述

5)用户登录案例+演示SQL注入(使用单元测试Junit)
首先在数据库db1下创建一个user表,包含username和password两个字段,查询如下
在这里插入图片描述

编写测试类

package com.itweb.jdbc;

import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
 * 用户登录
 */
public class JDBCDemo {
    @Test
    public void testLogin() throws Exception{
        // 注册驱动
        //Class.forName("com.mysql.jdbc.Driver"); // 将类加载进内存 (可以省略)
        // 获取连接
        String url="jdbc:mysql:///db1?useSSL=false";
        String username="root";
        String password="123456";
        Connection conn = DriverManager.getConnection(url, username, password);

        // 接收用户输入 用户名和密码
        String name="张三";
        String pwd="123";
        // 定义sql语句
        String sql="SELECT * FROM `user` where username='"+name+"' and password='"+pwd+"'";
        // 获取执行sql对象
        Statement stmt = conn.createStatement();
        // 执行sql
        ResultSet rs=stmt.executeQuery(sql);
        // 判断是否登录成功
        if (rs.next()){ // 查询结果集(数据库)里面包含用户输入的用户名和密码,即登录成功
            System.out.println("登录成功~");
        }
        else{
            System.out.println("登录失败~");
        }
        // 释放资源
        rs.close();
        stmt.close();
        conn.close();
    }

    /**
     * 演示SQL注入
     */
    @Test
    public void testLogin_Inject() throws Exception{
        // 注册驱动
        //Class.forName("com.mysql.jdbc.Driver"); // 将类加载进内存 (可以省略)
        // 获取连接
        String url="jdbc:mysql:///db1?useSSL=false";
        String username="root";
        String password="123456";
        Connection conn = DriverManager.getConnection(url, username, password);

        // 接收用户输入 用户名和密码
        String name="aaa";
        String pwd="' or '1'='1";
        // 定义sql语句
        String sql="SELECT * FROM `user` where username='"+name+"' and password='"+pwd+"'";
        System.out.println(sql);
        // 获取执行sql对象
        Statement stmt = conn.createStatement();
        // 执行sql
        ResultSet rs=stmt.executeQuery(sql);
        // 判断是否登录成功
        if (rs.next()){ // 查询结果集(数据库)里面包含用户输入的用户名和密码,即登录成功
            System.out.println("登录成功~");
        }
        else{
            System.out.println("登录失败~");
        }
        // 释放资源
        rs.close();
        stmt.close();
        conn.close();
    }
}

运行testLogin(),结果如下
在这里插入图片描述

运行testLogin_Inject(),结果如下
在这里插入图片描述

原因分析(重点)

使用(’ or ‘1’='1)等特殊字符作为登录密码,破解了上述定义sql查询语句的字符串拼接漏洞(即SQL注入的本质:通过操作输入来修改事先定义好的SQL语句,来达到执行代码对服务器进行攻击的方法)。通过打印sql,得到SELECT * FROM user where username=‘aaa’ and password=‘’ or ‘1’=‘1’,可以发现,where后面的条件判断为false and false or true,由于and的优先级比or高,所以整体结果为true,即无论输入的是什么用户名,通过该串密码都能查询成功。当然,现在的程序都不会让SQL注入发生,因为这是一个低级且严重的错误。

6)演示防止SQL注入(重点)

package com.itweb.jdbc;

import org.junit.Test;
import java.sql.*;
/**
 * API详解:PreparedStatement
 * 防止SQL注入操作
 */
public class JDBCDemo {
    @Test
    public void testPreparedStatement() throws Exception{
        // 注册驱动
        //Class.forName("com.mysql.jdbc.Driver"); // 将类加载进内存 (可以省略)
        // 获取连接
        String url="jdbc:mysql:///db1?useSSL=false";
        String username="root";
        String password="123456";
        Connection conn = DriverManager.getConnection(url, username, password);

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

        // 定义sql语句
        String sql="SELECT * FROM user WHERE username=? AND password=?";
        // 获取pstmt对象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        // 设置?的值
        pstmt.setString(1,name);
        pstmt.setString(2,pwd);
        // 执行sql
        ResultSet rs=pstmt.executeQuery(); // 这里不能传sql
        // 判断是否登录成功
        if (rs.next()){ // 查询结果集(数据库)里面包含用户输入的用户名和密码,即登录成功
            System.out.println("登录成功~");
        }
        else{
            System.out.println("登录失败~");
        }
        // 释放资源
        rs.close();
        pstmt.close();
        conn.close();
    }
}

运行结果
在这里插入图片描述

实质:pstmt对象在传入参数时会对其值进行转义操作,从而避免了由于拼接产生的语义问题

4、数据库连接池(了解,会用就行)

1)概念
管理数据库连接的容器,负责分配、管理数据库连接
2)特点
- 允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
- 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
3)好处
- 资源重用
- 提示系统响应速度
- 避免数据库连接遗漏
4Driud(德鲁伊)数据库连接池(阿里巴巴)
使用步骤
- 导入jar包
- 定义配置文件
- 加载配置文件
- 获取数据库连接池对象
- 获取连接
  • 28
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
JavaWeb购物车JDBC是一种基于JavaWeb技术开发的电子商务网站,它实现了用户注册、登录、浏览商品、购物车结算、订单管理等一系列电子商务操作。JDBC是Java数据库连接技术,它可以实现与关系型数据库的连接与交互,为JavaWeb购物车提供了持久化存储功能。 JavaWeb购物车JDBC的实现过程主要包括以下几个步骤: 1.建立数据库连接:首先需要加载JDBC驱动程序,然后通过该驱动程序与数据库建立连接。 2.创建数据库表结构:为了实现购物车功能,需要创建商品表、用户表、订单表和订单详情表等数据库表。 3.编写JDBC代码:使用Java语言编写数据访问对象(DAO)类,通过这些类实现对数据库的增删改查操作。同时,需要实现购物车功能的业务逻辑,如添加商品到购物车、计算商品总价、生成订单等。 4.部署JavaWeb应用程序:将JavaWeb购物车JDBC部署到Web服务器上,如Tomcat或Jetty,通过浏览器访问网站进行测试。 JavaWeb购物车JDBC的优势在于它遵循Java开发语言的规范,具有可移植性和跨平台性。同时,JDBC技术支持多种数据库类型,如Oracle、MySQL、PostgreSQL等,方便开发者根据实际需求选择最适合的数据库。在开发过程中,使用JDBC技术可以提高代码的可读性和可维护性,减少耦合度,提高代码复用率。 总之,JavaWeb购物车JDBC是一种基于JavaWeb技术和JDBC技术的电子商务网站,它具有可移植性、跨平台性和可读性强等优势,可为JavaWeb开发者提供一个高效、可扩展的电商开发框架。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码农后端

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

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

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

打赏作者

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

抵扣说明:

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

余额充值