4.11.1 如何通过JDBC访问数据库
Java数据库连接(Java DataBase Connectivity -JDBC)用于在java程序中实现数据库操作功能,它提供了执行SQL语句,访问各种数据库的方法,并为各种不同的数据库提供统一的操作接口。
java.sql包中包含了JDBC操作数据库的所有类。
通过JDBC访问数据库一般有如下几个步骤:
1)加载JDBC驱动器。
将数据库的JDBC驱动器加载到classpath中,在基于JavaEE的Web应用开发过程中,通常要把目标数据库产品的JDBC驱动复制到WEB-INF/lib下。
2)加载JDBC驱动,并将其注册到DriverManager中。一般使用反射Class.forName(String driverName)
3) 建立数据库连接,取得Connection对象,一般通过DriverManager.getConnection(url,username,password)方法实现
4)建立Statement对象或是PreparedStatement对象。
5)执行SQL语句。
6)访问结果集ResultSet对象
7)依次将ResultSet,Statement,PreparedStatement,Connection对象关闭,释放掉所占用的资源。
例如rs.close(),con.close()等。为啥要这么做,原因在于JDBC驱动在底层通常是通过网络IO实现SQL命令与数据传输的。
笔1:举出一个用JDBC访问MySQL的例子
package cn.itcast.demo1;
/*
*
* JDBC开发步骤
1. 注册驱动
告知JVM使用的是哪一个数据库的驱动
2. 获得连接
使用JDBC中的类,完成对MySQL数据库的连接
3. 获得语句执行平台
通过连接对象获取对SQL语句的执行者对象
4. 执行sql语句
使用执行者对象,向数据库执行SQL语句
获取到数据库的执行后的结果
5. 处理结果
6. 释放资源 一堆close()
*/
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Test {
//创建四个常量来存放地址、用户名、密码和驱动 ctrl +shift+x 变为大写
private static final String url = "jdbc:mysql://localhost:3306/mybase";
private static final String username = "root";
private static final String password = "123";
private static final String driver = "com.mysql.jdbc.Driver";
public static void main(String[] args) throws Exception {
//1.注册驱动 反射技术,将驱动类加入到内容
// 使用java.sql.DriverManager类静态方法 registerDriver(Driver driver)
// Diver是一个接口,参数传递,MySQL驱动程序中的实现类
//DriverManager.registerDriver(new Driver());
//驱动类源代码,注册2次驱动程序
//Class.forName("com.mysql.jdbc.Driver");
//2.获得数据库连接 DriverManager类中静态方法
//static Connection getConnection(String url, String user, String password)
//返回值是Connection接口的实现类,在mysql驱动程序
//url: 数据库地址 jdbc:mysql://连接主机IP:端口号//数据库名字
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
//1.注册驱动 反射技术,将驱动类加入到内容
Class.forName(driver);
//2.获得连接
//static Connection getConnection(String url, String user, String password)
//返回值是Connection接口的实现类,在mysql驱动程序
//url:数据库的链接地址 格式:jdbc:mysql://连接主机IP:端口号//数据库的名字!
con = DriverManager.getConnection(url, username, password);
//3.获得语句执行平台,通过数据库连接对象,获取到SQL语句的执行者对象
//con对象调用方法 Statement createStatement()
//创建一个 Statement 对象来将 SQL 语句发送到数据库
//返回值是Statement接口的实现类对象
//import java.sql.Statement;
stmt = con.createStatement();
//4.调用执行者对象方法,执行SQL语句获取结果集
stmt.execute("INSERT INTO Employee VALUES(1,'James1',25)");
stmt.execute("INSERT INTO Employee VALUES(2,'James2',26)");
//5.处理结果集
rs = stmt.executeQuery("SELECT * FROM Employee");
while(rs.next()){
System.out.println(rs.getInt(1) + "\t" + rs.getString(2) + "\t" + rs.getInt(3));
}
} catch (SQLException e) {
System.out.println(e.getMessage());
}finally {
//6.释放资源 一堆close()
try {
if(rs != null){
rs.close();
}
if(stmt != null){
stmt.close();
}
if(con != null){
con.close();
}
} catch (SQLException e2) {
System.out.println(e2.getMessage());
}
}
}
}
4.11.2 JDBC处理事务采用什么方法?
一个事务是由一条或多条对数据库的操作的sql语句所组成的一个不可分割的工作单元,只有当事务中的所有操作都正常执行完了,整个事务才会被提交给数据库。
在jdbc中,一般是通过commit()方法或者rollback()方法来结束事务的操作
commit()方法表示完成对事务的提交
rollback()方法表示完成事务回滚,多用于在处理事务的过程中出现异常的情况。
这两种方法都位于java.sql.Connection类中
一般而言,事务默认操作是自动提交,即操作成功后,系统将自动调用commit()方法,否则将调用rollbak()方法。
在jdbc中,也可以通过调用setAutoCommit(false)方法来禁止自动提交,然后就可以把多个数据库操作的表达式作为一个事务,在操作完成后调用commit()方法实现整体提交。
如果其中一个表达式操作失败,就会抛出异常而不会调用commmit()方法。
在这种情况下就可以在异常捕获的代码块中调用rollback()方法进行事务回滚。通过此方法可以保持对数据库的多次操作后,数据仍然保持一致性。
引申:jdbc有哪些事务隔离级别?
为了解决“多个线程请求相同数据”相关的问题,事务之间通常会用锁相互隔离开。
在jdbc中,定义了以下5种事务隔离级别:
1)TRANSACTION_NONE JDB.不支持事务
2)TRANSACTION_READ_UNCOMMITTED.未提交读。说明在提交前一个事务可以看到另一个事务的变化。这样读脏数据,不可重复读和虚读都是允许的。
3)TRANSACTION_READ_COMMITTED.已提交读。说明读取未提交的数据是不允许的,这个级别仍然允许不可重复读和虚读产生。
4)TRANSACTIONN_REPEATABLE_READ.可重复读,说明事务保证能够再次读取相同的数据而不会失败,但是虚读仍然会出现。
5)TRANSACTION_SERIALIZABLE.可序列化。是最高的事务级别,它防止读脏数据,不可重复读和虚读。
脏数据:一个事务读取了另一个事务尚未提交的数据。
不可重复读:一个事务的操作导致另一个事务前后两次读取到不同的数据。
虚读:一个事务的操作导致另一个事务前后两次查询的结果数据量不同。
事务隔离级别越高,为避免冲突所花的精力也越多,可以通过Connection对象的conn.setTransactionLevel()方法来设置隔离级别,
通过conn.getTransactionIsolation()方法来确定当前事务的级别。
4.11.3 Class.forName()的作用是什么?
在java语言中,任何类只有被装载到JVM上才能运行,Class.forName()方法的作用就是把类加载到JVM中。
它会返回一个与带有给定字符串名的类或接口相关联的Class对象,并且JVM会加载这个类,同时JVM会执行该类的静态代码段。
在使用JDBC连接数据库时,一般都会调用Class.forName(“com.mysql.jdbc.Driver”)方法来加载JDBC驱动,那么是否一定需要调用这个方法呢?为什么?
并不是一定要调用这个方法
例如:
Test t = (Test)Class.forName(“Test”).newInstance()语句和
Test t = new Test()
具有相同的效果
区别:创建对象的方式不同,
前者使用类加载机制,提高软件的可扩展性。
后者创建了一个新的类
JDBC规范中要求Driver类在使用前必须想DriverManager注册自己,所以,当执行Class.forName(“com.mysql.jdbc.Driver”)时,JVM会加载名为“com.mysql.jdbc.Driver”对应的Driver类,而com.mysql.jdbc.Driver类的实现如下所示:
在调用Class.forName()方法时,这个Driver类被加载了,由于静态部分被执行,因此Driver也被注册到了DriverManager中
4.11.4 Statement,PreparedStatement和CallableStatement有什么区别?
Statement用于执行不带参数的简单的SQL语句,并返回它所生成的结果的对象。每次执行sql语句时,数据库都要编译该sql语句。
以下是一个最简单的sql语句。
Statement stat = conn.createStatement();
stat.execute("insert into Employee values(1,'zhangsan',23)");
PrepareStatement表示预编译的SQL语句的对象,用于执行带参数的预编译的sql语句。
CallableStatement则提供了用来调用数据库中存储过程的接口,如果有输出参数要注册,说明是输出参数。
例:PreparedStatement的例子
package cn.itcast.demo1;
/*
*
* JDBC开发步骤
1. 注册驱动
告知JVM使用的是哪一个数据库的驱动
2. 获得连接
使用JDBC中的类,完成对MySQL数据库的连接
3. 获得语句执行平台
通过连接对象获取对SQL语句的执行者对象
4. 执行sql语句
使用执行者对象,向数据库执行SQL语句
获取到数据库的执行后的结果
5. 处理结果
6. 释放资源 一堆close()
*/
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Test1 {
//创建四个常量来存放地址、用户名、密码和驱动 ctrl +shift+x 变为大写
private static final String url = "jdbc:mysql://localhost:3306/mybase";
private static final String username = "root";
private static final String password = "123";
private static final String driver = "com.mysql.jdbc.Driver";
public static void main(String[] args) throws Exception {
//1.注册驱动 反射技术,将驱动类加入到内容
// 使用java.sql.DriverManager类静态方法 registerDriver(Driver driver)
// Diver是一个接口,参数传递,MySQL驱动程序中的实现类
//DriverManager.registerDriver(new Driver());
//驱动类源代码,注册2次驱动程序
//Class.forName("com.mysql.jdbc.Driver");
//2.获得数据库连接 DriverManager类中静态方法
//static Connection getConnection(String url, String user, String password)
//返回值是Connection接口的实现类,在mysql驱动程序
//url: 数据库地址 jdbc:mysql://连接主机IP:端口号//数据库名字
Connection con = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
//1.注册驱动 反射技术,将驱动类加入到内容
Class.forName(driver);
//2.获得连接
//static Connection getConnection(String url, String user, String password)
//返回值是Connection接口的实现类,在mysql驱动程序
//url:数据库的链接地址 格式:jdbc:mysql://连接主机IP:端口号//数据库的名字!
con = DriverManager.getConnection(url, username, password);
//3.获得语句执行平台,通过数据库连接对象,获取到SQL语句的执行者对象
//con对象调用方法 Statement createStatement()
//创建一个 Statement 对象来将 SQL 语句发送到数据库
//返回值是Statement接口的实现类对象
//import java.sql.Statement;
stmt = con.prepareStatement("SELECT * FROM Employee WHERE uid = ?");
stmt.setInt(1,1);//传递参数(第一个问号,传递的值)
//4.调用执行者对象方法,执行SQL语句获取结果集
rs = stmt.executeQuery();
//5.处理结果集
while(rs.next()){
System.out.println(rs.getInt(1) + "\t" + rs.getString(2) + "\t" + rs.getInt(3));
}
} catch (SQLException e) {
System.out.println(e.getMessage());
}finally {
//6.释放资源 一堆close()
try {
if(rs != null){
rs.close();
}
if(stmt != null){
stmt.close();
}
if(con != null){
con.close();
}
} catch (SQLException e2) {
System.out.println(e2.getMessage());
}
}
}
}
虽然Statement对象与PreparedStatement对象能完成相同的功能,但是相比之下,
PreparedStatement具有以下优点:
1)效率更高
2)代码的可读性和可维护性更好。
3)安全性更好,能够预防SQL注入攻击
select * from user where name='aa' and password='bb' or 1=1
CallableStatement由preparedCall()方法所创建,它为所有DBMS提供了一种以标准形式调用已存储过程的方法。
它从PreparedStatement中继承了用于处理输入参数的方法,而且还增加了调用数据库中的存储过程和函数以及设置输出类型参数的功能。
4.11.5 getString()方法与getObject()方法有什么区别?
JDBC提供了getString(),getInt(),和getDate()等方法从ResultSet中获取数据。
getString和getInt等方法在被调用时,程序会一次性把数据都放到内存中,然后通过调用ResultSet的next和getString等方法来获取数据。
当数据量太大到内存中放不下就会报异常。
getObject()每次调用会直接去数据库中获取数据,不会因为数据量太大抛出异常。
4.11.6 使用JDBC需要注意什么?
一定要释放不再使用的连接。
createStetement和prepareStatement最好放在循环外面,并且需要及时关闭。
4.11.7 什么是JDO?
java数据对象是一个用于存取某种数据仓库中的对象的标准化API,它使开发人员能够间接地访问数据库。
JDO是JDBC的一个补存,提供了透明的对象存储。
4.11.8 JDBC与Hibernate有什么区别?
Hibernate是JDBC的封装,采用配置文件的形式将数据库的连接参数写到xml文件中,至于对数据库的访问还是通过JDBC来完成的。
Hibernate是一个持久层框架,它将表的信息映射到XML文件中,再从XML文件映射到相应的持久化类中,这样就可以使用Hibernate独特的查询语言hql了。
1)Hibernate的HQL查询语言返回的是List<Object[.]>类
JDBC返回的是ResultSet并且有时候需要自己封装到List中。
2)Hibernate具有访问层,dao层类,具有很好的维护性和扩展性。