一、相关概念
1.JDBC的定义
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,使程序员无需对特定的数据库系统的特点有过多的了解,大大简化和加快了开发过程。
2.数据库驱动
我们安装好数据库之后,我们的应用程序也是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件。
二、常用接口
1.Driver接口
Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:
装载MySql驱动:Class.forName("com.mysql.jdbc.Driver");
装载Oracle驱动:Class.forName("oracle.jdbc.driver.OracleDriver");
DriverManager是驱动的管理类,可以通过重载的getConnection()方法获取数据库连接,较为方便;可以同时管理多个驱动程序。
2.Connection接口
Connection与特定数据库的连接(会话),在连接上下文中执行sql语句并返回结果。DriverManager.getConnection(url, user, password)方法建立在JDBC URL中定义的数据库Connection连接上。
连接MySql数据库:Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
连接Oracle数据库:Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@host:port:database", "user", "password");
常用方法:
createStatement():创建向数据库发送sql的statement对象。
prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
prepareCall(sql):创建执行存储过程的callableStatement对象。
setAutoCommit(boolean autoCommit):设置事务是否自动提交。
commit() :在链接上提交事务。
rollback() :在此链接上回滚事务。
3.Statement接口
用于执行静态SQL语句并返回它所生成结果的对象。
三种Statement类:
Statement:由createStatement创建,用于发送简单的SQL语句(不带参数)。
PreparedStatement :继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以我们一般都使用PreparedStatement。
CallableStatement:继承自PreparedStatement接口,由方法prepareCall创建,用于调用存储过程。
常用Statement方法:
execute(String sql):运行语句,返回是否有结果集
executeQuery(String sql):运行select语句,返回ResultSet结果集。
executeUpdate(String sql):运行insert/update/delete操作,返回更新的行数。
addBatch(String sql) :把多条sql语句放到一个批处理中。
executeBatch():向数据库发送一批sql语句执行。
4.ResultSet接口
ResultSet提供检索不同类型字段的方法,常用的有:
getString(int index)、getString(String columnName):获得在数据库里是varchar、char等类型的数据对象。
getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。
getDate(int index)、getDate(String columnName):获得在数据库里是Date类型的数据。
getBoolean(int index)、getBoolean(String columnName):获得在数据库里是Boolean类型的数据。
getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据。
ResultSet还提供了对结果集进行滚动的方法:
next():移动到下一行
Previous():移动到前一行
absolute(int row):移动到指定行
beforeFirst():移动resultSet的最前面。
afterLast() :移动到resultSet的最后面。
三、使用JDBC的步骤
1.注册驱动并获取数据库连接
/**
* 编写一个通用的方法,在不修改源程序的情况下,可以获取任何 数据库的连接
* 解决办法:把数据库驱动Driver实现类的全类名、url、user、password放入
* 一个配置文件中,通过修改配置文件的方式实现具体数据库的连接
*/
public Connection getConnection() throws Exception{
//1.准备连接数据库的基本信息
String driverClass = null;
String jdbcUrl = null;
String user = null;
String password = null;
//读取类路径下的jdbc.properties文件
InputStream in = getClass().getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(in);
driverClass = properties.getProperty("driver");
jdbcUrl = properties.getProperty("jdbcUrl");
user = properties.getProperty("user");
password = properties.getProperty("password");
//2.加载数据库驱动程序(对应的Driver实现类中有注册驱动的静态代码块,所以不用下面的方法,否则会产生两个一样的驱动)
//DriverManager
// .registerDriver(Class.forName(driverClass).newInstance));
Class.forName(driverClass);
//3.通过DriverManager的getConnection()方法 获取数据库连接
Connection conn = DriverManager.getConnection(jdbcUrl, user, password);
return conn;
}
jdbc.properties文件:
driver=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/test
user=root
password=1230
#driver=oracle.jdbc.driver.OracleDriver
#jdbcUrl=jdbc:oracle:thin:@localhost:1521:orcl
#user=scott
#password=tiger
注:
URL用于标识数据库的位置,通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为:
其他参数如:useUnicode=true&characterEncoding=utf8
2.创建执行SQL语句的statement
/**
* 通过JDBC向指定的数据表中插入一条记录
*/
public void testStatement(){
Connection conn = null;
Statement stat = null;
try {
//1.获取数据库连接
conn = getConnection();
//2.准备插入记录的SQL语句
String sql = "INSERT INTO customers (NAME, EMAIL, BIRTH) " +
"VALUES('XYZ', 'xyz@163.com','1999-11-11')";
//3.执行语句
//1)获取操作SQL语句的Statement 对象
stat = conn.createStatement();
//2)调用executeUpdate(sql)
stat.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
}finally{
//4.关闭Statement对象
if(stat != null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//5.关闭连接
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
SQL注入:
//Statement
String id = "5";
String sql = "delete from table where id=" + id;
Statement st = conn.createStatement();
st.executeUpdate(sql);
//存在sql注入的危险
//如果用户传入的id为“5 or 1=1”,那么将删除表中的所有记录
PreparedStatement:
是statement的子接口,可以传入带占位符的SQL语句,并且提供了补充占位符变量的方法。
对预编译语句提供性能优化,还可以防止SQL注入。
//preparedStatement
String id = "5";
String sql = "delete from table where id= ?";
PreparedStatement ps = conn.prepareStatement(sql);
//setXxx(int index,Xxx value):把sql语句中第index个占位符替换为value,index从1开始。
ps.setString(1, id);
ps.executeUpdate();
3.处理查询的执行结果(ResultSet)
/**
* 获取customers数据表的所有记录,并打印
*/
public void testResultSet() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
// 1.获取数据库连接
conn = getConnection();
// 2.获取statement
stat = conn.createStatement();
// 3.准备sql
String sql = "select id, name, email, birth from customers ";
// 4.执行查询,得到ResultSet
rs = stat.executeQuery(sql);
// 5.处理ResultSet
while (rs.next()) {
int id = rs.getInt(1);
String name = rs.getString("name");
String email = rs.getString(3);
Date birth = rs.getDate(4);
System.out.println(id);
System.out.println(name);
System.out.println(email);
System.out.println(birth);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6.关闭连接
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
注:
ResultSet返回的实际上是一张数据表,有一个指针指向表的第一行的前面。
调用 next() 方法可以检测下一行是否有效,若有效返回true,且指针下移。
当指针对位到某一行时,可以通过 getXxx(index) 或 getXxx(columnName) 获取该行中每一列的值。
ResultSet也需要关闭。