JDBC

JDBC(Java DataBase Connectivity)是一种规范,它提供的接口,一套完整的,允许便捷式访问底层数据库。

JDK自带了一个java.sql包,而这里面就定义了大量的接口,不同类型的数据库,都可以通过实现此接口,编写适用于自己数据库的实现类。

不同的数据库厂商实现的这套标准,称为数据库驱动

使用JDBC连接数据库

// 1. 通过DriverManager来获得数据库连接
try (Connection connection = DriverManager.getConnection("连接URL","用户名","密码");
    // 2. 创建一个用于执行SQL的Statement对象
    Statement statement = connection.createStatement()){   //注意前两步都放在try()中,因为在最后需要释放资源!
    // 3. 执行SQL语句,并得到结果集
    ResultSet set = statement.executeQuery("select * from 表名");
    // 4. 查看结果
    while (set.next()){
        ...
    }
}catch (SQLException e){
    e.printStackTrace();
}
// 5. 释放资源,try-with-resource语法会自动帮助我们close

Statement的3个方法

executeQuery()方法来执行select语句,此方法返回给我们一个ResultSet对象。

executeUpdate()方法来执行一个DML或是DDL语句,它会返回一个int类型,表示执行后受影响的行数,可以通过它来判断DML语句是否执行成功。

excute()来执行任意的SQL语句,它会返回一个boolean来表示执行结果是一个ResultSet还是一个int,然后使用getResultSet()getUpdateCount()来获取需要的结果。

批处理

public static void main(String[] args) throws ClassNotFoundException {
    try (Connection connection = DriverManager.getConnection();
        Statement statement = connection.createStatement()) {
        statement.addBatch("insert into user values ('f', 1234)");
        statement.addBatch("insert into user values ('e', 1234)");   //添加每一条批处理语句
        statement.executeBatch();   //一起执行

    } catch (SQLException e){
        e.printStackTrace();
    }
}

将查询结果映射为对象

public List<User> getAllUsers() {
    String sql = "SELECT id, username, email, age FROM users";
    List<User> userList = new ArrayList<>();
    
    try (Connection conn = dataSource.getConnection();
         PreparedStatement stmt = conn.prepareStatement(sql);
         ResultSet rs = stmt.executeQuery()) {
        
        while (rs.next()) {
            // 手动映射:从ResultSet中取出数据,set到User对象中
            User user = new User();
            user.setId(rs.getLong("id"));
            user.setUsername(rs.getString("username"));
            user.setEmail(rs.getString("email"));
            user.setAge(rs.getInt("age"));
            // 如果有日期字段
            user.setCreateTime(rs.getTimestamp("create_time"));
            
            userList.add(user);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    
    return userList;
}

利用反射将查询结果映射为对象

1. 注解定义

import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String name();
}

2. 实体类

public class User {
    @Column(name = "id")
    private Long id;
    
    @Column(name = "username")
    private String username;
    
    @Column(name = "email")
    private String email;
    
    @Column(name = "age")
    private Integer age;
    
    // 必须有无参构造
    public User() {}
    
    // getter/setter (省略)
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
}

3. 核心映射器

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class SimpleMapper {
    
    public static <T> List<T> mapToList(ResultSet rs, Class<T> clazz) throws Exception {
        List<T> list = new ArrayList<>();
        
        while (rs.next()) {
            T obj = clazz.getDeclaredConstructor().newInstance();
            
            // 遍历所有字段
            for (Field field : clazz.getDeclaredFields()) {
                Column col = field.getAnnotation(Column.class);
                if (col != null) {
                    field.setAccessible(true);
                    Object value = rs.getObject(col.name());
                    field.set(obj, value);
                }
            }
            
            list.add(obj);
        }
        
        return list;
    }
}

4. 使用示例

public List<User> getAllUsers() {
    String sql = "SELECT id, username, email, age FROM users";
    
    try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc-learn", "root", "root");
         PreparedStatement stmt = conn.prepareStatement(sql);
         ResultSet rs = stmt.executeQuery()) {
        
        // 一行代码完成映射
        return SimpleMapper.mapToList(rs, User.class);
        
    } catch (Exception e) {
        e.printStackTrace();
        return new ArrayList<>();
    }
}

核心原理

  1. 通过反射获取实体类的所有字段
  2. 读取字段上的 @Column 注解获取数据库列名
  3. ResultSet 中取出对应列的值
  4. 通过反射设置到对象字段中

使用PreparedStatement

如果单纯地使用Statement来执行SQL命令,会存在严重的SQL注入攻击漏洞。我们可以使用PreparedStatement来解决,在如下代码中,我们输入的参数在最终sql语句中会被引号包裹:

public static void main(String[] args) throws ClassNotFoundException {
    try (Connection connection = DriverManager.getConnection("URL","用户名","密码");
        PreparedStatement statement = connection.prepareStatement("select * from user where username= ? and pwd=?;");
        Scanner scanner = new Scanner(System.in)) {
        statement.setString(1, scanner.nextLine());
        statement.setString(2, scanner.nextLine());
        System.out.println(statement);
        ResultSet res = statement.executeQuery();
        while (res.next()) {
            String username = res.getString(1);
            System.out.println(username+" 登陆成功!");
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

管理事务

JDBC默认的事务处理行为是自动提交,使用事务时,我们需要手动关闭自动提交。

con.setAutoCommit(false);   //关闭自动提交相当于开启事务
// SQL语句
// SQL语句
// SQL语句
con.commit();或 con.rollback();

我们也可以去创建一个回滚点Savepoint savepoint = connection.setSavepoint(); 来实现定点回滚:

public static void main(String[] args) throws ClassNotFoundException {
    try (Connection connection = DriverManager.getConnection("URL","用户名","密码");
         Statement statement = connection.createStatement()){

        connection.setAutoCommit(false);  //关闭自动提交,现在将变为我们手动提交
        statement.executeUpdate("insert into user values ('a', 1234)");
        
        Savepoint savepoint = connection.setSavepoint();   //创建回滚点
        statement.executeUpdate("insert into user values ('b', 1234)");

        connection.rollback(savepoint);   //回滚到回滚点,撤销前面全部操作

        statement.executeUpdate("insert into user values ('c', 1234)");

        connection.commit();   //提交事务(回滚之前的内容都没了)

    }catch (SQLException e){
        e.printStackTrace();
    }
}
分类: Java后端 标签: Java

评论

暂无评论数据

暂无评论数据

目录