问题及答案来源自《Java程序员面试笔试宝典》第四章 Java基础知识 4.11Java数据库操作
1、如何通过JDBC访问数据库
什么是JDBC:
Java数据库连接(Java database connectivity,JDBC)用于在Java程序中实现数据库操作功能,它提供了执行SQL语句、
访问各种数据库的方法,并为各种不同的数据库提供统一的操作接口,java.sql包中包含了JDBC操作数据库的所有类
通过JDBC访问数据库的步骤如下:
- 加载JDBC驱动器,把数据库的JDBC驱动加载到classpath中(把目标数据库的JDBC驱动复制到项目的lib文件夹下)
- 加载JDBC驱动,并将其注册到DriverManager中,一般使用反射Class.forName(String driveName)方式实现
- 建立数据库连接,取得Connection对象,一般通过DriverManager.getConnection(url, username, password)方式实现
- 建立Statement对象或是PreparedStatement对象
- 执行SQL语句
- 访问结果集ResultSet对象
- 依次将上述对象关闭,释放掉所占用的资源
用JDBC访问MySQL实例(看看就行):
1 import java.util.*; 2 import java.sql.DriverManager; 3 import java.sql.ResultSet; 4 import java.sql.Statement; 5 import java.sql.Connection; 6 import java.sql.PreparedStatement; 7 8 // 远程数据库访问接口 9 public class DataBase{ 10 11 // 数据库配置系列: 12 private static final String DRIVER = "com.mysql.jdbc.Driver"; 13 private static final String DB_URI = "jdbc:mysql://localhost:3306/game_test"; 14 private static final String DB_USER = "root"; 15 private static final String DB_PWD = "root"; 16 17 // SQL语句(MySQL): 18 // 查询前五个最高分的用户名和分数 19 private static final String LOAD_SQL = "SELECT user_name, point FROM user_point WHERE game_type = 1 ORDER BY point DESC limit 5"; 20 // 存储用户名和分数 21 private static final String SAVE_SQL = "INSERT INTO user_point(user_name, point, game_type) VALUES (?, ?, ?)"; 22 23 // 静态构造函数 -> 类加载的时候调用 24 static { 25 try { 26 Class.forName(DRIVER); // 加载类 =》加载JDBC驱动 27 } catch (ClassNotFoundException e) { 28 e.printStackTrace(); 29 } 30 } 31 32 @Override 33 public List<Player> loadData() { 34 Connection conn = null; 35 ResultSet rs = null; 36 List<Player> players = new ArrayList<Player>(); 37 try { 38 // 建立数据库连接 39 conn = DriverManager.getConnection(DB_URI, DB_USER, DB_PWD); 40 // 建立Statement对象 41 Statement stmt = conn.createStatement(); 42 // 执行SQL语句(导出数据) 43 rs = stmt.executeQuery(LOAD_SQL); 44 // 访问结果集ResultSet对象 将数据保存到List中 45 while(rs.next()){ 46 players.add(new Player(rs.getString(1), rs.getInt(2))); 47 } 48 stmt.close(); 49 } catch (Exception e) { 50 e.printStackTrace(); 51 } finally{ 52 // 关闭对象并释放资源 53 try{ 54 if(conn!=null){ 55 conn.close(); 56 } 57 if(rs!=null){ 58 rs.close(); 59 } 60 } catch(Exception e){ 61 e.printStackTrace(); 62 } 63 64 } 65 66 return players; 67 } 68 69 @Override 70 public void saveData(Player player) { 71 Connection conn = null; 72 PreparedStatement stmt = null; 73 try { 74 // 建立数据库连接 75 conn = DriverManager.getConnection(DB_URI, DB_USER, DB_PWD); 76 // 建立prepareStatement对象 77 stmt = conn.prepareStatement(SAVE_SQL); 78 stmt.setObject(1, player.getName()); 79 stmt.setObject(2, player.getPoint()); 80 stmt.setObject(3, 1); 81 // 执行SQL语句(保存) 82 stmt.execute(); 83 } catch (Exception e) { 84 e.printStackTrace(); 85 } finally{ 86 // 关闭对象并释放资源 87 try{ 88 if(conn!=null){ 89 conn.close(); 90 } 91 if(stmt!=null){ 92 stmt.close(); 93 } 94 } catch(Exception e){ 95 e.printStackTrace(); 96 } 97 98 } 99 100 } 101 102 }
2、JDBC处理事务采用什么方法?
什么是事务:
一个事务是由一条或多条对数据库操作的SQL语句所组成的一个不可分割的工作单元,只有当事务中的所有操作
都正常执行完了,整个事务才会被提交给数据库。
JDBC中事务的操作:
- commit方法或rollback方法:都是结束事务的操作
- commit():表示完成对事务的提交,事务操作成功后系统将自动调用commit方法
- rollback():表示完成事务回滚,多用于在处理事务的过程中出现了异常的情况
当然在JDBC中,也可以通过调用setAutoCommit(false)方法来禁止自动提交,然后就可以把多个数据库操作的表达式作为
一个事务,在操作完成后调用commit方法,在这种情况下就可以在异常捕获的代码块中调用rollback方法进行事务回滚
通过这种方法可以保证对数据库的多次操作后,数据仍然保持一致性
JDBC的事务隔离级别(由低到高):
TRANSACTION_NONE JDB:不支持事务
TRANSACTION_READ_UNCOMMITTED:未提交读,说明在提交前一个事务可以看到另一个事务的变化,
这样读脏数据、不可重复读和虚读都是允许的
TRANSACTION_READ_COMMITTED:已提交读,说明读取未提及的数据是不允许的, 这样不可重复读和
虚读都是允许的
TRANSACTION_REPEATABLE_READ:可重复读,说明事务保证能够再次读取相同的数据而不会失败,但
虚读仍然会出现
TRANSACTION_SERIALIZABLE:可序列化,是最高的事务等级,它防止读脏数据、不可重复读和虚读
关于读脏数据、不可重复读和虚读:
读脏数据:一个事务读取了另一个事务尚未提交的数据,例如事务A和事务B并发执行,当事务A更新后事务B查询
读取到事务A尚未提交的数据,此时事务A回滚,则事务B读取到的数据是无效的脏数据
不可重复读:一个事务的操作导致另一个事务前后两次读取到不同的数据,例如当事务A与事务B并发执行时,当
事务B查询读取数据后,事务A更新操作更改事务B查询到的数据,此时事务B再次读该数据,发现前后两次的数据
不一样
虚读:一个事务的操作导致另一个事务前后两次查询的结果数据量不同,例如当事务A与事务B并发执行时,当事务
B查询读取数据后,事务A新增或删除了一条满足事务B的查询条件的记录,此时事务B再次查询发现查询不到上一次
存在的记录或查询到上一次不存在的记录
JDBC如何设置事务隔离级别:
事务隔离级别越高,为避免冲突所花的精力也就越多,可以通过Connection对象的conn.setTransactionLevel()方法来
设置事务隔离级别,通过conn.getTransactionIsolation()方法来确定当前事务的隔离级别
3、Class.forName的作用是什么?
在Java语言中,任何类只有被装载到JVM上才能允许,Class.forName()方法的作用就是把类加载到JVM中,它会返回一个
与带有给定字符串类名的类或接口相关联的Class对象,并且JVM会加载这个类,同时JVM会执行该类的静态代码段
JDBC规范中要求Driver类在使用前必须向DriverManager注册自己,所有当执行Class.forName("com.mysql.jdbc.Driver")
时,JVM会加载名字为"com.mysql.jdbc.Driver"对应的Driver类,这个类的部分实现代码如下:
1 public class Driver extends NonRegisteringDriver implements Java.sql.Driver{ 2 static{ 3 try{ 4 java.sql.DriverManager.registerDriver(new Driver); 5 } catch(SQLException e){ 6 throw new RuntimeException("Can't register driver!"); 7 } 8 } 9 }
在调用Class.forName()方法时,这个Driver类就被加载了,由于静态部分被执行,依次Driver也被注册到了DriverManager中
4、Statement、PreparedStatement和CallableStatement有什么区别?
Statement用于执行不带参数的简单SQL语句并返回它所生成结果的对象,每次执行SQL语句时,数据库都要编译该SQL语句,
示例如下:
1 Statement stmt = conn.getStatement(); 2 stmt.executeUpdate("insert into client values('aa', 'aaaa')");
PreparedStatement表示预编译的SQL语句的对象,用于执行带参数的预编译SQL语句,示例如下:
1 PreparedStatement stmt = conn.prepareStatement("select * from Employee where id = ?"); 2 stmt.setInt(1, 1); // 传递参数,第一个问号 3 ResultSet rs = stmt.executeQuery();
CallableStatement则提供了用来调用数据库中存储过程的接口,如果有输出参数要注册,说明是输出参数
CallableStatement由prepareCall()方法创建,它为所有DBMS提供了一种标准形式调用已存储过程的方法,它从
prepareStatement中继承了用于处理输入参数的方法,而且还增加了调用数据库中的存储过程和函数以及设置输出
类型参数的功能
虽然Statement对象与PrepareStatement对象能够完成相同的功能,但相比执行,PrepareStatement具有以下优点:
- 效率更高
- 代码可读性和可维护性更好
- 安全性更好
5、getString()方法和getObject()方法有什么区别?
JDBC提供了getString()、getInt()和getData()方法从ResultSet中获取数据,当查询结果集中的数据量较小时,不用考虑
性能,使用这些方法完全能够满足需求,但是当查询结果集中的数据量非常大时则会抛出异常,而通常情况下使用getObject()
方法就可以解决这个问题
getString()或getInt()等方法在被调用时,程序会一次性地把数据都放到内存中,然后通过调用ResultSet对象的next()和getString()方法
来获取数据,当数据量大到内存中放不下的时候会抛出异常,而使用getObject就不会抛出异常,因为数据不会一次性被读到内存中,
而是每次调用直接从数据库中去获取数据,因此使用getObject方法不会因为数据量过大而出错
6、使用JDBC时需要注意哪些问题?
及时释放连接:
JDBC编程需要先建立数据库连接才能进行对数据库的访问,因而数据库连接成了非常重要的资源。JDBC连接池提供
了JDBC连接定义和有限的连接资源。编程时需要保证数据库接的正常和及时地关闭,及时释放不使用的连接
Statement和PreparedStatement的使用原则:
(1)放在循环外面初始化。因为每次执行conn.createStatement()和conn.preparedStatemetn(),相当于在数据库中打开游标,
放在循还内反复打开游标浪费资源,不能及时关闭则可能导致抛出异常
(2)只要不再使用结果集,就立即关闭对应的Statement
7、什么是JDO
JDO:Java数据对象(Java Data Object)是一个用于存取某种数据仓库中的对象的标准API,它使开发人员能够间接访问数据库
JDO是JDBC的一个补充,它提供了透明的对象存储,因此对于开发人员来说,存储数据对象完全不需要额外的代码,这些繁琐
的工作已经转移到JDO产品提供商身上,让开发人员的精力和时间集中到业务逻辑上。
8、JDBC与Hibernate有什么区别?
Hibernate是JDBC的封装,采用配置文件的形式将数据库的连接参数写到XML文件中,对数据库的访问还是通过JDBC来完成
(1)Hibernate是一个持久层框架,它将表的信息映射到XML文件中,再从XML文件映射到相应的持久化类中,这样就可以使用
Hibernate独特的查询语言(HQL)了,Hibernate的HQL查询语句返回的是List<Object[.]>类,而JDBC通过statement返回的查询
结果是ResultSet,并且有时候需要自己封装到List中
(2)Hibernate具有访问层(DAO层,数据访问接口),该层是HQL查询语句唯一出现的位置,再往上层都不会出现查询语句,
而JDBC可以随时连接随时访问,例如有100个类都有SQL查询语句,如果表名变了,那么要使用JDBC的方式必须重写
所有查询语句,而采用Hibernate的方式只需要修改DAO层的类即可,因此Hibernate具有很好的维护性和扩展性