目录
JDBC相关介绍
数据的持久化
数据库存储技术
数据访问
体系结构
编写步骤
获取数据库连接
要素一:Driver接口实现类
要素二:URL
要素三:用户名和密码
具体实现
操作和访问数据库
图解
数据库的调用方式
ResultSet
ResultSetMetaData
资源的释放
代码实现:通用的增删改操作
代码实现:通用的查询操作
总结
两种思想
两种技术
JDBC相关介绍
概述:JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库的公共接口(一组API)。
数据的持久化
持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用,持久化的实现过程大多通过各种关系数据库来完成 |
文章图片
|
数据库存储技术
在Java中,数据库存取技术可分为如下几类:
1. JDBC直接访问数据库
2. JDO(Java Data Object )技术
3. 第三方O/R工具,如Hibernate, Mybatis等
JDBC是java访问数据库的基石,JDO、Hibernate、MyBatis等只是更好的封装了JDBC。
数据访问
没有JDBC,java程序访问数据库 |
文章图片
|
有了JDBC,java程序访问数据库 |
文章图片
|
总结 |
文章图片
|
体系结构
JDBC接口(API)包括两个层次:
1. 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果
2. 面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用
JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。
不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。(面向接口编程)
编写步骤
文章图片
|
说明:ODBC(Open Database Connectivity,开放式数据库连接) |
获取数据库连接
要素一:Driver接口实现类
Driver接口 |
java.sql.Driver接口是所有JDBC驱动程序需要实现的接口
这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现
|
在程序中不需要直接去访问实现了Driver接口的类
而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现
|
Oracle的驱动:oracle.jdbc.driver.OracleDriver mySql的驱动:com.mysql.jdbc.Driver |
加载驱动 |
加载JDBC驱动需调用Class类的静态方法forName(),向其传递要加载的JDBC驱动的类名 Class.forName(“com.mysql.jdbc.Driver”) |
注册驱动 |
DriverManager类是驱动程序管理器类,负责管理驱动程序 |
使用DriverManager.registerDriver(com.mysql.jdbc.Driver)来注册驱动 |
通常不用显式调用DriverManager类的registerDriver()方法来注册驱动程序类的实例
因为Driver接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用
DriverManager.registerDriver()方法来注册自身的一个实例
|
要素二:URL
说明 |
JDBC URL用于标识一个被注册的驱动程序,驱动程序管理器通过这个URL选择正确的驱动程序,从而建立到数据库的连接 |
JDBC URL的标准由三部分组成,各部分间用冒号分隔 jdbc:子协议:子名称 协议:JDBC URL中的协议总是jdbc 子协议:子协议用于标识一个数据库驱动程序 子名称:一种标识数据库的方法
子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息
包含主机名(对应服务端的ip地址),端口号,数据库名
|
图解 |
文章图片
|
常用数据库的JDBC URL
|
MySQL的连接URL编写 |
1. jdbc:mysql://主机名称:mysql服务端口号/数据库名称?参=值&参数=值
2. jdbc:mysql://localhost:3306/test
3. jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
如果JDBC程序与服务器端的字符集不一致,会导致乱码
可以通过参数指定服务器端的字符集
4. jdbc:mysql://localhost:3306/test?user=root&password=123456
|
Oracle 9i的连接URL编写 |
1. jdbc:oracle:thin:@主机名称:oracle服务端口号:数据库名称
2. jdbc:oracle:thin:@localhost:1521:test
|
SQLServer的连接URL编写 |
1. jdbc:sqlserver://主机名称:sqlserver服务端口号:DatabaseName=数据库名称
2. jdbc:sqlserver://localhost:1433:DatabaseName=atguigu
|
要素三:用户名和密码
- user,password可以用"属性名=属性值"方式告诉数据库
- 可以调用DriverManager类的getConnection()方法建立到数据库的连接
具体实现
使用配置文件的方式保存配置信息,在代码中加载配置文件 |
使用配置文件的好处 |
实现了代码和数据的分离,如果需要修改配置信息,直接在配置文件中修改,不需要深入代码 |
如果修改了配置信息,省去重新编译的过程 |
配置文件设计 |
文章图片
|
因为操作数据库时会多次获取连接,所以就把获取连接和关闭资源的功能代码放在工具类中。
package util;
//导包
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
//操作数据库的工具类
public class JDBCUtils {//获取数据库的连接
public static Connection getConnection() throws Exception {
//1.读取配置文件中的4个基本信息
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
//文件内容读到集合
Properties prop = new Properties();
prop.load(is);
//获取对应的值
String user = prop.getProperty("user");
String password = prop.getProperty("password");
String url = prop.getProperty("url");
String driverClass = prop.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//3.获取连接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}//关闭资源
public static void closeResource(Connection conn, Statement ps){
try {
if (ps != null)
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try {
if (conn != null)
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}//关闭资源
public static void closeResource(Connection conn, Statement ps,ResultSet rs){
try {
if (ps != null)
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try {
if (conn != null)
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}try {
if (rs != null)
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
操作和访问数据库
概述:数据库连接被用于向数据库服务器发送命令和SQL语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接
图解
文章图片
数据库的调用方式
Statement |
概述:用于执行静态SQL语句并返回它所生成结果的对象 |
接口中定义了的方法 |
int excuteUpdate(String sql):执行更新操作INSERT、UPDATE、DELETE
ResultSet executeQuery(String sql):执行查询操作SELECT
|
弊端 |
存在拼串操作,繁琐
说明:"SELECT user,password FROM user_table
WHERE user = '"+ user +"' AND password = '"+ password +"'"
|
存在SQL注入问题
说明:SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查
而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password
FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1')
从而利用系统的SQL引擎完成恶意行为的做法
|
PrepatedStatement |
概述:SQL语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句 |
介绍:PreparedStatement接口是Statement的子接口,它表示一条预编译过的SQL语句 |
创建:可以通过调用Connection对象的preparedStatement(String sql)方法获取PreparedStatement对象 |
使用 |
PreparedStatement对象所代表的SQL语句中的参数用问号(?) 来表示
调用PreparedStatement对象的setObject()方法来设置这些参数
|
setObject()方法有两个参数
第一个参数是要设置的SQL语句中的参数的索引(从1开始)
第二个是设置的SQL语句中的参数的值
|
PreparedStatementVSStatement |
1. PreparedStatement可以操作Blob的数据,Statement做不到
2. PreparedStatement能实现更高效的批量操作,最大可能提高性能
3. PreparedStatement可以解决Statement拼串和防止SQL注入
|
CallableStatement |
概述:用于执行SQL存储过程 |
图解 |
文章图片
|
ResultSet
说明:查询需要调用PreparedStatement的executeQuery()方法,查询结果是一个ResultSet对象 |
介绍 |
ResultSet对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet接口由数据库厂商提供实现 |
ResultSet返回的实际上就是一张数据表,有一个指针指向数据表的第一条记录的前面 |
使用 |
ResultSet对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前
可以通过ResultSet对象的next()方法移动到下一行。调用next()方法检测下一行是否有效
若有效,该方法返回true,且指针下移。相当于Iterator对象的hasNext()和next()方法的结合体
|
当指针指向一行时, 可以通过调用getXxx(int index)或getXxx(int columnName)获取每一列的值 例如: getInt(1), getString("name") 注意:Java与数据库交互涉及到的相关Java API中的索引都从1开始 |
常用方法 |
- boolean next() - getString() |
图解 |
文章图片
|
Java与SQL
对应数据类型转换表
|
文章图片
|
ResultSetMetaData
说明 |
可用于获取关于ResultSet对象中列的类型和属性信息的对象 ResultSetMetaData meta = rs.getMetaData();
|
文章图片
|
常用方法 |
getColumnName(int column):获取指定列的名称 getColumnLabel(int column):获取指定列的别名 getColumnCount():返回当前ResultSet对象中的列数 |
getColumnTypeName(int column):检索指定列的数据库特定的类型名称 getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位 isNullable(int column):指示指定列中的值是否可以为null isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的 |
注意 |
表的字段名与类的属性名不相同的情况:
1. 必须声明sql时,使用类的属性名来命名字段的别名 2. 使用ResultSetMetaData时,需要使用getColumnLabel()来替换getColumnName()获取列的别名
说明:如果sql中没有给字段起别名,getColumnLabel()获取的就是列名
|
图解 |
文章图片
|
资源的释放
释放ResultSet,、Statement、Connection |
数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机
Connection的使用原则是尽量晚创建,尽量早的释放
|
可以在finally中关闭,保证及时其他代码出现异常,资源也一定能被关闭 |
代码实现:通用的增删改操作
说明:获取数据库连接和资源关闭使用的是之前创建的工具类中的方法。
//通用的增删改操作
//sql中占位符的个数与可变形参的长度相同!
public void update(String sql,Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句,返回PreparedStatement实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for (int i = 0;
i < args.length;
i++) {
ps.setObject(i+1,args[i]);
}
//4.执行
ps.execute();
}catch (Exception e){
e.printStackTrace();
}finally {
//5.资源关闭
JDBCUtils.closeResource(conn, ps);
}
}
代码实现:通用的查询操作
说明:查询操作有返回值,我们一般使用创建对象,再给对象赋值的方式输出结果。
//结果集的输出方式
//方式一:
//System.out.println("id="+id+" name="+name+" email="+email+" Date:"+birth);
//方式二:
//Object[] date = new Object[]{id,name,email,birth};
//方式三:将数据封装为一个对象(推荐)
查询一条记录:
//针对于不同表的通用查询操作,返回表中一条记录
public T getInstance(Class clazz,String sql,Object ...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//获取连接
conn = JDBCUtils.getConnection();
//预编译sql语句,返回PreparedStatement实例
ps = conn.prepareStatement(sql);
//填充占位符
for (int i = 0;
i < args.length;
i++) {
ps.setObject(i+1,args[i]);
}//执行:捕获结果集
rs = ps.executeQuery();
//获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获取列数
int columnCount = rsmd.getColumnCount();
if (rs.next()){
//创建此Class对象所表示类的实例
T t = clazz.newInstance();
//处理结果集一行数据中的每一个列
for (int i = 0;
i < columnCount;
i++) {
//获取每个列的列值:通过ResultSet
Object columnValue = https://www.it610.com/article/rs.getObject(i + 1);
//获取每个列的列名:通过ResultSetMetaData
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射,将对象指定名columnName的属性赋值为指定的值columnValue
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
return t;
}
}catch (Exception e){
e.printStackTrace();
}finally {
//释放资源
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
查询多条记录:
//针对于不同表的通用查询操作,查询多条记录
public List getForList(Class clazz,String sql,Object ...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//获取连接
conn = JDBCUtils.getConnection();
//预编译sql语句,返回PreparedStatement实例
ps = conn.prepareStatement(sql);
//填充占位符
for (int i = 0;
i < args.length;
i++) {
ps.setObject(i+1,args[i]);
}//执行:捕获结果集
rs = ps.executeQuery();
//获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获取列数
int columnCount = rsmd.getColumnCount();
//创建集合对象
ArrayList list = new ArrayList<>();
while (rs.next()){
//创建此Class对象所表示类的实例
T t = clazz.newInstance();
//处理结果集一行数据中的每一个列:给t对象指定的属性赋值
for (int i = 0;
i < columnCount;
i++) {
//获取每个列的列值:通过ResultSet
Object columnValue = https://www.it610.com/article/rs.getObject(i + 1);
//获取每个列的列名:通过ResultSetMetaData
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射,将对象指定名columnName的属性赋值为指定的值columnValue
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
//添加对象到集合
list.add(t);
}
return list;
}catch (Exception e){
e.printStackTrace();
}finally {
//释放资源
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
总结
两种思想
- 面向接口编程的思想
- ORM思想(object relational mapping)
一个数据表对应一个java类
表中的一条记录对应java类的一个对象
表中的一个字段对应java类的一个属性
两种技术
- JDBC结果集的元数据:ResultSetMetaData
获取列数:getColumnCount()
获取列的别名:getColumnLabel()
- 通过反射,创建指定类的对象,获取指定的属性并赋值
【Java散装知识点总结|JDBC相关介绍、操作访问数据库】
推荐阅读