答题解析

1、 如何注册Driver() 分值5分

○A. Class.forName(“com.mysql.jdbc.Driver”)
○B. System.setProperty(“jdbc.drivers”,”com.mysql.jdbc.Driver”);
○C. new com.mysql.jdbc.Driver();
●D. 以上都可以
回答正确(+5分)
解析:
Class.forName(“com.mysql.jdbc.Driver”)
通过反射注册驱动类
System.setProperty(“jdbc.drivers”,”com.mysql.jdbc.Driver”);
可以同时导入多个jdbc驱动,中间用冒号“:”分开
比如System.setProperty(“jdbc.drivers”,”XXXDriver:XXXDriver:XXXDriver”);
new com.mysql.jdbc.Driver();
编译时会依赖mysql的驱动包,而且静态代码块中会进行驱动的注册,new 出来的 Driver的实例是多余无用的

2、使用下面Connection 的哪个方法可以建立一个PreparedStatement对象() 分值5分

○A. createPrepareStatement()
●B. prepareStatement()
○C. createPreparedStatement()
○D. preparedStatement()
回答正确(+5分)

3、 哪个Statement对象可以sql=”SELECT * FROM EMP WHERE ENAME=?”() 分值5分

○A. Statement
●B. PreparedStatement
○C. CallableStatement
○D. 以上都可以
回答正确(+5分)
解析:
sql中使用?代替变量数据,可以避免sql注入问题
需要先预处理sql
再为sql中的?传递参数
内部拼装参数。拼装时会将参数中的’ 转移\’ 从而避免注入
PreparedStatement对象可以预处理sql,为?传递参数
拓展:
sql注入的情况:
http://c.biancheng.net/view/8283.html
预处理是如何解决注入问题的:
因为SQL语句在程序运行前已经进行了预编译,在程序运行时第一次操作数据库之前,SQL语句已经被数据库分析,编译和优化,对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or ‘1=1’也数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令,如此,就起到了SQL注入的作用了!
参考:
https://www.cnblogs.com/yaochc/p/4957833.html
解释的很清晰

4、 下面有关Statement的说法错误的是() 分值5分

○A. JDBC提供了Statement、PreparedStatement 和 CallableStatement三种方式来执行查询语句,其中 Statement 用于通用查询, PreparedStatement 用于执行参数化查询,而 CallableStatement则是用于存储过程
○B. 对于PreparedStatement来说,数据库可以使用已经编译过及定义好的执行计划,由于 PreparedStatement 对象已预编译过,所以其执行速度要快于 Statement 对象”
●C. PreparedStatement中,“?” 叫做占位符,一个占位符可以有一个或者多个值
○D. PreparedStatement可以阻止常见的SQL注入式攻击
回答正确(+5分)
解析:
Statement、PreparedStatement和CallableStatement都是接口
Statement提供了执行语句和获取结果的基本方法,与需要通过字符串拼接的
方式编译完整的sql,可能会产生sql注入问题
PreparedStatement继承自Statement,可以预编译sql,一次编译,多次执行,
提高效率
CallableStatement继承自PreparedStatement 支持调用存储过程
在预处理sql中,使用一个占位符?代替之前的字符串拼接,可以避免sql注入

拓展:
关于Statement prepareStatement CallableStatement区别:
https://www.cnblogs.com/yaochc/p/4957833.html
解释的很清晰
存储过程的特点:
存储过程(Stored Procedure)是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库。中用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
1.存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而
一般SQL 语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速
度。
2.当对数据库进行复杂操作时(如对多个表进行
Update,Insert,Query,Delete 时),可将此复杂操作用存储过程封装起来
与数据库提供的事务处理结合一起使用。
3.存储过程可以重复使用,可减少数据库开发人员的工作量
4.安全性高,可设定只有某此用户才具有对指定存储过程的使用权

相对于直接使用SQL 语句,在应用程序中直接调用存储过程有以下好处:
(1)减少网络通信量。
调用一个行数不多的存储过程与直接调用SQL 语句的网络通信量可能不会有很大的差别,可是如果存储过程包含上百行SQL 语句,那么其性能绝对比一条一条的调用SQL 语句要高得多。
(2)执行速度更快。
有两个原因:首先,在存储过程创建的时候,数据库已经对其进行了一次解析和优化。其次,存储过程一旦执行,在内存中就会保留一份这个存储过程,这样下次再执行同样的存储过程时,可以从内存中直接调用。
(3)更强的适应性。
由于存储过程对数据库的访问是通过存储过程来进行的,因此数据库开发人员可以在不改动存储过程接口的情况下对数据库进行任何改动,而这些改动不会对应用程序造成影响。
(4) 分布式工作。
应用程序和数据库的编码工作可以分别独立进行,而不会相互压制。

缺点:
1.如果更改范围大到需要对输入存储过程的参数进行更改,或者要更改由其返回的数据,则您仍需要更新程序集中的代码以添加参数、更新 GetValue() 调用,等等,这时候估计比较繁琐了。
2.可移植性差
由于存储过程将应用程序绑定到 SQL Server,因此使用存储过程封装业务逻辑将限制应用程序的可移植性。

5、 如果要手动处理事务,需要先执行Connection的以下哪个方法() 分值5分

●A. conn.setAutoCommit(false) ;
○B. conn.setSavepoint();
○C. conn.commit();
○D. conn.rollback();
回答正确(+5分)
解析:
jdbc默认是自动提交事务
如果要手动处理事务,需要先将自动关闭使用setAutoCommit(false) ;方法
数据库操作完成后,根据操作结果,提交事务或回滚事务。

6、下面描述错误的是() 分值5分

○A. Statement的executeQuery()方法会返回一个结果集
●B. Statement的executeUpdate()方法会返回是否更新成功的boolean值
○C. 使用ResultSet中的getString()可以获得一个对应于数据库中char类型的值
○D. ResultSet中的next()方法会使结果集中的下一行成为当前行
回答正确(+5分)
解析:
statement对象有2个常用方法
executeQuery 执行查询语句,返回装有查询记录的ResultSet结果集
executeUpdate 执行增删改语句,返回操作的记录数
还有一个不太常用的execute方法,可以执行crud,返回boolean
数据库中的char和varchar对应java的类型都是String
ResultSet是一个集合,装有查询结果,但又不同于List,Set这样的集合
List等集合遍历时会返回一条记录,操作记录时与集合无关
ResultSet的所有行记录都来自于ResultSet本身
相当于ResultSet内部有一个table表
有一个指针一开始指向表的第一行数据
所以使用getString(“cno”)方法时获得的指针指向那行的字段值
next()移动指针到下一行,从而可以获得下一行数据,以此类推

7、 以下关于ResultSet说法错误的是() 分值5分

●A. ResultSet是查询结果集对象,如果JDBC执行查询语句没有查询到数据,那么ResultSet将会是null值。
○B. 判断ResultSet是否存在查询结果集,可以调用它的next()方法。
○C. 如果Connection对象关闭,那么ResultSet也无法使用。
○D. 如果一个事物没有提交,那么ResultSet中是看不到事物过程中的临时数据。
回答正确(+5分)
解析:
executeQuery执行查询操作后,一定会获得ResultSet对象
调用ResultSet.next() 将指针下移
如果存在下一条记录返回true,并通过getXXX(key)方法获得值
如果不存在下一条记录返回false
Statement执行sql和ResultSet操作查询结果都需要基于数据库连接
Connection关闭,则相关数据操作就无法执行,ResultSet也无法使用

不同数据库的存储机制不同,未提交的数据可能写在内存,也可能写在指定的
日志中,也可能写在数据文件的指定位置,总之就是与持久化的数据位置
不同

8、已知表中第二个字段NAME,以下哪个方法可以在ResultSet集合集中获取NAME的值() 分值5分

○A. rs.getInt(“name”);
○B. rs.getString(1);
○C. rs.getObject(2);
●D. 以上都不可以
回答错误(+0分)
正确答案:
C. rs.getObject(2);
解析:
数据操作不区分大小写,name和NAME都可以匹配表字段
但name字段值明显应该是字符串,getInt无法获取
获取表字段值时除了可以使用字段名,也可以使用字段下标,从1开始
getObject()方法返回Object类型
拓展:
JDBC提供了getString()、getInt()、getData()等方法从ResultSet中获取数据。

当查询结果集中的数据量较小时,不用考虑性能,使用这些方法完全能够满足需求,但是当查询结果集中的数据量非常大时,则会抛出异常:OrcelException为处理:ORA-01000:maximum open cursors exceedde(以Orcel数据库为例)。而通常情况下,使用getObject()方法就可以解决这个问题。

getString()或getInt()等方法在被调用时,程序会一次性地把数据都放到内存中,然后通过调用ResultSet的next()和getString()等方法来获取数据。

当数据量大到内存中放不下时就会抛出异常,而使用getObject()方法就不会出现这种问题,因为数据不会一次性被读到内存中,每次调用时会直接从数据库中去获取数据,因此使用这种方法不会因为数据量过大而出错。

9、 有一个变量name=”杰克”,以下哪个sql无法达到预期查询效果() 分值5分

○A. “SELECT FROM EMP WHERE ENAME = ?” ;
●B. “SELECT
FROM EMP WHERE ENAME = ”+name+” ;
○C. “SELECT FROM EMP WHERE ENAME = ‘”+name+”’”;
○D. “SELECT
FROM EMP WHERE ENAME = ‘杰克’ “ ;
回答正确(+5分)
解析:
占位符?在预处理拼串时,会自动增加字符串两侧的单引号
如果手动拼装sql,需要手动设置单引号,

10、 以下哪一个url写法是正确的() 分值5分

○A. “jdbc:mysql:///test”
●B. “jdbc:mysql://localhost:3306/test” ;
○C. “jdbc:mysql://localhost:3306/test?characterEncoding=utf-8”
○D. 以上都正确
回答错误(+0分)
正确答案:
D. 以上都正确
解析:
“jdbc:mysql://localhost:3306/test” url比较常见
“jdbc:mysql://localhost:3306/test?characterEncoding=utf8”url在实际开发中比较常见,需要设置交互数据时的中文编码
否则在存储中文信息时,数据库存储的是乱码,这个比较容易发现
有时登录的用户名是中文,结果传递过滤时是乱码,无法查出结果

“jdbc:mysql:///test”在与数据库连接中,默认的主机名是localhost,默认的端口是3306.所以可以
省略不写

11、 简述execute(sql),executeUpdate(sql),executeQuery(sql) 三个方法的区别 分值5分

execute(sql) 可以执行DML和DQL语句。若返回值为true表示执行的是select 操作获得一个ResulitSet,可以通过getResultSet()方法获取ResultSet结果,若返回值是false,表示执行的是DML操作语句;
executeUpdate(sql)可以执行DML和DDL语句,执行insert delete update操作返回值类型为int,表示受影响的行数,执行create alter drop操作,返回值为0;
executeQuery(sql)只能执行select操作,不论是否查询到结果返回值均为ResultSet。
需要人工判分(待判分)
答案解析:
executeUpdate执行增删改语句 和 ddl语句,返回int 表示操作的记录数
比如返回3表示删除了3条记录
executeQuery执行查询语句 返回ResultSet结果集对象,存储查询结果
execute 执行所有sql , 返回boolean
返回true 表示有查询结果,stmt.getResultSet()获取结果集
返回false 表示没有查询结果,stmt.getUpdateCount()获取操作记录数

12、 已知对象数据student 包含3个属性(sno,sname,sex)
编写sql代码和jdbc代码,将对象中的数据存储到同名表的同名字段中。
使用Statement对象执行sql 分值5分

String sql = “INSERT INTO STUDENT(SNO,SNAME,SEX) VALUES(student.getSno()’”+”‘“+student.getSname()+”‘“+”‘“+student.getSex()+”‘)”;
String url = “jdbc:mysql://localhost:3306/student”;
String user = “root”;
String password = “root”;
Connection conn = null;
Statement stat = null;
try{
Class.forName(“com.mysql.jdbc.Driver”);
conn = DriverManager.getConnection(url,user,password);
stat = conn.createStatement();
int count = stat.executeUpdate(sql);
}catch(Exception e) {
e.printStackTrace();
}finally{
try{
if(stat ! = null){
stat.close();

}catch(SQLException e){
e.printStackTrace();

try{
if(conn != null){
conn.close();

}catch(SQLException e){
e.printStackTrace();

需要人工判分(待判分)
答案解析:
String sql = “insert into student(sno,sname,sex) “+
values(”+student.getSno()+”,’”+student.getSname()+”’,’”+student.getSex()+”’)”;
try{
Connection conn = DriverManager.getConnection(
“jdbc:mysql:///test?characterEncoding=utf8”,
”root”,
”root”);
Statement stmt = conn.createStatement();
stmt.executeUpdate(sql);
stmt.close()
conn.close();
}catch(Exception e){
e.printStackTrace()
}

13、已知STUDENT表有3个字段(SNO,SNAME,SEX), java中定义了条件变量sex
编写sql代码和jdbc代码,将表中与sex变量相同的数据全部取出,并输出在控制台。
使用PreparedStatement对象执行sql 分值5分

String sql = “SELECT SNO,SNAME,SEX FROM STUDENT WHERE SEX = ?”;
String url = “jdbc:mysql://localhost:3306/student”;
String user = “root”;
String password = “root”;
Connection conn = null;
PreparedStatement pstat = null;
ResultSet rs = null;
try{
Class.forName(“com.mysql.jdbc.Driver”);
conn = DriverManager.getConnection(url,user,password);
pstat = conn.prepareStatement(sql);
pstat.setString(1,sex);
rs = pstat.executeQuery();
while(rs.next()){
int sno = rs.getInt(1);
String sname = rs.getString(2);
String ssex = rs.getString(3);
System.out.println(sno+””+sname+ssex);

}catch(Exception e){
e.printStackTrace();
}finally{
try{

}catch(SQLException e){
if(rs != null){
rs.close();


try{
if(pstat != null){
pstat.close();

}catch(SQLException e){
e.printStackTrace();

try{
if(conn != null){
conn.close();

}catch(SQLException e){
e.printStackTrace();


需要人工判分(待判分)
答案解析:
String sql = “select sno,sname,sex from student where sex = ?” ;
try{
Connection conn = DriverManager.getConnection(
“jdbc:mysql:///test?characterEncoding=utf8”,
”root”,
”root”);
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1,sex);
ResultSet rs = stmt.executeQuery();
while(rs.next){
System.out.print(rs.getInt(“sno”)+”,”);
System.out.print(rs.getString(“sname”)+”,”);
System.out.print(rs.getString(“sex”));
System.out.println();
}
rs.close();
stmt.close();
conn.close();
}catch(Excepiton e){
e.printStackTrace()
}

14、 已知STUDENT表有3个字段(SNO,SNAME,SEX) 其中SNO PRIMARY KEY AUTO_INCREMENT
已知待执行sql=”INSERT INTO STUDENT(SNAME,SEX) VALUES(‘zzt’,’male’)” ;
编写statement部分代码执行sql,并获得数据库表中自动生成的主键值 分值5分

Statement stat = conn.createStatement();
int count = stat.executeUpdate(sql);
if(count == 1){
ResultSet rs = stat.executeQuery(sql);
if(rs.next()){
int result = rs.getInt(“sex”);


需要人工判分(待判分)
答案解析:
try{
Connection conn = DriverManager.getConnection(
“jdbc:mysql:///test?characterEncoding=utf8”,
”root”,
”root”);
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.executeUpdate();
ResultSet rs = stmt.getGeneratedKeys ();
while(rs.next){
int sno = rs.getInt(1) ;
}
rs.close();
stmt.close();
conn.close();
}catch(Excepiton e){
e.printStackTrace()
}