JDBC_增删改查
Posted D大调奏鸣曲_第七序章_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC_增删改查相关的知识,希望对你有一定的参考价值。
目录
2.4. 使用PreparedStatement实现CRUD操作
三、JDBC程序编写(使用PreparedStatement实现查询操作)
2、ResultSet与ResultSetMetaData(结果集,元数据)
一、JDBC概念
JDBC(Java Database Connectivity)是一个**独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口**(一组API),定义了用来访问数据库的标准Java类库,(**java.sql,javax.sql**)使用这些类库可以以一种**标准**的方法、方便地访问数据库资源。
JDBC的目标是使Java程序员使用JDBC可以连接任何**提供了JDBC驱动程序**的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
如果没有JDBC,那么Java程序访问数据库时是这样的:
有了JDBC,Java程序访问数据库时是这样的:
所以,当我们使用JDBC的时候,我们会将形成一定的标准,规范化,利于形成集群化、良性化的生态:
引出了一种思想: ————面向接口编程
不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。
二、JDBC程序编写(实现对数据表的增删改)
1、JDBC程序编写步骤
补充:
ODBC(**Open Database Connectivity**,开放式数据库连接),是微软在Windows平台下推出的。使用者在程序中只需要调用ODBC API,由 ODBC 驱动程序将调用转换成为对特定的数据库的调用请求。
2、数据表增删改
2.1. 基本步骤
①获取数据库连接
②获取PreparedStatement的实例 (或:预编译sql语句)
③填充占位符
④执行sql语句
⑤关闭资源
2.2. 通用的增删改方法(调用即可)
//通用的增、删、改操作(体现一:增、删、改 ; 体现二:针对于不同的表)
public void update(String sql,Object ... args)
Connection conn = null;
PreparedStatement ps = null;
try
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.获取PreparedStatement的实例 (或:预编译sql语句)
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i = 0;i < args.length;i++)
ps.setObject(i + 1, args[i]);
//4.执行sql语句
ps.execute();
catch (Exception e)
e.printStackTrace();
finally
//5.关闭资源
JDBCUtils.closeResource(conn, ps);
//调用上述方法,实现对数据表的增删改
@Test public void testCommonUpdate()
String sql =”增删改语句(要有占位符)”;
调用增删改的具体方法update(sql,索引);
2.3. 获取数据库连接(分解注释)
四项基本信息要素
加载驱动的具体信息(要素一)
url(要素二)(基于什么样的协议,基于那个端口号下的数据库)
用户名user(要素三)
密码password(要素三)
要素一:Driver接口实现类
Driver接口介绍
java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现。
在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现。
- Oracle的驱动:**oracle.jdbc.driver.OracleDriver**
- mysql的驱动: **com.mysql.jdbc.Driver**
加载与注册JDBC驱动
加载驱动:加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名
**Class.forName(“com.mysql.jdbc.Driver”);**
注册驱动:DriverManager 类是驱动程序管理器类,负责管理驱动程序
- **使用DriverManager.registerDriver(com.mysql.jdbc.Driver)来注册驱动**
- 通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱动程序类的实例,因为 Driver 接口的驱动程序类**都**包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver() 方法来注册自身的一个实例。下图是MySQL的Driver实现类的源码:
要素二:URL
- DBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。
- JDBC URL的标准由三部分组成,各部分间用冒号分隔。
- **jdbc:子协议:子名称**
- **协议**:JDBC URL中的协议总是jdbc
- **子协议**:子协议用于标识一个数据库驱动程序
- **子名称**:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了**定位数据库**提供足够的信息。包含**主机名**(对应服务端的ip地址)**,端口号,数据库名**
举例:
**几种常用数据库的 JDBC URL**
MySQL的连接URL编写方式:
- dbc:mysql://主机名称:mysql服务端口号/数据库名称?参数=值&参数=值
- jdbc:mysql://localhost:3306/test
- jdbc:mysql://localhost:3306/test**?useUnicode=true&characterEncoding=utf8**(如果JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集)
- jdbc:mysql://localhost:3306/test?user=root&password=123456
Oracle 9i的连接URL编写方式:
- jdbc:oracle:thin:@主机名称:oracle服务端口号:数据库名称
- jdbc:oracle:thin:@localhost:1521:test
SQLServer的连接URL编写方式:
- jdbc:sqlserver://主机名称:sqlserver服务端口号:DatabaseName=数据库名称
- jdbc:sqlserver://localhost:1433:DatabaseName=atguigu
要素三、四:用户名和密码
- user,password可以用“属性名=属性值”方式告诉数据库
- 可以调用 DriverManager 类的 getConnection() 方法建立到数据库的连接
数据库连接方式举例
//最终版
@Test
public void testConnection5() throws Exception
//1.加载配置文件
InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
//2.读取配置信息
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
//3.加载驱动
Class.forName(driverClass);
//4.获取连接
Connection conn = DriverManager.getConnection(url,user,password);
System.out.println(conn);
其中,配置文件声明在工程的src目录下:【jdbc.properties】
user=root
password=abc123
url=jdbc:mysql://localhost:3306/test
driverClass=com.mysql.jdbc.Driver说明:使用配置文件的方式保存配置信息,在代码中加载配置文件
2.4. 使用PreparedStatement实现CRUD操作
在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:
- Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。
- PrepatedStatement:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。
- CallableStatement:用于执行 SQL 存储过程
通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果。
Statement 接口中定义了下列方法用于执行 SQL 语句:
int excuteUpdate(String sql):执行更新操作INSERT、UPDATE、DELETE
ResultSet executeQuery(String sql):执行查询操作SELECT
我们主要使用PreparedStatement,因为Statement存在弊端:
- **问题一:存在拼串操作,繁琐**
- **问题二:存在SQL注入问题**
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1') ,从而利用系统的 SQL 引擎完成恶意行为的做法。
对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从Statement扩展而来) 取代 Statement 就可以了。
三、JDBC程序编写(使用PreparedStatement实现查询操作)
1、通用的查询方法(调用即可)
代码如下:
// 通用的针对于不同表的查询:返回一个对象 (version 1.0)
public <T> T getInstance(Class<T> clazz, String sql, Object... args)
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = 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.执行executeQuery(),得到结果集:ResultSet
rs = ps.executeQuery();
// 5.得到结果集的元数据:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
// 6.1通过ResultSetMetaData得到columnCount,columnLabel;通过ResultSet得到列值
int columnCount = rsmd.getColumnCount();
if (rs.next())
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) // 遍历每一个列
// 获取列值
Object columnVal = rs.getObject(i + 1);
// 获取列的别名:列的别名,使用类的属性名充当
String columnLabel = rsmd.getColumnLabel(i + 1);
// 6.2使用反射,给对象的相应属性赋值
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnVal);
return t;
catch (Exception e)
e.printStackTrace();
finally
// 7.关闭资源
JDBCUtils.closeResource(conn, ps, rs);
return null;
写一个测试方法,然后调用查询方法,就可以找到我们想要的数据:
@Test
Public void testGetInstance()
String sql=”select id, name,email ...from 表名 where 筛选条件 (比如id=?(占位符),大于小于?(占位符)等等)”
调用查询方法getInstance(表名.class,sql,实例化占位符).var;
输出语句sout;
说明:使用PreparedStatement实现的查询操作可以替换Statement实现的查询操作,解决Statement拼串和SQL注入问题。
2、ResultSet与ResultSetMetaData(结果集,元数据)
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开始。**- ResultSet 接口的常用方法:
- boolean next()
- getString()
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:得到结果集后, 如何知道该结果集中有哪些列 ? 列名是什么
需要使用一个描述 ResultSet 的对象, 即 ResultSetMetaData
问题2:关于ResultSetMetaData
1. **如何获取 ResultSetMetaData**: 调用 ResultSet 的 getMetaData() 方法即可
2. **获取 ResultSet 中有多少列**:调用 ResultSetMetaData 的 getColumnCount() 方法
3. **获取 ResultSet 每一列的列的别名是什么**:调用 ResultSetMetaData 的getColumnLabel() 方法
四、资源的释放(关闭)& JDBC API小结
资源的释放
释放ResultSet, Statement,Connection。
数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
可以在finally中关闭,保证及时其他代码出现异常,资源也一定能被关闭。
API小结
两种思想
面向接口编程的思想
ORM思想(object relational mapping)
- 一个数据表对应一个java类
- 表中的一条记录对应java类的一个对象
- 表中的一个字段对应java类的一个属性
注意:sql是需要结合列名和表的属性名来写。注意起别名。两种技术
JDBC结果集的元数据:ResultSetMetaData
- 获取列数:getColumnCount()
- 获取列的别名:getColumnLabel()
通过反射,创建指定类的对象,获取指定的属性并赋值
五、升华
文学涵养提升法:
《清平调·其一》
云想衣裳花想容,春风拂槛露华浓。
若非群玉山头见,会向瑶台月下逢。
请问:全诗写的是谁?
再问:这个谁与李白的关系?
再再问:这个谁有什么称号?
再再再问:由这个谁你能说说唐代的一大特点是什么吗?
又双叒叕问:李白,为什么是一个剑客?号什么?
最后一问:李白是不诗王者的一员?请说出他的一句名言?近而写出能代表其豪迈特色、快意恩仇的一首诗?
最后,请把答案打在公屏上,哦不,请写在评论区。有奖竞猜......
以上是关于JDBC_增删改查的主要内容,如果未能解决你的问题,请参考以下文章