利用JDBC API可以通过java简便的操作数据库。在java8以及以后的版本中jdbc-odbc桥连接已经舍弃。对于不同的数据库要到对应的官网中下载驱动。
下面给出三种常用数据库驱动名,驱动类和对应的连接字符串名(x代表版本)
数据库 | 驱动 jar | 具体驱动类名 | 连接字符串 |
---|---|---|---|
Oracle | ojdbc-x.jar | oracle.jdbc.OracleDriver | jdbc:oracle:thin:@localhost:1521:ORCL |
MySQL | mysql-connector-java-x.jar | com.mysql.jdbc.Driver | jdbc:mysql://localhost:33006/数据库实例名 |
SQLServer | sqljdbc-x.jar | com.microsoft.sqlserver.jdbc.SQLServerDriver | jdbc:microsoft:sqlserver:localhost:1433:databasename=数据库实例名 |
JDBC操作数据库的四个步骤
- 倒入驱动,加载具体的驱动类
- 与数据库建立连接
- 执行SQL
- 处理结果集(针对查询结果集)
通过JDBC API 不同的数据库操作起来都差不多,这里以SQLServer为例,不同数据库差别就在驱动与连接字符串。
import java.sql.*;
/**
* JDBC操作数据库的四个步骤
* a.倒入驱动,加载具体的驱动类
* b.与数据库建立连接
* c.执行SQL
* d.处理结果集
* @author 1
*
*/
public class JDBCDemo{
private static String URL = "jdbc:sqlserver://127.0.0.1:1433;databaseName=学生数据库";
private static String user = "sa";
private static String pwd = "123";
public static void update(String sql){ //增删改
Statement stmt = null;
Connection con = null;
try {
//a.倒入驱动,加载具体的驱动类
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
//b.与数据库建立连接
con = DriverManager.getConnection(URL, user, pwd);
//c.执行SQL
stmt = con.createStatement();
int count = stmt.executeUpdate(sql); //返回影响的结果集数
//d.处理结果集
if(count>0) {
System.out.println("操作成功,影响了"+count+"行");
}
}catch(ClassNotFoundException e) {
e.printStackTrace();
System.out.println("无法加载驱动");
}catch(SQLException e) {
e.printStackTrace();
System.out.println("执行SQL失败");
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
if(stmt!=null) stmt.close();
if(con!=null) con.close();
}catch(SQLException e) {
e.printStackTrace();
System.out.println("关闭失败");
}catch(Exception e) {
e.printStackTrace();
}
}
}
public static void query(){ //查
Statement stmt = null;
Connection con = null;
ResultSet rs = null;
try {
//a.倒入驱动,加载具体的驱动类
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
//b.与数据库建立连接
con = DriverManager.getConnection(URL, user, pwd);
//c.执行SQL
String sql = "select * from Course";
stmt = con.createStatement();
rs = stmt.executeQuery(sql);
//d.处理结果集
while(rs.next()) {
System.out.print(rs.getString("Cno")+" ");
System.out.print(rs.getString("Cname")+" ");
System.out.print(rs.getInt("Credit")+" ");
System.out.println(rs.getInt("Semester"));
}
}catch(ClassNotFoundException e) {
e.printStackTrace();
System.out.println("无法加载驱动");
}catch(SQLException e) {
e.printStackTrace();
System.out.println("执行SQL失败");
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
if(rs!=null) rs.close();
if(stmt!=null) stmt.close();
if(con!=null) con.close();
}catch(SQLException e) {
e.printStackTrace();
System.out.println("关闭失败");
}catch(Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
String sql = "update Course set Credit = 4 where Cno='C001'";
JDBCDemo.update(sql);
query();
}
}
Statement 和 preparedStatement
Statement 和 preparedStatement都可以用来进行增删改查。
Statement
statement 对象可以通过connection对象的createStatement()方法产生。
createStatement()方法原型 Statement java.sql.Connection.createStatement() throws SQLException
常用方法:
- executeUpdate(sql);
用于进行数据库更新(增删改)
函数原型:int java.sql.Statement.executeUpdate(String sql) throws SQLException
返回值为int,即进行更新影响到的几条记录。 - executeQuery(sql) ;
用于数据库查询
函数原型:ResultSet java.sql.Statement.executeQuery(String sql) throws SQLException
返回值为ResultSet类型。用于保存数据库结果集的数据表,ResultSet对象指向当前数据行的光标,最初光标被置于第一行之前(类似于迭代器)。
通过next()方法可将光标移动到下一行,没有下一行时返回false,否则返回true。
通过getXXX(“属性名”)的方法获得属性值
rs = stmt.executeQuery(sql);
//d.处理结果集
while(rs.next()) {
System.out.print(rs.getString("Cno")+" ");
System.out.print(rs.getString("Cname")+" ");
System.out.print(rs.getInt("Credit")+" ");
System.out.println(rs.getInt("Semester"));
}
preparedStatement
preparedStatement拓展了Statement
原型:public abstract interface java.sql.PreparedStatement extends java.sql.Statement
所以Statement的所有方法preparedStatement都可使用,除此之外preparedStatement还多了很多set方法:
由connection对象的prepareStatement(String sql)方法产生。
函数原型:PreparedStatement java.sql.Connection.prepareStatement(String sql) throws SQLException
(注:构建Statement对象的createStatement()不许参数sql,SQL语句是最后执行execute的时候传入的。)
接口preparedStatement用于表示预编译的SQL语句对象。这里的SQL语句通常含有占位符?。SQL语句被预编译并存储在preparedStatement对象中,这样可以提高执行该SQL语句的效率。
//这里省略了try、catch,模板与上面的大致相同
//a.倒入驱动,加载具体的驱动类
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
//b.与数据库建立连接
con = DriverManager.getConnection(URL, user, pwd);
//c.执行SQL
String sql = "select Sno,Sname from Student where Sname like ?";
pstmt = con.prepareStatement(sql); //预编译
pstmt.setString(1,"张%"); //查询姓李的学生
rs = pstmt.executeQuery();
//d.处理结果集
while(rs.next()) {
System.out.print(rs.getString("Sno")+" ");
System.out.print(rs.getString("Sname")+" ");
System.out.println();
}
set方法用于设置占位符的值,第一个参数为对应占位符在SQL语句出现的顺序(从1开始),第二个参数为值。
preparedStatement较Statement的优点
1)编写代码更简便(避免字符串拼接的麻烦–采用占位符)
2)提高性能(采用预编译)
3)更安全(有效防止SQL注入)
例如:对于一个登陆操作(使用statement)
用户输入用户名:XXX ’ or 1=1 –
密码:XXX
这样查询语句就由
select count(*)from login where uname='name' and upwd ='pwd'
变成了
select count(*)from login where uname='XXX' or 1=1 --' and upwd ='XXX'
–后的被注释
or 1=1 永真所以能登陆成功。
但是使用preparedStatement时,用户输入会被当成一个整体,危险输入会被转义,所以更安全。
CallableStatement
CallableStatement用于调用储蓄过程或者储蓄函数
通过connection对象的prepareCall(String sql)方法产生CallableStatement对象。
注:这里的sql有格式要求
对于储蓄过程:sql={call 储蓄过程名(参数列表)}
对于存储函数:sql={?= call 存储函数名(参数列表)}
有几个参数就用几个占位符?代替
通过setXXX(index,value)设置参数值
通过registerOutParameter(index,SQLType)设置输出参数类型
调用execute()后
通过getXXX()接收输出参数。
例:储蓄过程p3用于统计学校某系的男生人数,输入参数为系,输出参数为该系男生人数
ALTER PROCEDURE p3 @dept VARCHAR(10),@number INT OUTPUT
AS
SELECT @number = COUNT(Sno) FROM Student WHERE Sdept = @dept AND Ssex = '男'
//a.倒入驱动,加载具体的驱动类
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
//b.与数据库建立连接
con = DriverManager.getConnection(URL, user, pwd);
//获取CallableStatement对象
cstmt = con.prepareCall("{call p3(?,?)}");
//为参数赋值
cstmt.setString(1, "计算机系");
//设置输出结果的数据类型
cstmt.registerOutParameter(2, Types.INTEGER);
//执行
cstmt.execute();
//获取输出结果
int result = cstmt.getInt(2);
System.out.println(result);