JDBC

快速入门

在模块下新建lib文件夹,将mysql的jar包粘贴进去并右键选择添加为库,范围选择模块库

package jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class JDBCDemo {
    public static void main(String[] args) throws Exception {
        //1.注册驱动(反射)
        Class.forName("com.mysql.jdbc.Driver");//mysql5之后的jar可以不写这一行

        //2.获取连接
        String url = "jdbc:mysql://127.0.0.1:3306/test";//固定写法 3306后面是要连接的数据库名
        //jdbc:mysql:///test //如果数据库地址和端口是127.0.0.1:3306可以省略
        String username = "root";
        String password = "123456";
        Connection connection = DriverManager.getConnection(url, username, password);

        //3.定义sql
        String sql = "update student set name = \"Ben\" where id = 1";

        //4.获取执行sql语句的对象
        Statement statement = connection.createStatement();

        //5.执行sql
        int i = statement.executeUpdate(sql);//返回受影响的行数
        //executeUpdate能执行DML和DDL语句
        System.out.println(i);
        //ResultSet resultSet = statement.executeQuery(sql);能执行DQL语言

        //6.释放资源
        statement.close();
        connection.close();
    }
}


数据库连接池


如果不使用数据库连接池,如左图,有很多用户要同时访问数据库的情况下,先给用户1开一个连接对象conn,用完后关掉conn,再到下一个用户。开启数据库连接是一个非常耗时的工作,需要耗费一些资源,关闭连接也需要耗费资源。这种情况就是来一个用户就开一个连接,用完就关。又来一个用户又开一个连接,用完又关,很浪费时间和资源。

而连接池如右图,就是提前申请多个数据库连接放进连接池这个容器中,有用户来就给用户分配连接,用完后不会释放掉,而会回到连接池中,提高了资源复用和系统响应速度。
当用户特别多,第5个用户申请连接时连接池里的连接已经用完了,此时连接池会看前4个用户是否在有效利用连接。如果发现有用户没有利用连接池(如长时间无操作(挂机))就会强制中断该用户的连接,然后把那个连接分配给第5个用户。从而实现避免数据库连接遗漏。

连接池实现

Druid

先导入jar包

在src下编写配置文件
druid.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test
username=root
password=123456
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000

druidDemo.java

package jdbcdemo.druid;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties;

public class druidDemo {
public static void main(String[]args) throws  Exception{
    //加载配置文件
    Properties prop = new Properties();
    prop.load(new FileInputStream("src/druid.properties"));

    //获取连接池对象
    DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);

    //获取数据库连接
    Connection connection = dataSource.getConnection();
    System.out.println(connection);//com.mysql.cj.jdbc.ConnectionImpl@34b7ac2f

    //如果prop.load报错,说明地址不对,可以用下面的方法查看当前工作目录
    System.out.println(System.getProperty("user.dir"));//D:\Programme\code\JavaSE
}
}

管理事务



setAutoCommit(false)就是开启事务的代码

代码实现
5、6就是事务


1000都变成了3000

现在人为制造异常


结果还是1000,说明回滚了

sql注入


sql注入的应用:在密码处输入

' or '1' = '1

发现账号乱写的也能登录成功

因为密码的字符串放到sql中使得sql语句的意思发生了变化

  • 通过PrepareStatement来防止sql注入

    第2个作用就是防止sql注入
    原理是把传递的值包含的关键字或者敏感字符都进行了转义处理

    代码实现

    现在sql注入就失败了

使用JDBC实现增删改查

项目结构

Brand.java

package jdbcdemo.pojo;

public class Brand {
    private Integer id;
    private String brandName;
    private String companyName;
    private Integer ordered;
    private String description;
    private Integer status;//0:禁用 1:启用
    /*基本类型建议换成包装类,因为基本类型的默认值可能不适应需求
    比如int的默认值是0,对于ordered和status而言不适合
    而Integer的默认值是null,相比之下更合适些
    */

    public Integer getId() {
        return id;
    }

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

    public String getBrandName() {
        return brandName;
    }

    public void setBrandName(String brandName) {
        this.brandName = brandName;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public Integer getOrdered() {
        return ordered;
    }

    public void setOrdered(Integer ordered) {
        this.ordered = ordered;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    @Override
    public String toString() {
        return "Brand{" +
                "id=" + id +
                ", brandName='" + brandName + '\'' +
                ", companyName='" + companyName + '\'' +
                ", ordered=" + ordered +
                ", description='" + description + '\'' +
                ", status=" + status +
                '}';
    }
}

BrandTest.java

package jdbcdemo.example;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import jdbcdemo.pojo.Brand;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class BrandTest {
    public static void main(String[] args) throws Exception {
        BrandTest bt = new BrandTest();
        bt.selectAll();
        bt.add();
        bt.selectAll();
        bt.updateTest();
        bt.selectAll();
        bt.deleteTest();
        bt.selectAll();
    }

    public void selectAll() throws Exception {
        //1.获取connection
        Properties prop = new Properties();
        prop.load(new FileInputStream("src/druid.properties"));
        //获取连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //获取数据库连接
        Connection conn = dataSource.getConnection();

        //2.定义sql
        String sql = "select * from tb_brand;";

        //3.获取prepareStatement对象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        /*用PreparedStatement是因为它比Statement效率更高*/

        //4.执行sql
        ResultSet rs = pstmt.executeQuery();//执行DQL语句

        //5.处理结果

        //获取数据
        /*ResultSet的对象rs像一个游标在mysql表格中,默认在表头(字段那一行)
         获取数据时会往下一行移动,先判断此行的数据是否有效(是否全都是null)
         有效才会在此行逐列读取数据
        */
        Brand brand = null;
        /*把Brand brand提取出来,brand每次循环指向新new出的对象,节约栈内存
        不提取出来,循环几次,栈中就会有几个同名的brand。提取出来就能复用brand
        */
        List<Brand> brands = new ArrayList<>();
        while (rs.next()) {//rs.next就是往下一行判断该行数据是否有效的方法,返回布尔值
            int id = rs.getInt("id");
            /*rs.getXxx就是rs获取数据的方法如getInt,获取int类型的数据
            getXxx(第几列)如,getInt(1) 表示获取该行第1列的数据,从1开始算
            也可以根据字段来获取,getInt("id"),获取该行名为id的字段,必须跟表中的字段名一致
            */
            String brandName = rs.getString("brand_name");
            String companyName = rs.getString("company_name");
            int ordered = rs.getInt("ordered");
            String description = rs.getString("description");
            int status = rs.getInt("status");
            //将每一行的数据都封装进一个Brand对象中
            brand = new Brand();
            brand.setId(id);
            brand.setBrandName(brandName);
            brand.setCompanyName(companyName);
            brand.setOrdered(ordered);
            brand.setDescription(description);
            brand.setStatus(status);
            //把对象放到集合中
            brands.add(brand);
        }
        System.out.println(brands);

        //6.释放资源
        rs.close();
        pstmt.close();
        conn.close();
    }

    public void add() throws Exception {
        //模拟前端传来的数据
        String brandName = "香飘飘";
        String companyName = "喜之郎";
        int ordered = 5;
        String description = "绕地球一圈";
        int status = 1;


        //1.获取connection
        Properties prop = new Properties();
        prop.load(new FileInputStream("src/druid.properties"));
        //获取连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //获取数据库连接
        Connection conn = dataSource.getConnection();

        //2.定义sql
        String sql = "insert into tb_brand(brand_name,company_name,ordered,description,status) values(?,?,?,?,?);";
        //id自增,不需要写

        //3.获取prepareStatement对象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        /*用PreparedStatement是因为它比Statement效率更高*/

        //4.设置参数
        pstmt.setString(1, brandName);
        pstmt.setString(2, companyName);
        pstmt.setInt(3, ordered);
        pstmt.setString(4, description);
        pstmt.setInt(5, status);

        //5.执行sql
        int count = pstmt.executeUpdate();//受影响的行数

        //6.处理结果
        if (count > 0) {
            System.out.println("添加成功");
        } else {
            System.out.println("添加失败");
        }

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

    public void updateTest() throws Exception {
        //模拟前端传来的数据
        String brandName = "红米";
        String companyName = "小米子公司红米";
        int ordered = 4;
        String description = "redmi";
        int status = 1;
        int id = 2;

        //1.获取connection
        Properties prop = new Properties();
        prop.load(new FileInputStream("src/druid.properties"));
        //获取连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //获取数据库连接
        Connection conn = dataSource.getConnection();

        //2.定义sql
        String sql = "update tb_brand set\n" +
                "brand_name = ?,\n" +
                "company_name = ?,\n" +
                "ordered = ?," +
                "description = ?,\n" +
                "status = ?\n" +
                "where id = ?";

        //3.获取prepareStatement对象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        /*用PreparedStatement是因为它比Statement效率更高*/

        //4.设置参数
        pstmt.setString(1, brandName);
        pstmt.setString(2, companyName);
        pstmt.setInt(3, ordered);
        pstmt.setString(4, description);
        pstmt.setInt(5, status);
        pstmt.setInt(6, id);

        //5.执行sql
        int count = pstmt.executeUpdate();//受影响的行数

        //6.处理结果
        if (count > 0) {
            System.out.println("修改成功");
        } else {
            System.out.println("修改失败");
        }

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

    public void deleteTest() throws Exception {
        //1.获取connection
        Properties prop = new Properties();
        prop.load(new FileInputStream("src/druid.properties"));
        //获取连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //获取数据库连接
        Connection conn = dataSource.getConnection();

        //2.定义sql
        String sql = "delete from tb_brand where id = ?";

        //3.获取prepareStatement对象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        /*用PreparedStatement是因为它比Statement效率更高*/

        //4.设置参数
        int id = 2;//模拟前端传来的数据
        pstmt.setInt(1, id);

        //5.执行sql
        int count = pstmt.executeUpdate();//受影响的行数

        //6.处理结果
        if (count > 0) {
            System.out.println("删除成功");
        } else {//DDL语句成功删除后count可能为0,这种判断不严谨。一般不报异常就算成功了
            System.out.println("删除失败");
        }

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


posted @ 2023-02-24 16:32  ben10044  阅读(12)  评论(0编辑  收藏  举报