JDBC访问数据库操作详解(二)之JDBC更多用法:以MySQL为例
一、返回主键:保存数据后返回生成的主键
利用
Connection.preparedStatement()
的重载方法
PreparedStatement preparedStatement(String sql, int autoGeneratedKeys) throws SQLException
,该方法创建一个默认的
PreparedStatement
对象,该对象具有检索自动生成的键的能力。
int autoGeneratedKeys
——指示是否应返回自动生成的键的标志;常量
Statement.RETURN_GENERATED_KEYS和Statement.NO_GENERATED_KEYS
表示生成的键可/不可用于检索。该方法获取的主键存放在
PreparedStatement
里,通过
PreparedStatement.getGeneratedKeys()
方法获取,该方法返回一个ResultSet。
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;public class Test4 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;String sql = new StringBuffer().append(\" insert into \").append(\" t_user \").append(\" (username,password,age) \").append(\" values \").append(\" (\'zhangsan\',\'123\',18) \").toString();try {conn = JdbcUtil.getConnection();//传入常量值RETURN_GENERATED_KEYS,表示返回生成的主键ps = conn.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);ps.executeUpdate();//获取存储主键的结果集rs = ps.getGeneratedKeys();while(rs.next()) {//可能是复合主键System.out.println(rs.getInt(1));}} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtil.close(conn, ps);}}}
注:JdbcUtil是数据库工具类,封装了连接数据库和关闭资源的方法,具体可参考我的上一篇博客
二、事务
Transaction事务是用来保证数据操作的完整性
-
一个业务由若干个一次性的操作组成,这些操作要么都成功,要么都失败,如银行转账(对应数据库的多个操作,A给B转钱,在数据库底层要对A做一次更新操作,对B做一次更新操作,对业务而言,这两个操作是一个整体,只有两个操作都成功才算业务成功,如果A操作成功,B操作失败,业务就是失败,那就得回滚——撤销)
-
事务的四大特性ACID
原子性(Atomicity):不可再分
一致性(Consistency):事务执行的前后,数据库是一致的
隔离性(Isolation):两个事务的操作互不干扰
持久性(Durability):事务提交后,结果被永久保存下来
JDBC默认是自动提交事务的,将每一条SQL语句都当作一个独立的事务执行
-
关闭自动提交事务
Connection.setAutoCommit(false);
-
提交事务
Connection.commit()
-
回滚事务
Connection.rollback()
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;public class Test05 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;try {conn = JdbcUtil.getConnection();//关闭自动提交事务conn.setAutoCommit(false);ps = conn.prepareStatement(\"insert into t_user values (null,\'xxx\',\'111\',20)\");ps.executeUpdate();ps = conn.prepareStatement(\"insert into t_user values (null,\'yyy\',\'111\',20)\");ps.executeUpdate();//提交事务conn.commit();System.out.println(\"同时添加两个用户成功!\");} catch (SQLException e) {//回滚事务try {conn.rollback();System.out.println(\"操作被回滚,出现异常:\"+e.getMessage());} catch (SQLException e1) {e1.printStackTrace();}} finally {JdbcUtil.close(conn, ps);}}}
三、批处理操作
当需要执行的SQL语句比较多时,每次发送一条SQL语句并执行的效率很低,此时可以批量处理并执行SQL
实现步骤:
- 在url中添加
rewriteBatchedStatements=true参数,启用批处理[ol]调用addBatch()方法,添加批处理,放到缓冲区
- 设置批处理大小,调用executeBatch(),执行批处理(批处理一次执行多少条需要人为优化)
[/ol]
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;public class Test05 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;try {conn = JdbcUtil.getConnection();//批处理前一般都关闭自动提交事务,防止出错conn.setAutoCommit(false);ps = conn.prepareStatement(\"insert into t_user values (null,?,?,?)\");for(int i = 0;i < 300000; i++) {//30万条ps.setString(1, \"name\" + i);ps.setString(2, \"123\");ps.setInt(3, 18);//添加批处理,放到缓冲区ps.addBatch();//设置批处理大小if(i % 10000 == 0) {//执行批处理ps.executeBatch();//提交事务conn.commit();}}//最后一次可能不足一万条,防止数据没被提交,再执行一次ps.executeBatch();conn.commit();} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtil.close(conn, ps);}}}
事务和批处理的区别:
-
事务:
底层是在数据库方存储SQL,没有提交事务的数据放在数据库的临时表空间
最后一次把临时表空间中的数据提交到数据库服务器执行
消耗的数据库服务器内存
-
批处理:
底层是在客户端存储SQL
最后一次把客户端存储的数据发送到数据库服务器执行
消耗的是客户端的内存
四、元数据
MetaData元数据是用来描述数据的数据,主要是描述数据属性的信息
分类:
- 数据库的元数据DatabaseMetaData,获取数据库的名称、版本等信息
- 结果集的元数据ResultSetMetaData,获取结果集中的列名,列的类型,列的属性等信息
import java.sql.Connection;import java.sql.DatabaseMetaData;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.ResultSetMetaData;import java.sql.SQLException;public class Test05 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = JdbcUtil.getConnection();/** 获取数据库元数据*/DatabaseMetaData dbmd = conn.getMetaData();System.out.println(dbmd.getDatabaseProductName());//获取数据库产品名System.out.println(dbmd.getDatabaseProductVersion());//获取数据库产品版本System.out.println(dbmd.getDriverName());//获取数据库驱动名System.out.println(dbmd.getDriverVersion());//获取数据库驱动版本System.out.println(dbmd.getUserName());//获取数据库用户名ps = conn.prepareStatement(\"select * from t_user\");rs = ps.executeQuery();/** 获取结果集元数据*/ResultSetMetaData rsmd = rs.getMetaData();System.out.println(\"总列数:\" + rsmd.getColumnCount());//获取结果集的总列数for(int i = 1; i <= rsmd.getColumnCount(); i++) {System.out.println(rsmd.getColumnName(i)+\"\\t\"+rsmd.getColumnTypeName(i));//获取列名和列的类型System.out.println(rsmd.getTableName(i));//获取该列所属的表名System.out.println(rsmd.isAutoIncrement(i));//获取该列是否自动增长System.out.println(rsmd.isNullable(i));//获取该列能否为空,0表示为not null,1表示可为nullSystem.out.println(\"-------------\");}} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtil.close(conn, ps);}}}