5.7.2 JDBC 要点 下文为方便理解本章内容所整理的 JDBC 要点,需要指出的是,这一节内容不能代替 专业的 JDBC教材或者参考资料。 JDBC 要点 1. 用接口的方式将数据库分成两部分. 一部分是对开发人员提供的编程接口, java.sql.*;第 二部分是给各大厂商, 给他们是驱动接口(java.sql.Driver). DriverManager.getConnection(String url, String username, String password). java.sql.Connection ==> 实现类, 不是接口. class OracleConnection implements java.sql.Connection createStatement() -> OracleStatement implements java.sql.Statement 提供者/调用者 => 工厂模式 => 透明的开发和调用 2. 一般的 JDBC 项目(增删查改) 增删改: 能改数据 INSERT INTO TABLE_NAME [(列 1, 列 2, ...)] VALUES(值 1, 值 2, ...) DELETE TABLE_NAME [WHERE 条件子句] 列名 = 值 AND 列名 LIKE 'beijing%' OR 列 1 = 列 2 UPDATE TABLE_NAME SET 列 1 = 值 1 , 列 2 = 值 2, ... [WHERE 条件子句] 查就算是查询 SELECT *, 或者用列名 FROM [表 1 别名 1, 表 2 别名 1] [WHERE 条件子句] 两个表的查询: 表 1.id = 表 2.id 或者 别名 1.id = 别名 2.id select c.roomName from student s, classroom c where s.id = c.student_id 1) 把驱动程序加入到 classpath; 2) // 加载驱动程序 // 方式 a new com.mysql.jdbc.Driver(); // 方式 b 动态类加载 try { Class.forName("com.mysql.jdbc.Driver"); } catch(Exception e) { } // 打开数据库连接 try { String url = "jdbc:mysql://localhost:3306/test"; String username = "root"; String password = "";// 空密码可以写成 "" 或者 null Connection conn = DriverManager.getConnection(url, username, password); MyEclipse 6 Java开发中文教程 85 刘长炯著 // 数据改动用 executeUpdate(String sql) Statement stmt = conn.createStatement(); // Statement stm = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);//获得可更新和可滚动的结果集 String sql = "insert into student values(1, 'student1')"; int rows = stmt.executeUpdate(sql);// 返回改动的数据的行数 // 读取数据 executeQuery(String sql) sql = "select * from student"; ResultSet rs = stmt.executeQuery(sql); // rs = null; while(rs != null && rs.next()) { // 取数据可以根据下标或者列名 String studentname = rs.getString(2);// 下标从 1 开始 int id = rs.getInt("id");// 根据列名获取 byte[] bytes = rs.getBytes("face");// 读取二进制 } // 释放资源 rs.close(); stmt.close(); conn.close(); } catch(Exception e) { } 代码的问题在于如果中间出现异常, 那么连接资源就不能释放, 解决办法是把变量声 明放在 try-catch 语句之外; 第二把资源释放给放进 finally 里面. // 打开数据库连接 // 声明用到的资源 Connection conn = null; Statement stmt = null; ResultSet rs = null; try { String url = "jdbc:oracle:thin:@hostname:1521:tarena"; String username = "openlab"; String password = "";// 空密码可以写成 "" 或者 null conn = DriverManager.getConnection(url, username, password); MyEclipse 6 Java开发中文教程 86 刘长炯著 // 数据改动用 executeUpdate(String sql) stmt = conn.createStatement(); // Statement stm = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); String sql = "insert into student values(1, 'student1')"; int rows = stmt.executeUpdate(sql);// 返回改动的数据的行数 // 读取数据 executeQuery(String sql) sql = "select * from student"; rs = stmt.executeQuery(sql); // rs = null; while(rs != null && rs.next()) { // 取数据可以根据下标或者列名 String studentname = rs.getString(2);// 下标从 1 开始 int id = rs.getInt("id");// 根据列名获取 byte[] bytes = rs.getBytes("face");// 读取二进制 } } catch(SQLException e) { throw new 数据处理失败异常(); // throw SQLExeption } finally { // 释放资源 try { rs.close(); } catch(Exeption ex) { } try { stmt.close(); } catch(Exeption ex) { } try { conn.close(); MyEclipse 6 Java开发中文教程 87 刘长炯著 } catch(Exeption ex) { } // 简化成 // close(rs, stmt, conn); } close(ResultSet rs, Statement stmt, Connection conn) { // 释放资源 try { rs.close(); } catch(Exeption ex) { } try { stmt.close(); } catch(Exeption ex) { } try { conn.close(); } catch(Exeption ex) { } } 3. 获取结果集中有多少字段及其类型,可以用 rs.getMetaData() 来获取 ResultSetMetaData 对象, 很多框架就是用这种办法再加上反射来进行自动的属性填充操作 的,例如 Hibernate。 ResultSetMetaData 可用于获取关于 ResultSet 对象中列的类型和属性信息的对象。 以下代码片段创建 ResultSet 对象 rs,创建 ResultSetMetaData 对象 rsmd,并使用 rsmd 查找 rs 有多少列,以及 rs 中的第一列是否可以在 WHERE 子句中使用。 ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM TABLE2"); ResultSetMetaData rsmd = rs.getMetaData(); int numberOfColumns = rsmd.getColumnCount(); boolean b = rsmd.isSearchable(1); 4. PreparedStatement 的用法 PreparedStatement 继承自 Statement, 所有的 Statement 能进行的操作这里都可以MyEclipse 6 Java开发中文教程 88 刘长炯著 用. 1) 执行速度优化(预编译) 2) 简化 SQL 编写 String sql = "select * from user where username = ?"; 3) 增加安全性 SQL 注入攻击 String sql = "select * from user where username = '" + username + "'"; username 输入 1' = '1' or username = '张三 select * from user where username = '1' = '1' or username = '张三' 避免方法: a) 过滤用户输入的特殊字符 '' = ' username.replaceAll("'", "''"); b) 用 PreparedStatement.setString(下标, username) 自动转换输入的字符串为合法 的 SQL 的格式 用法: // 1. 打开 PreparedStatement pstmt = conn.createPreparedStatement("select * from user where username = ? and regdate = ?"); // 2. 设置要处理的数据 pstmt.setString(1, "张三"); java.util.Date now = new java.util.Date(); pstmt.setDatetime(2, new java.sql.Date(now.getTime()) );// 设置日期 // 3. 执行查询或者更新 ResultSet rs = pstmt.executeQuery();// rs = pstmt.executeQuery("select * ..:");// int rows = pstmt.executeUpdate();// 更新 3. CallableStatement 用来调用存储过程(了解) 在 JDBC 中调用已储存过程的语法如下所示。注意,方括号表示其间的内容是可选项;方 括号本身并不是语法的组成部份。 {call 过程名[(?, ?, ...)]} 返回结果参数的过程的语法为: {? = call 过程名[(?, ?, ...)]} 不带参数的已储存过程的语法类似: {call 过程名} 示例代码: String procedure="{call Operator_login(?,?,?)}"; //注册存储过程 MyEclipse 6 Java开发中文教程 89 刘长炯著 CallableStatement callStmt=conn.prepareCall(procedure); //注册存储过程输出参数的类型 callStmt.registerOutParameter(3,java.sql.Types.INTEGER); //提供输入参数的值 callStmt.setString(1,this.operatorID); callStmt.setString(2,this.password); //执行存储过程 callStmt.execute(); //返回输出参数 login_state=callStmt.getInt(3); CallableStatement cs = conn.prepareCall("{call ec_get_cust_terms(?)}"); cs.setInt(1, custNo); rs = cs.executeQuery(); |