JDBC面试知识点整理(温习用)
要面试,所以把之前的笔记整理一遍,嘻嘻,加油
JDBC编程
使用JDBC,java程序可以轻松地操作各种主流数据库,Oracle,MySQL,等,使用JDBC编写的程序不仅可以实现跨数据库,还具有跨平台性和可移植性。
JDBC(Java Database Connectiovity,Java数据库连接)是一种执行SQL语句的JavaAPI,程序可以通过JDBC API连接数据库,并使用SQL结构化查询语言完成对数据库的操作,程序员使用JDBC编程时只需要掌握标准的JDBC API 即可,当需要在不同的数据库之间切换时,只需要更换不同的数据库驱动类,是面向接口编程的典例应用。
JDBC访问数据库时主要完成三个操作:
- 建立与数据库的连接
- 执行SQL语句
- 获取执行结果
JDBC驱动: 数据库驱动程序是JDBC程序和数据库之间的转换层,数据库驱动程序负责将JDBC调用映射成特定的数据库调用,有4种类型:JDBC-ODBC桥(最早),本地API驱动,网络协议驱动,本地协议驱动(建议使用,纯java编写)。
JDBC API:提供一组用于与数据库进行通信的接口和类,都定义在java.sql包下。
Java.sql包常用接口和类
- DriverManager
- 用于管理JDBC驱动的服务类,该类的主要功能是加载和卸载各种驱动程序,建立数据库连接并获取连接对象
- Connection
- 该接口代表数据库的连接,要访问数据库必须先获得数据库的连接
- Statement
- 用于执行SQL语句的工具接口,当执行查询语句时返回一个查询到的结果集
- PreparedStatement
- 该接口用于执行预编译的SQL语句,这些SQL语句带有参数,避免数据库每次都需要编译SQL语句,执行时传参
- CallableStatement
- 该接口用于调用SQl储存过程
- ResultSet
- 该接口表示结果集,包含访问查询的各种方法
使用JDBC API中的类或者接口访问数据库时,容易引发SQLException异常,属于检查性异常。需要放在try……catch语句里,对于DriverManager来讲,要用到ClassNotFoundException异常。
DriverManager类:是数据库驱动管理类,用于管理一组JDBC驱动程序的基本服务,应用程序和数据库之间可以通过DriverManager建立连接,
DriverManager常用静态方法
- static connection getConnection(String url,String user,String password)
- 获取指定的URL的数据库连接,其中url为提供了一种标识数据库位置的方法,user用户名,password密码
- static Driver getDriver(String url)
- 返回能够打开url所指定的数据库的驱动程序
Connection接口:用于连接数据,每个Connection代表一个数据库连接会话,一个应用程序可以与单个或者多个数据库连接,通过DriverManager类的getConnection()方法可以返回同一个Connection对象,该对象提供创建SQL的语法,完成基本SQL操作
Connection的常用方法
- void close()
- 断开连接,释放此Connection对象的数据库和JDBC资源
- Statement createStatement()
- 创建一个Statement对象来将SQL语句发送到数据库
- void commit()
- 用于提交SQL语句,确认从上一次提交/回滚以来进行的所有更改
- boolean isClosed()
- 用于判断Connection对象是否已经被关闭
- CallableStatement prepareCall(String sql)
- 创建一个CallableStatement对象来调用数据库储存过程
- PrepareStatement(String sql)
- 创建一个PreparedStatement对象来将参数化的SQL语句发送到数据库
- void rollback()
- 用于取消SQL语句,取消在当前事务中进行的所有更改
Statement接口:一般用于执行SQL语句,在JDBC中要执行SQL查询语句的方式有
- 一般查询(Statement),
- 参数查询(PrepareStatement)
- 储存查询(CallableStatement )
三种方式。Statement和PreparedStatement和CallableStatement三个接口具有依次继承关系。
- Statement接口的主要功能是将SQL语句传送给数据库,并返回SQL语句的执行结果,
- Statement提交的SQL语句是静态的,不需要接受任何参数,SQL语句可以包含三种类型语句:SELECT查询语句,DML语句,DDL语句。
Statement接口常用方法
- void close()
- 关闭Statement对象
- boolean execute(String sql)
- 执行给定的SQL语句,可以返回多个结果
- ResultSet executeQuery(String sql)
- 执行给定SQL语句,返回单个ResultSet对象
- int executeUpdate(String sql)
- 执行给定的SQl语句,可以为DML语句或者DDL语句,返回影响行数
- Connection getConnection()
- 获取生成此Statement对象的Connection对象
- int getFetchSize()
- 获取结果集合的行数,该数是根据此Statement对象生成的ResultSet对象的默认获取大小
- int getMaxRows()
- 获取由此Statement对象生成的ResultSet对象可以包含的最大行数
- ResultSet getResultSet()
- 获取由此Statement执行查询语句返回的的ResultSet对象
- int getUpdateCount()
- 获取此Statement执行DML语句所影响的记录个数
- void cioseOnComplection()
- 当所有依赖Statement对象的ResultSet结果集关闭时,该Statement会自动关闭
- boolean isCloseOnCompletion()
- 判断是否打开closeOnCompletion()
- long executeLargeUpdate(String sql)
- 增强版读完executeUpdate()方法,记录数超过Integer时使用,为long
ResultSet接口:用于封装结果集对象,该对象包含访问查询结果的方法,使用Statement中的executeQuery()方法可以返回一个ResultSet结果集对象(f封装了所有查询条件的记录),ResultSet具有指向当前数据行的游标,并提供了许多方法来操作结果集中的游标,提供getXXX()方法对结果集进行访问。调用next()方法游标会向下移动,
ResultSet接口常用方法
- boolean absolute(int row)
- 将游标移动到row条记录
- boolean relative(int rows)
- 按相对行数(正或负)移动游标
- void beforeFirst()
- 将游标移动到结果集的开头(第一行前)
- boolean first()
- 将游标移动到结果集的第一行
- boolean previous()
- 将游标移动到结果集的上一行
- boolean next()
- 将游标移动到结果集中当前位置的下一行
- boolean last()
- 将游标移动到结果集的最后一行
- void afterlast()
- 将游标移动到结果集的末尾(最后一行后)
- boolean isAfterLast()
- 判断游标是否位于结果集的末尾(最后一行后)
- boolean isBeforeFirst()
- 判断游标是否位于结果集的开头(第一行前)
- boolean isFirst()
- 判断游标是否位于结果集的第一行
- boolean isLast()
- 判断游标是否位于结果集的最后一行
- int getRow()
- 检索当前行编号
- String getString(int x)
- 返回当前行第X列的列值,类型为String
- int getInt(int x)
- 返回当前行第X列的列值,类型为int
- Statement getStatement()
- 获取生成结果集的Statement对象
- void close()
- 释放此ResultSet对象的数据库和JDBC资源
- ResultSetMetaData getMetaData()
- 获取结果集的列的编号,类型,和属性
数据库环境搭建:
- 1,创建数据库表。
- 2设置Oracle驱动类路径(Oracledatabase所提供的JDBC驱动程序(jre文件)导入到工程中)。
数据库访问:使用JDBC访问数据库步骤:
- 加载数据库驱动:
- Class.forName("数据库驱动类名");
- mysql:com.mysql.jdbc.Driver;
- oracle:oracle.jdbc.driver.OracleDriver;
- Class.forName("数据库驱动类名");
- 建立数据连接:
Connection conn = DriverManager.getConnection(String url,String user,String pass);
mysql:DriverManager.getConnection("jdbc:mysql://localhost:3306/shop", "root","mysql");
oracle:DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl","sys as sysdba","oracle"); - 创建Statement对象:通过Connection对象获取Statement的方法;
- createStatement()//创建一个基本的Statement对象。
- prepareStatement(String sql)//根据参数化的SQL语句创建一个预编译的PreparedStatement对象。
- PrepareCall(String sql)//根据SQL语句创建一个CallableStatement对象,用于调用储存过程。
- 执行SQl语句:
- 获取Statement对象后,可以调用该对象的不同方法来执行SQL语句,有三种执行SQl语句的方法,
- executeQuery():只能执行查询语句,用于产生单个结果集。
- executeUpdate()和executeLargeUpdate():用于执行DML和DDL语句,返回对应的行数和0;
- execute():可以执行任何SQL语句返回值为布尔型。Statement对象将结果集分装为ResultSet对象并返回
- 获取Statement对象后,可以调用该对象的不同方法来执行SQL语句,有三种执行SQl语句的方法,
- 访问结果集:
- SQL的查询结果使用ResultSet封装,ResultSet结果集中包含了满足SQL查询条件的所有的行,使用getXXX()方法对结果集进行数据访问,通过列索引和列名获取游标所指的列数据,通过next()和循环控制游标。
- 关闭对象:
- 关闭结果集,Statement对象,连接对象。
- 操作数据库:
Statement接口:
execute()方法可以执行任何SQL语句,返回值为布尔型表示是否返回了ResultSet对象,true时使用Statement的getResultSet()方法来获取execute()方法执行SQL语句返回的ResultSet对象;false时使用getUpdateCount()方法获取受影响得行数。
executeUpdate()和executeLargeUpdate():用于执行DML和DDL语句,返回对应的行数和0;当DML语句影响的记录超过Integer.MAX_VALUE时,使用executeLargeUpdate(),类型为long;
PreparedStatement接口:
PreparedStatement对象包含的SQL语句进行预编译,需要多次执行相同的SQL语句时,编译好的语句比Statement对象快。可以执行动态的SQL语句,即在SQL 语句中提供参数,动态SQL语句中用?作为动态参数的占位符。用setXXX()的方法通过占位符的索引完成对参数的赋值。
例:String insterSql = "INSERT INTO LIRUILONG.my_tests VALUES(?,?)";
PreparedStatement ps = conn.prepareStatement(insterSql);
ps.setInt(1,4);
ps.setString(2, "李瑞龙");
CallableStatement接口:
JDBC提供CallableStatement接口,用于执行数据库的储存过程,该接口可以处理一般的SQL 语句,也可以处理以三种带参数(IN ,OUT,IN OUT)的SQL储存过程,使用Connection类的prepareCall(String sql)方法可以创建一个CallableStatement对象,方法的参数可以是一个调用存储过程的字符串,cast = conn.prepareCall("{call LIRUILONG.addSub(?,?)");对CallableStatement的SQL语句执行一般用execute().
调用存储过程的SQL:“{call 存储过程名[(参数占位符?)]}”、、有返回值的:“{参数占位符?=call 存储过程名[(参数占位符?)]}”
CallableStatement接口通过setXXX()方法对IN参数进行赋值,
通过registerOutParameter(1, sql.Types.)方法对OUT参数进行类型注册,第二个参数通常使用java.sql.Types静态常量指定。
检索结果的获取通过getXXX()方法获取OUT和IN OUT参数的值,
对于IN OUT 参数的值来讲需要先使用setXXX()方法对参数进行设置,然后使用registerOutParameter()进行类型注册,最后使用getXXX()方法来获取检索结果
数据库访问优化:
即编写一个数据库访问工具类DBUtil,用于提供访问数据库时所用到的连接,查询等
编写属性文件:存放连接数据库的参数信息,在项目的更目录下创建一个config子目录。并添加一个属性文件oracle.properties,该文件以键-值对的形式保存连接Oracle的配置信息。在读取配置文件的配置信息时,需要编写一个Config配置类,在给类中通过java.util.Properrties类的get()方法来获取指定的“键”所对应的值。
编写DBUtil工具类。将SQL语句与参数包装传递
使用DBUtil工具类。
集元数据
集元数据(Meta Data)是有关数据库和表结构的信息,JDBC提供了获取这些信息的DatabaseMetaData和ResultSetMetaData接口。
DatabaseMetaData接口:DatabaseMetaData接口主要用于获取数据库相关信息,如数据库的所有表的列表,系统函数,关键字,数据库产品名以及驱动类型。DatabaseMetaData对象通过getMetaData()方法进行获取。DatabaseMetaData接口提供大量获取信息的方法,这些方法可分为两大类:
- 返回值为boolean型,多用于检查数据库或驱动器是否支持某项功能,
- 获取数据库或驱动的参数特征值。
DatabaseMetaData
- boolean supportsOuterJolns()
- 检查数据库是否支持外部连接
- boolean supportsStoredProcedures()
- 检查数据库是否支持储存过程
- String getURL()
- 返回用于连接数据库的URL地址
- String getUserName()
- 获取当前的用户名
- String getDatabaseProductName()
- 获取使用的数据库产品名
- String getDatabaseProductVersion()
- 获取使用的数据库版本号
- String getDriverName()
- 获取用于连接的驱动类型名称
- String getProductVerslon()
- 获取用于连接的驱动器版本号
- ResultSet getTypeInfo()
- 获取数据库中可能取得的所有数据类型的描述
ResultSetMetaData接口:用于获取结果集的结构信息,通过ResultSet的getMetaData()方法来获取对应的ResultSetMetaData对象
ResultSetMetaData的常用方法
- int getColumnCount()
- 返回此ResultSst对象中的列数
- String getColumnName(int column)
- 获取指定列的名称
- int getColumnType(int cloumn)
- 检索指定列的SQL类型
- String getTableName(int column)
- 获取指定列的表名
- int getColumnDisplaySize(int column)
- 指示指定列的最大标准宽度,以字符为单位
- boolean isAutoIncrement(int column)
- 指示是否为自动为指定列中进行编号,这些列任然为只读
- int isNullable(int column)
- 指示指定列中的值是否可以为null
- boolean isSearchable(int column)
- 指示是否可以在where子句中使用指定的列
- boolean isReadOnly(int column)
- 指示指定法人列是否明确不可写入
事务处理:
事务是保证底层数据完整的重要手段由一步或几步数据库操作序列注组成的逻辑执行单元。事务具有ACID四个特性:
- 原子性(Atomicity):事务是应用中的最小执行单位,具有不可在分的特性,全部完成或不执行。
- 一致性(Consistency):事务执行前后数据库都必须是出于一致状态。
- 隔离性(Isolation):各个事务互不干扰,及并发事务间互不影响。
- 持久性(Durability):事务一旦提交,对数据库的的物理存储进行改变,不被丢失。
事务处理包括事务提交,终止和回滚,事务提交分显式提交(commit)和自动提交,事务终止指未能成功完成事务,执行中断,事务回滚有显式回滚(rollback)和自动回滚(系统错误或强行退出)
JDBC对事务操作由Connection提供:
开启事务,执行任意多条DML语句,执行成功提交事务,失败回滚事务,Connection在默认情况下会自动提交,及事务是关闭的,一条SQL语句执行后,系统调用commit()方法提交数据库,无法回滚,使用Connection的setAutoCommit()方法可以开启关闭自动提交模式(事务),该方法为布尔型,参数false为关闭自动提交,反之打开自动提交。
- 开启事务:conn.setAutoCommit(false);
- 提交事务:conn.commit();
- 回滚事务:conn.rollback();
当程序遇到未处理的SQLException异常,都会自动回滚,当捕获该异常时,则需要在处理块中显示回滚。
保存点操作:
- 使用Savepoint类声明一个保存点对象(Savepoint s1),在需要的位置设置保存点(s1= conn.setSavepoint();)在回滚操作中可以回滚到保存点位置(conn.rollback(s1));
批量更新:
- 即多条SQL语句被作为一批操作同时收集,并同时提交,必须得到底层数据库的支持,调用DatabaseLargeBatch()方法查看底层数据库是否支持批量更新。创建一个Statement对象,使用Statement对象的addBatch()方法收集多条SQL语句,调用Statement的executeBatch()或者executeLargeBatch()方法同时提交所有的SQL语句。
---------------------
JDBC工具类
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/project jdbc.user=root jdbc.password=123456 jdbc.datasource.size=10
package com.lirong.sparkproject.constant; public interface Constants { /** * 数据库相关常量 */ String JDBC_DRIVER = "jdbc.driver"; String JDBC_URL = "jdbc.url"; String JDBC_USER = "jdbc.user"; String JDBC_PASSWORD = "jdbc.password"; String JDBC_DATASOURCE_SIZE="jdbc.datasource.size"; }
import java.io.InputStream; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Properties; import com.lirong.sparkproject.constant.Constants; public abstract class JDBCHelper { //配置对象 private static Properties prop=new Properties(); /* * jdbc变量 */ static String driver=null; static String url=null; static String user=null; static String password=null; //thread封装Connection对象 static ThreadLocal<Connection> thread=new ThreadLocal<Connection>(); //唯一空构造器私有化,不允许外部创建对象 private JDBCHelper() {} /** * 静态代码块为jdbc变量赋值 * 因为静态代码块最先执行,所以调用getConnection()方法时, * 该方法内部的jdbc变量就完成了赋值操作 */ static { try { //动态获取配置文件的路径 InputStream in=JDBCHelper.class.getClassLoader().getResourceAsStream("my.properties"); prop.load(in);//加载键值对信息 /* * Constants常量接口中保存了很多常量,这些常量的值就是配置文件k-v数据的键 * */ driver=prop.getProperty(Constants.JDBC_DRIVER); url=prop.getProperty(Constants.JDBC_URL); user=prop.getProperty(Constants.JDBC_USER); password=prop.getProperty(Constants.JDBC_PASSWORD); /* * 加载驱动,静态代码块只执行一次,驱动只加载一次(加载驱动很耗性能的) */ Class.forName(driver);//加载驱动 } catch (Exception e) { e.printStackTrace(); } } /** * 通过本方法客获取一个MySQL数据库的Connection对象 * * @return Connection对象 */ public static Connection getConnection() { Connection con = thread.get(); if(con==null) { try { con = DriverManager.getConnection(url, user, password); thread.set(con); } catch (SQLException e) { e.printStackTrace(); } } return con;//返回jdbc连接 } /** * 本方法中调用Date类型变量的setter方法时使用的是java.sql.Date, * 所以实体类在声明Date类型变量时一定声明成java.sql.Date * 至少Date类型变量对应的setter方法的形参必须是java.sql.Date,否则报错 * * 查询完毕后,使用者通过JDBCHelper.getConnection()获取连接对象,并关闭它 * 外部获取的连接对象与本方法使用的连接对象,在同一线程类,是同一个对象 * * @param sql 要执行的查询语句 * @param t 实体类对象 * @param objs SQL语句中的参数 * @return 装有实体类对象的list集合 */ public static <T> List<T> executeQuery(String sql,T t,Object...objs){ //声明jdbc变量 List<T> list=new ArrayList<>(); Connection conn = null; PreparedStatement ps =null; ResultSet rs =null; try { conn = JDBCHelper.getConnection(); ps = conn.prepareStatement(sql); //给占位符赋值 if(objs!=null) { for(int i=0;i<objs.length;i++) { ps.setObject((i+1), objs[i]); } } //执行sql语句 rs = ps.executeQuery(); //获取结果集中字段的所有信息 ResultSetMetaData rm = rs.getMetaData(); int columnCount = rm.getColumnCount();//获取字段数 //遍历结果集 while(rs.next()) { Class<? extends Object> cla = t.getClass();//获取类对象 T newInstance=(T)cla.newInstance();//获取类的对象 //一个for循环封装一条记录的所有值 for(int i=1;i<=columnCount;i++) { String columnName = rm.getColumnName(i);//获取字段名 //获取字段对应的setter方法 String methodName="set"+columnName.substring(0, 1).toUpperCase()+columnName.substring(1); String columnClassName = rm.getColumnClassName(i);//获取字段java类型的完全限定名 //创建方法对象 Method method = cla.getDeclaredMethod(methodName, Class.forName(columnClassName)); method.invoke(newInstance,rs.getObject(columnName));//调用setter方法,执行对象属性赋值 } list.add(newInstance);//将对象加入集合 } } catch (Exception e) { e.printStackTrace(); }finally { //关流 JDBCHelper.close(ps,rs); } return list; } /** * 该方法封装了MySQL数据库的DML操作 * 如果要实现事务: * 事务的开启,关闭,回滚,及连接对象的关闭等操作 * 使用者通过JDBCHelper.getConnection()获取连接对象,通过连接对象并在外部声明 * 外部获取的连接对象与本方法使用的连接对象,在同一线程类,是同一个对象 * * @param sql 要执行的SQL语句 * @param objs SQL语句中的参数 * @return 成功执行返回影响的记录条数,否则返回0 * @throws SQLException */ public static Integer executeDML(String sql,Object...objs) { //声明jdbc变量 Connection conn =null; PreparedStatement ps =null; Integer i =0; try{ conn = JDBCHelper.getConnection(); ps = conn.prepareStatement(sql); //给占位符赋值 if(objs!=null) { for(int j=0;j<objs.length;j++) { ps.setObject((j+1), objs[j]); } } //执行SQL语句 i = ps.executeUpdate(); }catch(SQLException e) { e.printStackTrace(); }finally { //关流 JDBCHelper.close(ps); } return i; } /** * 关流的方法,接收任意多个任意类型的流对象 * 如果关闭的流对象有关闭的先后顺序 * 请将要先关闭的流对象放在前方 * * 所有流对象的顶级父接口都是AutoCloseable * @param t 要关闭的流对象,可以是一个或多个(也可以是零个) * */ private static <T>void close(T...t){ //循环关流 for(T tmp:t) { //关闭流对象 if(tmp instanceof AutoCloseable) { try { ((AutoCloseable)tmp).close(); } catch (Exception e) { e.printStackTrace(); } } } } }