<!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"/@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.5pt; mso-bidi-font-size:12.0pt; font-family:"Times New Roman"; mso-fareast-font-family:宋体; mso-font-kerning:1.0pt;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:595.3pt 841.9pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:42.55pt; mso-footer-margin:49.6pt; mso-paper-source:0; layout-grid:15.6pt;} div.Section1 {page:Section1;} -->
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();