首页 > 技术文章 > JDBC

liqiju 2022-03-11 15:06 原文

一、什么是JDBC

image

​ JDBC是一套面向对象的应用编程接口,它制定了统一的访问各类关系数据库的标准接口,为各个数据库厂商提供了标准接口的实现。通过使用JDBC技术,开发人员可以用纯java和标准的SQL语句编写完整的数据库应用程序。

一下操作基于此表:

-- auto-generated definition
create table user(
    id          int auto_increment primary key,
    name        char(10)            not null,
    gender      tinyint 	default 0 not null,
    age         tinyint     default 0   not null,
    nationality char(10)            null,
    constraint user_name_uindex
        unique (name)
);

二、连接步骤

代码

public class JDBCTest {
    public static void main(String[] args) {
        final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
        final String URL = "jdbc:mysql://localhost:3306/数据库名称?useSSL=false&serverTimezone=UTC";
        final String USERNAME = "root";
        final String PASSWORD = "123";	// 数据库密码(换成你自己的)
        String sql = "select * from user";
        try {
            // 1. 加载mysql驱动
            Class.forName(JDBC_DRIVER);
            // 2. 获取连接
            Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            // 3. 获取命令对象
            Statement statement = conn.createStatement();
            // 4. 执行sql,并获取结果
            ResultSet resultSet = statement.executeQuery(sql);
            while (resultSet.next()) {
                int id = rSet.getInt("id");
                String name = rSet.getString("name");
                String gender = rSet.getString("gender");
                int age = rSet.getInt("age");
                String nationality = rSet.getString("nationality");
                System.out.println(id + "--" + name + "--" + gender + "--" + age + "--" + nationality);
            }
            // 5. 关闭连接
            conn.close();
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }
}

详细步骤

1. 加载驱动程序

注:mysql驱动jar包版本最好和mysql版本一致

final String driver = "com.mysql.jdbc.Driver";
Class.forName(driver);

2. 获取数据库连接

注:mysql版本不同url的写法也略有差别

final String URL = "jdbc:mysql://localhost:3306/数据库名?useSSL=false";
final String USER = "root"; // 数据库用户名
final String PASSWORD = "123"; // 数据库密码
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);

3. 创建命令对象

Statement stat = conn.createStatement();

3.1 三种Statement对象

  • Statement:用于执行不带参数的简单SQL语句;
  • PreparedStatement:Statement的子接口类型,PreparedStatement对象用于执行带或不带in参数的预编译SQL语句;可以避免SQL注入问题,提高SQL执行效率;
  • CallableStatement:PreparedStatement的子接口类型,CallableStatement对象用于执行对数据库已存在的存储过程的调用;

3.2 SQL注入

select *from user where name='admin' and pwd=''or'1'='1';

4. 执行SQL语句,获取查询结果

String sql = "select * from website";
ResultSet resultSet = stat.executeQuery(sql);

4.1 执行SQL语句对象

  • execute(sql); 可执行任意SQL语句(建议使用相关sql执行);返回值boolean:true表示返回的是一个结果集(查询sql语句),false: 表示不是一个结果集
  • executeUpdate(sql); 执行增删改相关的SQL语句
  • executeQuery(sql); 此方法执行查询的SQL语句

5. 关闭资源

conn.close(); // 仅关闭connection即可

三、JDBC API中的接口和类

  • DriverManager: 这个类是JDBC的管理层,作用域用户和驱动程序之间。它跟踪可用的驱动程序,并在数据库和响应的驱动程序之间建立连接。
  • Driver:此接口处理与数据库服务器的通信,通常由数据库厂商实现该接口。在进行Java Web开发时,程序员只需根据程序使用的驱动程序类型进行装载就行。
  • Connection:此接口代表数据库连接。与数据库的所有通信都通过该对象连接。
  • Statement:此接口代表SQL语句对象。可以向数据库发送任何SQL语句。
  • ResultSet:它封装了查询返回的结果集对象。结果集是一个存储查询结果的对象,但是结果集不仅仅具有存储功能,同时后具有操纵数据的功能,可能完成对数据的更新等。
  • SQLException:这个类处理发生在数据库操作过程中的任何错误。

四、批量操作

​ 使用JDBC提供的一次执行多条sql语句。

​ 如果需要多次执行某一条sql语句,每次获得执行sql命令对象只执行一条sql语句效率比较低,Statement接口提供了addBatch()方法和executeBatch()方法; 允许一次向sql命令的对象中批量添加sql语句,统一发送到数据库运行,提高效率;

// Statement批量操作
public class Insert1 {
    public static void main(String[] args) {
        String sql1 = "insert into user(name,gender,age,nationality) value('小蜜瓜','女',13,'CN')";
        String sql2 = "insert into user(name,gender,age,nationality) value('小红薯','女',14,'CN')";
        String sql3 = "insert into user(name,gender,age,nationality) value('小蜜桃','女',12,'CN')";
        String sql4 = "insert into user(name,gender,age,nationality) value('小西瓜','男',11,'CN')";

        try (Connection conn = DBUtil.getConnection()) {
            Statement statement = conn.createStatement();
            statement.addBatch(sql1);
            statement.addBatch(sql2);
            statement.addBatch(sql3);
            statement.addBatch(sql4);
            int[] res = statement.executeBatch();
            System.out.println("受影响的行数:" + Arrays.toString(res));
        } catch (SQLException sqlException) {
            sqlException.printStackTrace();
        }
    }
}

// PreparedStatement批量操作
public class Insert2 {
    public static void main(String[] args) {
        String sql = "insert into user(name,gender,age,nationality) value(?, ?, ?, ?)";

        try (Connection conn = DBUtil.getConnection()) {
            PreparedStatement pst = conn.prepareStatement(sql);
            for (int i = 0; i < 100; i++) {
                pst.setString(1, "小蜜瓜" + i);
                pst.setString(2, "女");
                pst.setInt(3, i);
                pst.setString(4, "CN");
                pst.addBatch();
                // 每20条执行一次,防止内存溢出
                if (i % 20 == 0) {
                    pst.executeBatch();
                }
            }
            pst.executeBatch();
        } catch (SQLException sqlException) {
            sqlException.printStackTrace();
        }
    }
}

五、数据库连接池

1. 什么是数据库连接池

​ 数据库连接池是一个包含多个数据连接对象的容器。

​ 数据库连接池可以避免频繁创建销毁带来的系统开销。连接池可以预先创建多个Connection对象,当有需要会自动创建关闭,还能规定最大连接数量,防止内存消耗。

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

<dependency>
	<groupId>commons-dbcp</groupId>
	<artifactId>commons-dbcp</artifactId>
	<version>1.4</version>
</dependency>

2. 通过连接池获取数据库连接

// 这里使用的是dbcp连接池
public class DBUtil {
    private static BasicDataSource bds;

    static {
        // 创建属性对象,该对象用于读取属性配置文件
        Properties p = new Properties();
        // 得到文件的输入流(见6.2)
        InputStream in = DBUtil1.class.getClassLoader()
                .getResourceAsStream("db.properties");
        // 把文件加载到属性对象中
        try {
            p.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 读取数据
        String driver = p.getProperty("driver");
        String url = p.getProperty("url");
        String username = p.getProperty("username");
        String password = p.getProperty("password");

        // 创建数据连接池对象
        bds = new BasicDataSource();
        // 为连接池中的连接对象设置基本参数信息
        bds.setDriverClassName(driver);
        bds.setUrl(url);
        bds.setUsername(username);
        bds.setPassword(password);
        // 连接池连接对象管理策略
        bds.setInitialSize(3); // 初始连接数量
        bds.setMaxActive(5); // 最大连接数
        bds.setMaxIdle(3); // 最大空闲连接数
    }

    public static Connection getConnection() throws SQLException {
        // 从连接池中获取连接
        return bds.getConnection();
    }
}

六、获取数据库连接的方式

1. 直接获取

public class DBUtil1 {
    public static Connection getConnection() {
        final String driver = "com.mysql.jdbc.Driver";
        final String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
        final String username = "root";
        final String password = "123";
        Connection connection = null;
        try {
            // 1. 加载驱动
            Class.forName(driver);
            // 2. 获取连接
            connection = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }
}

2. properties文件获取

db.properties 文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC
username=root
password=123
public class DBUtil {
    public static Connection getConnection() {
        Connection connection = null;
        try {
            // 创建属性对象,该对象用于读取属性配置文件
            Properties p = new Properties();
            // 得到文件的输入流
            InputStream in = DBUtil1.class.getClassLoader()
                    .getResourceAsStream("db.properties");
            // 把文件加载到属性对象中
            p.load(in);
            // 读取数据
            String driver = p.getProperty("driver");
            String url = p.getProperty("url");
            String username = p.getProperty("username");
            String password = p.getProperty("password");
            // 1. 加载驱动
            Class.forName(driver);
            // 2. 获取连接
            connection = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }
}

3. 连接池获取

见5.2

七、练习

1. 创建UserBean

​ UserBean就是一个javaBean,javaBean实际上就是一个Java类,用于封装从数据库中查询出的数据。

public class UserBean {
    private int id;
    private String name;
    private String gender;
    private int age;
    private String nationality; // 国籍
    // 无参、全参构造略...
    // getter,setter,toString方法略...
}

2. 创建操作类UserDao

​ UserDao中封装了操作相关数据表的方法。如:添加数据、删除、修改、查询数据等

public class UserDao {
    // 添加
    public void addUser(UserBean user) {
        String sql = "insert into user(name, gender, age, nationality) value(?,?,?,?)";
        try(Connection conn = DBUtil.getConnection()) {
            // 预编译SQL(可以提高sql执行效率,避免SQL注入)
            PreparedStatement ptmt = ptmt = conn.prepareStatement(sql);
            // 传参. 1对应第一个? name;2对应第二个? url ...
            ptmt.setString(1, user.getName());
            ptmt.setString(2, user.getGender());
            ptmt.setInt(3, user.getAge());
            ptmt.setString(4, user.getNationality());
            // 获取返回结果,这里返回受影响的行数
            int rows = ptmt.executeUpdate();
            System.out.println("添加数据受影响的行数:" + rows);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 更新
    public void updateUser(UserBean user) {
        String sql = "update user set gender=?, age=?, nationality=? where name=?";
        try(Connection conn = DBUtil.getConnection()) {
            PreparedStatement ptmt = connection.prepareStatement(sql);
            ptmt.setString(1, user.getGender());
            ptmt.setInt(2, user.getAge());
            ptmt.setString(3, user.getNationality());
            ptmt.setString(4, user.getName());
            int rows = ptmt.executeUpdate();
            System.out.println("更新数据受影响的行数:" + rows);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 删除
    public void delWebsite(int id) {
        try(Connection conn = DBUtil.getConnection()) {
            PreparedStatement ptmt = conn.prepareStatement("delete from user where id = ?");
            ptmt.setInt(1, id);
            int rows = ptmt.executeUpdate();
            System.out.println("删除数据受影响的行数:" + rows);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 查询所有
    public void findAllUser() {
        final Connection connection = DBUtil.getConnection();
        try(Connection conn = DBUtil.getConnection()) {
            PreparedStatement ptmt = conn.prepareStatement("select * from user");
            ResultSet resSet = ptmt.executeQuery();
            while (resSet.next()) {
                int id = rSet.getInt("id");
                String name = rSet.getString("name");
                String gender = rSet.getString("gender");
                int age = rSet.getInt("age");
                String nationality = rSet.getString("nationality");
                System.out.println(id + "--" + name + "--" + gender + "--" + age + "--" + nationality);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    // 根据id查询user
    public void findUserById(int id) {
        String sql = "select * from user where id = ?";
        try (Connection connection = DBUtil.getConnection()) {
            PreparedStatement pst = connection.prepareStatement(sql);
            pst.setInt(1, id);
            ResultSet resSet = pst.executeQuery();
            resSet.next();
           	int id = rSet.getInt("id");
            String name = rSet.getString("name");
            String gender = rSet.getString("gender");
            int age = rSet.getInt("age");
            String nationality = rSet.getString("nationality");
            System.out.println(id + "--" + name + "--" + gender + "--" + age + "--" + nationality);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

推荐阅读