Python工程师Java之路(o)JDBC

本文详细介绍了Java数据库连接JDBC的使用步骤,包括注册驱动、连接数据库、执行SQL和处理批处理及事务。同时,文章讨论了SQL注入问题和BLOB数据的解决方案,推荐使用PreparedStatement。此外,还探讨了数据库连接池的概念和MyBatis框架的优势,给出了MyBatis的简单配置和使用示例,包括创建表、类、XML映射文件以及主类的编写。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、JDBC概述

  • 全称:Java Database Connectivity
  • 一组独立于任何 DBMSAPI

JDBC是一组规范接口,其实现类由各数据库厂商提供

2、JDBC使用步骤(以MySQL为例)

  1. 注册驱动
  2. 使用驱动来创建数据库连接
  3. 执行SQL
  4. 释放资源

2.1、注册驱动相关源码

com.mysql.jdbc.Driver(其实现了java.sql.Driver接口)源码如下
可以看到:类加载时,有个静态代码块,自动注册驱动

package com.mysql.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

2.2、把驱动jar添加到项目中

2.3、代码示例

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

public class TestJDBC {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1、注册驱动(下面两行任选一行都可)
        Class.forName("com.mysql.jdbc.Driver");
        //DriverManager.registerDriver(new com.mysql.jdbc.Driver());

        // 2、连接数据库(TCP/IP协议)
        // jdbc:mysql是应用层协议;localhost是地址;3306是端口号;b1是数据库名
        String url = "jdbc:mysql://localhost:3306/b1";
        String user = "root";
        String password = "密码";
        Connection conn = DriverManager.getConnection(url, user, password);

        // 3、执行SQL
        /*
         * 回忆TCP/IP
         * Socket代表连接
         * socket.getOutputStream()来发送数据,
         * socket.getInputStream()来接收数据
         *
         * 可以把Connection比喻成Socket,把Statement比喻成OutputStream
         */
        // 3.1、获取Statement对象
        Statement st = conn.createStatement();
        // 3.2、执行SQL,返回成功操作的记录数
        String sql = "insert into tb1 values(null,'value1','value2')";
        int len = st.executeUpdate(sql);
        System.out.println(len > 0 ? "成功" : "失败");

        // 4、释放资源
        st.close();
        conn.close();
    }
}

3、SQL注入 和 blob类数据 的解决

sql注入

String sql = "SELECT * FROM t_employee where ename='" + ename + "'";
// 如果我此时从键盘输入ename值的时候,输入:【张三' OR '1'= '1】
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
// 结果会把所有数据都查询出来

处理blob等类型的数据

String sql = "insert into user(username,photo) values('chailinyan', 图片字节流)";
// 此时photo是blob类型的数据时,无法在sql中直接拼接

解决方法:使用PreparedStatement

String sql = "insert into t_employee(ename,tel,gender,salary) values(?,?,?,?)";
PreparedStatement pst = conn.prepareStatement(sql);
// 这里要传带?的sql,然后mysql端就会对这个sql进行预编译

//设置?的具体值
pst.setObject(1, ename);
pst.setObject(2, tel);
pst.setObject(3, gender);
pst.setObject(4, salary);

int len = pst.executeUpdate(); //此处不用传sql
System.out.println(len);
String sql = "insert into user(username,photo) values(?,?)";
PreparedStatement pst = conn.prepareStatement(sql);

//设置?的值
pst.setObject(1, "chailinyan");
FileInputStream fis = new FileInputStream("D:/QMDownload/img/美女/15.jpg");
pst.setBlob(2, fis);

int len = pst.executeUpdate();
System.out.println(len > 0 ? "成功" : "失败");

4、批处理

在url中要加一个参数rewriteBatchedStatements=true
方法:addBatch()executeBatch()
注意:如果批量添加时,insert要用values而不要用value

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class TestBatch {
    public static void main(String[] args) throws Exception {
        // 注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        String url = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true";
        // 获取连接
        Connection conn = DriverManager.getConnection(url, "root", "123456");

        // SQL
        String sql = "insert into t_department values(null,?,?)";
        PreparedStatement pst = conn.prepareStatement(sql);

        // 批量插入
        for (int i = 1; i <= 90000; i++) {
            pst.setObject(1, "模拟部门" + i);
            pst.setObject(2, "模拟部门的简介" + i);

            // 添加到批处理一组操作中,攒一块处理
            pst.addBatch();

            // 分批后可以再分批(可选)
            if (i % 1000 == 0) {
                // 执行
                pst.executeBatch();
                // 清空
                pst.clearBatch();
            }

        }
        pst.executeBatch();

        // 关闭
        pst.close();
        conn.close();
    }
}

5、事务

  1. 在执行之前,设置手动提交事务:setAutoCommit(false)
  2. 成功:commit();失败:rollback()
  3. 还原为自动提交:setAutoCommit(true)
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class TestTransaction {
    public static void main(String[] args) throws Exception {
        /*
         * update t_department set description = 'xx' where did = 2;
         * update t_department set description = 'yy' where did = 3;
         *
         * 我希望上面两个语句要么一起成功,要么一起回滚
         * 为了制造失败,我故意把第二条语句写错
         * update t_department set description = 'yy' (少了where) did = 3;
         */

        //1、注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        //2、获取连接
        Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3306/b1", "root", "密码");

        //设置手动提交事务
        c.setAutoCommit(false);

        //3、执行sql
        String sql1 = "update t_department set description = 'xx' where did = 2";
        String sql2 = "update t_department set description = 'yy' did = 3"; // 这句错的,造成回滚
        //使用prepareStatement的sql也可以不带?
        PreparedStatement pst = null;
        try {
            pst = c.prepareStatement(sql1);
            int len = pst.executeUpdate();
            System.out.println("第一条:" + (len > 0 ? "成功" : "失败"));

            pst = c.prepareStatement(sql2);
            len = pst.executeUpdate();
            System.out.println("第二条:" + (len > 0 ? "成功" : "失败"));

            System.out.println("提交");
            c.commit();
        } catch (Exception e) {
            System.out.println("回滚");
            c.rollback();
        }

        //4、关闭
        if (pst != null) {
            pst.close();
        }
        c.setAutoCommit(true); // 还原为自动提交
        c.close();
    }
}

6、数据库连接池

  • 数据库连接池
    负责分配、管理和释放数据库连接
    允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个

  • 最小连接数是连接池一直保持的数据库连接

  • 若数据库连接请求超过最大连接数,后面的数据库连接请求将被加入到等待队列中

  • 大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放

import com.alibaba.druid.pool.DruidDataSource; // 数据库连接池 类

import java.sql.Connection;
import java.sql.SQLException;

public class Hello {
    public static void main(String[] args) throws SQLException {
        // 1、创建数据源(数据库连接池)对象
        DruidDataSource ds = new DruidDataSource();

        // 2、设置参数
        // 2.1、连接参数
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/b0");
        ds.setUsername("root");
        ds.setPassword("");

        // 2.2、连接数
        ds.setInitialSize(5);//一开始提前申请好5个连接,不够了,重写申请
        ds.setMaxActive(10);//最多不超过10个,如果10都用完了,还没还回来,就会出现等待
        ds.setMaxWait(1000);//用户最多等1000毫秒,如果1000毫秒还没有人还回来,就异常了

        // 3、获取连接
        for (int i = 1; i <= 15; i++) {
            Connection conn = ds.getConnection();
            System.out.println("第" + i + "个:" + conn);

            // 如果这里没有关闭,就相当于没有还(超过最大连接数后会报错)
            conn.close(); // 这里关闭,是还回池中
        }
    }
}

7、MyBatis

  • MyBatis是一款优秀的 持久层 框架,用于简化JDBC开发
  • 支持自定义 SQL、存储过程以及高级映射
  • 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
  • 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO 为数据库中的记录

JDBC缺点

MyBatis简化JDBC开发

7.1、使用 Maven 来构建项目

pom.xml添加依赖

<!-- MyBatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>
<!-- MySQL的JDBC -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

7.2、创建表和类

创建teacher表

CREATE TABLE teacher (
 id      INT     PRIMARY KEY  AUTO_INCREMENT,
 gender  char(1),
 pwd     char(20));
INSERT INTO teacher VALUES
(1,'男','abc'),
(2,'女','abc'),
(3,'女','bcd');

创建Teacher类

package a.b.pojo;

public class Teacher {
    private Integer id;
    private String gender;
    private String pwd;

    public Integer getId() {
        return id;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "id=" + id +
                ", gender='" + gender + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

7.3、从 XML 中构建 SqlSessionFactory

  1. resources目录下创建mybatis-config.xml
  2. 修改数据库连接参数【driverurlusernamepassword
  3. 修改SQL映射文件路径mapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 数据库环境 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/b0"/>
                <property name="username" value="root"/>
                <property name="password" value="密码"/>
            </dataSource>
        </environment>
    </environments>
    <!-- SQL映射文件 -->
    <mappers>
        <mapper resource="a/b/mapper/TeacherMapper.xml"/>
    </mappers>
</configuration>

7.4、SQL映射

  1. SQL映射文件要与Mapper接口同名(都是TeacherMapper
  2. Mapper接口和SQL映射文件 的目录要相同(都是a/b/mapper/
  3. SQL映射文件的namespace属性为Mapper接口全限定名
  4. 在Mapper接口中定义方法,方法名是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致

映射接口a/b/mapper/TeacherMapper.java(在java目录下)

package a.b.mapper;

import a.b.pojo.Teacher;

import java.util.List;

public interface TeacherMapper {
    List<Teacher> selectAll();
}

SQL映射文件a/b/mapper/TeacherMapper.xml(在resources目录下)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="a.b.mapper.TeacherMapper">
    <select id="selectAll" resultType="a.b.pojo.Teacher">
        select * from teacher
    </select>
</mapper>

7.5、主类

  1. 加载MyBatis核心配置文件(路径要跟上面mybatis-config.xml一致),获取SqlSessionFactory对象
  2. 使用 SqlSession工厂对象 创建 SqlSession对象
  3. 使用 SqlSession对象 获取Mapper对象
  4. 使用Mapper对象执行SQL
  5. 释放资源
package a.b;

import a.b.mapper.TeacherMapper;
import a.b.pojo.Teacher;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class Demo {
    public static void main(String[] args) throws IOException {
        // 1、加载MyBatis核心配置文件,获取SqlSessionFactory对象
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 2、使用 SqlSession工厂对象 创建 SqlSession对象
        SqlSession session = sqlSessionFactory.openSession();

        // 3、使用 SqlSession对象 获取Mapper对象
        TeacherMapper mapper = session.getMapper(TeacherMapper.class);

        // 4、使用Mapper对象执行SQL
        List<Teacher> teachers = mapper.selectAll();
        System.out.println(teachers);

        // 5、释放资源
        session.close();
    }
}

7.6、最终目录结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小基基o_O

您的鼓励是我创作的巨大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值