其他分享
首页 > 其他分享> > 20201115 JDBC

20201115 JDBC

作者:互联网

JDBC 代码

AutoCloseable

public class AutoCloseableTest {
    public static void main(String[] args) {
        try (AutoCloseableObject app = new AutoCloseableObject()) {
            System.out.println("--执行main方法--");
            int i = 1 / 0;
        } catch (Exception e) {
            System.out.println("--exception--");
        } finally {
            System.out.println("--finally--");
        }
    }

    //自定义类 并实现 AutoCloseable
    public static class AutoCloseableObject implements AutoCloseable {
        @Override
        public void close() throws Exception {
            System.out.println("--close--");
            throw new RuntimeException("xxx");
        }
    }
}
/*
* 
* 执行结果:
* 
    --执行main方法--
    --close--
    --exception--
    --finally--
* 
* */

使用 Statement

/**
 * JDBC 使用 Statement 执行 sql 语句
 *
 * @throws SQLException
 * @throws ClassNotFoundException
 */
@Test
public void testStatement() throws SQLException, ClassNotFoundException {

    String dbUrl = "jdbc:mysql://192.168.181.129:3306/mybatis";
    String dbUser = "root";
    String dbPassword = "123456";
    String sql = "select * from user";

    // 1. 注册驱动,可省略
    // 加载类 DriverManager 时,会以 SPI 的方式将驱动加载
    // {@code java.sql.DriverManager#loadInitialDrivers}
    Class.forName("com.mysql.jdbc.Driver");

    // 2. 获取连接,表示 JVM 进程与数据库进程之间的通道打开了
    // 3. 获取数据库操作对象,专门执行 sql 语句的对象
    // 4. 执行 sql 语句,获取结果集

    // 6. try-with-resources 释放资源
    try (Connection connection = DriverManager.getConnection(dbUrl, dbUser, dbPassword); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) {

        // 5. 处理查询结果集
        while (resultSet.next()) {
            int id = resultSet.getInt(1);
            String username = resultSet.getString("username");

            User user = new User();
            user.setId(id);
            user.setUsername(username);
            System.out.println(user);
        }
    }
}

使用 PreparedStatement

/**
 * JDBC 使用 PreparedStatement 执行 sql 语句
 *
 * @throws SQLException
 * @throws ClassNotFoundException
 */
@Test
public void testPreparedStatement() throws SQLException, ClassNotFoundException {

    String dbUrl = "jdbc:mysql://192.168.181.129:3306/mybatis";
    String dbUser = "root";
    String dbPassword = "123456";
    String sql = "select * from user where id = ?";

    // 1. 注册驱动,可省略
    // 加载类 DriverManager 时,会以 SPI 的方式将驱动加载
    // {@code java.sql.DriverManager#loadInitialDrivers}
    Class.forName("com.mysql.jdbc.Driver");

    // 2. 获取连接,表示 JVM 进程与数据库进程之间的通道打开了
    // 3. 获取数据库操作对象,专门执行 sql 语句的对象
    // 4. 执行 sql 语句,获取结果集

    // 6. try-with-resources 释放资源
    try (Connection connection = DriverManager.getConnection(dbUrl, dbUser, dbPassword); PreparedStatement preparedStatement = getPreparedStatement(sql, connection); ResultSet resultSet = preparedStatement.executeQuery()) {

        // 5. 处理查询结果集
        while (resultSet.next()) {
            int id = resultSet.getInt(1);
            String username = resultSet.getString("username");

            User user = new User();
            user.setId(id);
            user.setUsername(username);
            System.out.println(user);
        }
    }
}

private PreparedStatement getPreparedStatement(String sql, Connection connection) throws SQLException {
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setInt(1, 2);
    return preparedStatement;
}

对比 Statement 和 PreparedStatement

操作 Blob 类型数据

// 1. 插入 Blob
sql = "insert into user(id, username, photo) values(?, ?, ?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 3);
preparedStatement.setString(2, "u3");

// 设置 Blob 内容
InputStream inputStream = Files.newInputStream(new File("lm.jpg").toPath());
preparedStatement.setBlob(3, inputStream);

int rowCount = preparedStatement.executeUpdate();
if (rowCount > 0) {
    System.out.println("插入成功");
} else {
    System.out.println("插入失败");
}

// 2. 读取 Blob
sql = "select * from user where id = 3";
resultSet = preparedStatement.executeQuery(sql);
if (resultSet.next()) {
    Blob photo = resultSet.getBlob("photo");
    InputStream InputStream = photo.getBinaryStream();
    Files.copy(InputStream, new File("test.jpg").toPath());
    System.out.println("读取成功");
}

批量插入数据

@Test
public void testBatch() throws SQLException {

    long start = System.currentTimeMillis();

    sql = "insert into user(username) values(?)";
    preparedStatement = connection.prepareStatement(sql);
    for (int i = 0; i < 1000000; i++) {
        preparedStatement.setString(1, "batch-" + i);
        // preparedStatement.executeUpdate();   // 10w - 57s

        preparedStatement.addBatch();           // 100w - 4s

        if ((i + 1) % 1000 == 0) {
            preparedStatement.executeBatch();
            preparedStatement.clearBatch();
        }
    }

    long end = System.currentTimeMillis();
    System.out.println("共花费时间:" + (end - start) / 1000);
}

手动控制事务

@Test
public void testTx() throws SQLException {
    // 设置关闭自动提交
    connection.setAutoCommit(false);
    statement = connection.createStatement();
    sql = "insert into user(username) values('1')";
    statement.executeUpdate(sql);

    // 测试,抛出异常
    // int i = 1 / 0;

    sql = "insert into user(username) values('2')";
    statement.executeUpdate(sql);

    // 手动提交
    connection.commit();

    // 还原默认设置
    // 如果使用连接池,需要恢复自动提交设置
    connection.setAutoCommit(true);

    System.out.println("执行完成");
}

事务隔离级别

事务的 ACID 属性

数据库的并发问题

隔离级别

隔离级别 描述 被解决的并发问题
READ UNCOMMITTED 读未提交
READ COMMITTED 读已提交 脏读
REPEATABLE READ 可重复读 脏读、不可重复读
SERIALIZABLE 串行化 脏读、不可重复读、幻读

悲观锁和乐观锁

悲观锁 乐观锁
概念 查询时直接锁住记录使得其它事务不能查询,更不能更新 提交更新时检查版本或者时间戳是否符合
语法 select ... for update 使用 version 或者 timestamp 进行比较
实现者 数据库本身 开发者
适用场景 并发量大 并发量小
类比Java Synchronized 关键字 CAS 算法

悲观锁

又称 行级锁

  1. 准备数据表

    DROP TABLE IF EXISTS employee;
    
    CREATE TABLE IF NOT EXISTS employee (
      id      INTEGER NOT NULL,
      money   INTEGER,
      version INTEGER,
      PRIMARY KEY (id)
    )
      ENGINE = INNODB;
    
    INSERT INTO employee VALUE (1, 0, 1);
    
    SELECT * FROM employee;
    
  2. 开启两个终端,分别执行以下语句

    -- 自动提交
    set autocommit = false;
    -- 加悲观锁
    select * from employee where id = 1 for update; 
    
  3. 第二个终端被挂起,无法执行完成,超时后报错

    select * from employee where id = 1 for update
    > 1205 - Lock wait timeout exceeded; try restarting transaction
    > 时间: 51.043s
    

乐观锁

execute、executeUpdate、executeQuery三者的区别

  1. ResultSet executeQuery(String sql);

    执行SQL查询,并返回 ResultSet 对象。

  2. int executeUpdate(String sql);

    可执行增,删,改,返回执行受到影响的行数。

  3. boolean execute(String sql);

    可执行任何SQL语句,返回一个布尔值,表示是否返回 ResultSet ,如果返回,可以通过 getResultSet 方法获取

参考资料

标签:JDBC,String,20201115,--,preparedStatement,user,sql,id
来源: https://www.cnblogs.com/huangwenjie/p/13975659.html