JDBC编程
JDBC简介
JDBC,Java DataBase Connectivity,是java提供的访问数据库的标准接口;不同的数据库厂商实现了该接口类作为数据库驱动,比如MySql提供的驱动jar包为mysql-connector-java;
java提供了如下几个重要的接口类:
1、Connection接口
提供了java应用程序到数据库的TCP连接;
2、Statement接口
提供数据库操作接口,executeQuery用于执行查询操作;executeUpdate用来执行insert、update、delete、DDL(删除表等)操作;execute方法可以用于执行任何操作
3、PreparedStatement接口
提供数据库操作接口,不同的是,PreparedStatement预编译sql语句,提升了执行效率,并且对于输入参数使用占位符,防止了SQL注入的发生;
注:java应用程序中永远不要使用Statement接口
4、CallableStatement接口
支持执行存储过程接口
JDBC查询操作
public void test_query() { try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD); PreparedStatement preparedStatement = connection.prepareStatement(QUERY_SQL)) { preparedStatement.setString(1, "bale"); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { String name = resultSet.getString(1); int age = resultSet.getInt(2); int sex = resultSet.getInt(3); System.out.println("name=" + name + ", age=" + age + ", sex=" + sex); } } catch (SQLException ex) { ex.printStackTrace(); } }
注:Connection和Statement是系统资源,使用完成后需要释放,这里使用try with resource语法糖进行释放;
JDBC更新操作
public void test_update() { try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD); PreparedStatement preparedStatement = connection.prepareStatement(INSERT_SQL, Statement.RETURN_GENERATED_KEYS)) { preparedStatement.setString(1, "ramos"); preparedStatement.setInt(2,32); preparedStatement.setInt(3, 2); int i = preparedStatement.executeUpdate(); System.out.println("afftected rows: " + i); ResultSet generatedKeys = preparedStatement.getGeneratedKeys(); while (generatedKeys.next()) { long aLong = generatedKeys.getLong(1); System.out.println(aLong); } } catch (SQLException ex) { ex.printStackTrace(); } }
注:executeUpdate可以执行insert、update、delete、以及数据库自带的DDL操作;可以在预编译sql时指定返回自增键值;
JDBC事务
数据库事务即一个或者一系列数据库操作,这些操作要么全部执行成功,要么全部不执行;类似于java中的多线程,数据库事务可以并发执行,与之带来的就是一系列并发问题:
1、脏读问题
事务A读取到了事务B未提交的数据,当事务B回滚时,事务A即读到了脏数据;
2、不可重复读问题
事务A多次读取数据,在多次的中间,事务B修改了数据,导致了事务A多次读取数据结果不同;
3、幻读问题
事务A多次读取数据,在多次的中间,事务B向数据库中插入了数据,导致事务A多次读取结果不同;和不可重复读的区别是:幻读强调的是表中数据条数的不同,而不可重复读强调的是某条数据发生变化,也就是说解决幻读问题需要锁表,而解决不可重复读问题只需要锁某条数据即可
针对事务并发带来的问题,数据库提供了事务隔离级别:mysql默认的事务隔离级别为read commit
脏读 | 不可重复读 | 幻读 | |
read uncommit | 存在 | 存在 | 存在 |
read commit | 存在 | 存在 | |
repeatable read | 存在 | ||
serializable |
默认情况下,一条操作即为一个事务,并且事务是自动提交的;可以通过connection.setAutoCommit(false)来取消自动提交,并且在执行数据库操作后通过connection.commit提交事务;
JDBC批次操作
对于多个操作相同,数据不同的数据操作,我们可以使用for循环来多次执行,但是这样效率很低;对于该场景,JDBC提供了batch操作,对于batch操作,整体的效率比for循环多次执行高的多
public void test_batch() { try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD); PreparedStatement preparedStatement = connection.prepareStatement(INSERT_SQL)) { for (int i = 0; i < 3; i++) { preparedStatement.setString(1, "ramos" + i); preparedStatement.setInt(2,32 + i); preparedStatement.setInt(3, 2 + i); preparedStatement.addBatch(); } preparedStatement.executeBatch(); } catch (SQLException ex) { ex.printStackTrace(); } }
JDBC连接池
和多线程类似,数据库连接的不断创建和销毁会带来很大的系统资源消耗,和线程池类似,java提供了标准的数据库连接池接口DataSource,而DataSource接口的实现类即为我们常说的数据源,常用的数据源有:c3p0数据源、druid数据源;
数据库连接通过dataSource获取,第一次获取时,创建连接;之后的获取先判断连接池中是否有空闲的连接,如果没有则创建,直到最大连接数;
而java程序中对connection的close方法调用不会真正释放连接,而是将连接标识为空闲状态
public void test_datasource() throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass("com.mysql.cj.jdbc.Driver"); dataSource.setJdbcUrl(URL); dataSource.setUser(USER); dataSource.setPassword(PASSWORD); try (Connection connection = dataSource.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(QUERY_SQL)) { preparedStatement.setString(1, "bale"); final ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { String name = resultSet.getString(1); int age = resultSet.getInt(2); int sex = resultSet.getInt(3); System.out.println("name=" + name + ", age=" + age + ", sex=" + sex); } } catch (SQLException ex) { ex.printStackTrace(); } }