JavaWeb数据库开发知识总结(jdbc基础)
JavaWeb数据库开发知识总结(jdbc基础)
- JavaWeb数据库开发知识总结jdbc基础 JDBC概述
- 单元测试
- JDBC开发步骤 1 加载数据库驱动类的方式以MySql数据库为例
- 2 获取数据库的连接对象
- 3 获取执行SQL语句的对象
- 4 释放资源对象
- 5 案例代码
1. JDBC概述
JDBC:(Java DataBase Connectivity Java数据库连接),是一种用于执行SQL语句的Java的API.可以为多种关系型数据库提供统一的访问.它是由一组使用Java语言编写的类或接口组成.
主要功能是使得Java程序能够连接数据库.
JDBC驱动:
驱动是两个设备之间的通信桥梁.
Java程序需要连接数据库必须使用对应数据库的驱动,驱动就是实现了JDBC规范的数据库操作接口的Java文件.
2. 单元测试
针对Java程序中某一个方法进行的测试.
使用时需要在测试的方法上加上@Test 注解,用来表明该方法可以进行单元测试.
运行时,选中该方法以run as JUnit Test来执行该方法.
要求:单元测试的方法必须是public修饰,无返回值,方法无参数;
3. JDBC开发步骤
- 引入要使用的数据库的驱动的jar包;
- 加载数据库的驱动类;
- 获取数据库的连接对象;
- 编写SQL语句,获取SQL执行对象,执行SQL语句获得结果集对象;
- 执行结果的处理;
- 释放资源对象(结果集对象,执行SQL对象,数据库连接对象).
3.1 加载数据库驱动类的方式(以MySql数据库为例)
使用DriverManager类registerDriver方法加载驱动:
DriverManager.registerDriver(实现jdbc规范的Driver对象); // 加载mysql驱动,将mysql驱动的jar包添加到BuildPath中 // mysql的jar包:mysql-connector-java-5.0.8-bin.jar DriverManager.registerDriver(new Driver()); // Driver()是mysqljar包中的类
使用反射方式加载驱动:
Class.forName(数据库驱动Driver类的全路径名); // 加载mysql驱动 Class.forName("com.mysql.jdbc.Driver"); // 参数是mysql的驱动Driver的全路径名
两种加载驱动方式的比较:
使用registerDriver注册驱动有两个弊端: 该方法是通过导入数据库驱动的类,使得程序依赖了具体的驱动类; mysql的Driver类源码中通过静态代码块注册了驱动类,该方式使得驱动被注册2次. 使用反射方式加载驱动: 较为常用,避免了使用registerDriver注册驱动的弊端,官方推荐使用该方式进行驱动类的加载.
3.2 获取数据库的连接对象
Connection Conn = DriverManager.getConnection(数据库url,数据库用户名,数据库密码); // 获取mysql的数据库连接 DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "root", "root");
数据库url格式说明:
# 以mysql的连接为例:jdbc:mysql://localhost:3306/mydatabase jdbc:代表使用的是jdbc协议连接数据库; mysql:代表使用jdbc的子协议mysql协议(也可能为Oracle协议等); localhost:代表连接数据库服务器的主机名,此格式代表本地服务器,当远程连接时,将此部分换为数据库服务器的主机名或ip地址; 3306:代表的是数据库服务器工作的端口号; mydatabase:代表的是使用数据库服务器中的数据库名称. # 注意: 当使用的本地服务器且端口号为330时,可以将localhost省略,简写为:jdbc:mysql:///mydatabase
3.3 获取执行SQL语句的对象
执行SQL语句的对象有两种:Statement和PreparedStatement.
Statement对象是直接将SQL语句进行编译执行,不能防止SQL注入问题;
PreparedStatement对象会将SQL语句进行预编译,能有效的预防SQL注入问题.
获取执行SQL语句的Statement对象:
通过数据库连接对象获取SQL执行对象 Statement stmt = 数据库连接对象.createStatement(); 通过Statement对象执行SQL语句 String sql = "sql语句"; // sql语句,原始的SQL语句 ResultSet rs = stmt.executeQuery(sql语句); // 执行查询语句,返回的是查询的结果集对象 int row = stmt.executeUpdate(sql语句); // 执行增,删,改的语句,返回执行该sql语句影响的数据库中的行数
获取执行SQL语句的PreparedStatement对象:
通过数据库连接对象获取SQL执行对象,并对sql语句进行预编译 PreparedStatement ps = 数据库连接对象.PreparedStatement(sql语句); 通过PreparedStatement对象执行SQL语句 String sql = "参数化的sql语句"; // sql语句,将其中的参数使用?代替 通过PreparedStatement对象的setXXX()方法将sql语句中的参数设置值,其中的参数索引从1开始,对应字段数据类型要使用相应的设置值的方法 ps.setInt(参数索引,参数值); // 设置指定索引处的int类型的值 ps.setString(参数索引,参数值); // 设置指定索引处的String类型的值 ResultSet rs = ps.executeQuery(); // 执行查询语句,返回的是查询的结果集对象 int row = ps.executeUpdate(); // 执行增,删,改的语句,返回执行该sql语句影响的数据库中的行数
Statement和PreparedStatement对象的区别:
# Statement对象会编译每一个传递的SQL语句,效率较低;不进行预编译检查sql语句,会在数据库中执行传入的原始SQL语句,因此无法避免SQL注入的问题. # PrepareedStatement对象对于相同的SQL语句只会编译一次,预编译后只需要对应的传入参数,效率较高;进行预编译检查SQL语句,会将其中的语句中特殊字符进行转义,有效的避免了SQL注入问题.
3.4 释放资源对象
数据库的连接数量是有限的,使用数据库连接需要遵循`晚创建,早释放`的原则. 为确保数据库连接对象能够及时关闭,通常将资源的释放代码放在finally代码块中.
资源释放标准代码:
// 释放结果集对象资源 if(rs != null) { // 当资源对象不为null时,才释放资源 try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; // 将资源对象置为空,JVM可以尽早回收 } // 释放执行SQL对象资源 if(stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } // 释放数据库连接资源 if(conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } }
3.5 案例代码
public class My_JDBC_Demo01 { // 单元测试 @Test public void demo() throws SQLException, ClassNotFoundException { // 加载驱动,方式1 // DriverManager.registerDriver(new Driver()); // 加载驱动,方式2,反射 Class.forName("com.mysql.jdbc.Driver"); // 获得连接 Connection con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/jdbc", "root", "root"); // 数据库连接的url简写形式 // Connection con = DriverManager.getConnection("jdbc:mysql:///jdbc","root", "root"); // 创建SQL语句,并执行 String sql = "select * from user"; // 获取执行SQL语句的对象 Statement sta = con.createStatement(); // 执行SQL语句并获取结果集 ResultSet rs = sta.executeQuery(sql); // 遍历结果集 while (rs.next()) { // 获取查询结果集中的id字段的值 int id = rs.getInt("id"); // 获取查询结果集中的username字段的值 String username = rs.getString("username"); // 获取查询结果集中的password字段的值 String password = rs.getString("password"); // 输出结果集中每一行的值 System.out.println(id + "---" + username + "---" + password); } // 释放资源 rs.close(); sta.close(); con.close(); } /* * 释放资源标准写法 */ @Test public void demo2() { // 定义资源对象 Connection conn = null; // 连接对象 PreparedStatement ps = null; // 执行SQL语句对象 ResultSet rs = null; // 结果集对象 try { // 加载驱动,反射方式 Class.forName("com.mysql.jdbc.Driver"); // 获得连接 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "root"); // 创建SQL语句,并执行 String sql = "select * from user where id = ?"; // 获取执行SQL语句的对象 ps = conn.prepareStatement(sql); // 设置sql语句中的参数 ps.setInt(1, 1); // sql中的第一个参数的值为1 // 执行SQL语句并获取结果集 rs = ps.executeQuery(sql); // 遍历结果集 while (rs.next()) { // 获取查询结果集中的id字段的值 int id = rs.getInt("id"); // 获取查询结果集中的username字段的值 String username = rs.getString("username"); // 获取查询结果集中的password字段的值 String password = rs.getString("password"); // 输出结果集中每一行的值 System.out.println(id + "---" + username + "---" + password); } } catch (Exception e) { e.printStackTrace(); } finally { // 释放结果集对象资源 if(rs != null) { // 当资源对象不为null时,才释放资源 try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; // 将资源对象置为空,JVM可以尽早回收 } // 释放执行SQL对象资源 if(ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } ps = null; } // 释放数据库连接资源 if(conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } } }
4. JDBC中相关类
4.1 Connection类
Connection类是由DriverManager类获取连接的返回对象.代表一个数据库连接对象.
主要作用有两个方面:创建执行SQL语句的对象和存储事务的管理.
创建执行SQL对象:
Statement createStatement():创建Statement对象来将SQL语句发送到数据库; PreparedStatement prepareStatement(String sql):创建PreparedStatement对象来将参数化的SQL语句发送到数据库; CallableStatement prepareCall(String sql):创建CallableStatement对象来调用数据库存储过程.
事务管理:
void setAutoCommit(boolean autoCommit):将此连接的自动提交模式设置为给定状态,true代表自动提交事务,false代表禁止自动提交事务,默认是自动提交事务; void commit():使所有上一次提交/回滚后进行的更改成为持久更改,并释放此Connection对象当前持有的所有数据库锁,此方法只应该在已禁用自动提交模式时使用; void rollback():取消在当前事务中进行的所有更改,并释放此Connection对象当前持有的所有数据库锁,此方法只应该在已禁用自动提交模式时使用.
4.2 Statement和PreparedStatement对象
Statement和PreparedStatement对象由Connection对象创建,代表是可以执行SQL语句的类.
主要作用有两个方面:执行SQL语句和执行批处理.
执行SQL语句:
Statement类方法: ResultSet executeQuery(String sql):执行给定的SQL语句,该语句返回单个ResultSet结果集对象. int executeUpdate(String sql):执行给定SQL语句,该语句可能为INSERT,UPDATE,DELETE语句,对于SQL数据操作语言语句,返回行计数,对于什么都不返回的SQL语句,返回0; boolean execute(String sql):执行SQL语句,执行select,insert,update,delete语句,返回的是boolean值,如果返回的结果为ResultSet结果集,那么boolean值为true.如果返回的是更新的记录数.那么boolean就为false. PreparedStatement类方法: ResultSet executeQuery():在此PreparedStatement对象中执行SQL查询,并返回该查询生成的ResultSet对象; int executeUpdate():在此PreparedStatement对象中执行SQL语句,该语句必须是一个SQL数据操作语言语句; boolean execute():在此PreparedStatement对象中执行SQL语句,该语句可以是任何种类的SQL语句,如果第一个结果是 ResultSet对象,则返回true,如果第一个结果是更新计数或者没有结果,则返回false. 注意事项: execute()方法的效率较低,当能确定执行的SQL语句的类型时,尽量使用对用的SQL执行对象.
执行批处理:
Statement类的批处理方法:PreparedStatement类无批处理 void addBatch():将一组参数添加到此PreparedStatement对象的批处理命令中; void clearBatch():清空此Statement对象的当前SQL命令列表; int[] executeBatch():将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组. 注意事项: 执行完一次批处理命令后,需要调用清空当前SQL命令列表(clearBatch方法完成).
4.3 ResultSet类
ResultSet对象由Statement或者PreparedStatement对象中executeQuery()方法获取的返回值;
ResultSet对象代表的是select查询后的结果;
在ResultSet内部维护了一个指向标哥数据行的游标Cursor,初始化时候,游标在第一行之前的位置.调用ResultSet中next()方法.可以使游标指向具体的数据行,进而调用方法获得该行的数据.
操作结果集中数据的方法:
boolean next():将光标从当前位置向下移一行; 从结果结果集中获取当前光标指向的行的数据: getXXX(字段名字符串或该结果集中某一列字段的值); // 列的序号从1开始 getInt(String 字段名); // 获取指定字段名的整型数据 getInt(int 字段所在的列); // 获取指定列的改行的整型值 getString(String 字段名); // 获取指定字段名的字符串数据 getString(int 字段所在的列); // 获取指定列的改行的字符串值
遍历结果集数据:
while(rs.next()){ // 按照字段名获取结果集中的数据 int id = rs.getInt("id"); String username = rs.getString("username"); String password = rs.getString("password"); // 将结果输出 System.out.println(id+"---"+username+"---"+password); }
5. SQL注入漏洞
# SQL注入的漏洞在早期互联网中是普遍存在. * 已知用户名的情况下,可以对这个用户名的用户进行攻击. * 1.攻击方式一: * 在用户名地方输入:aaa' or '1=1 * 密码随意. * 2.攻击方式二: * 在用户名地方输入:aaa' -- * 密码随意. # 产生原因:因为在用户名地方输入SQL的关键字.(SQL注入) * SQL语句: * select * from user where username = '' and password = ''; * 产生原因一:可以绕过密码的验证 * select * from user where username = 'aaa' or '1=1' and password = 'xxxx'; * 产生原因二:--在SQL语句中是注释标示,将密码验证代码注释 * select * from user where username = 'aaa' -- ' and password = 'xxxx'; # 解决SQL注入的漏洞: * 1.使用JS在文本框进行校验.校验不可以输入-,or,'特殊的字符都不可以输入. * 不可行:JS的校验只是为了提升用户的体验.不能真正进行校验.(JS的代码可以被绕行.) * 在地址上上直接输入访问路径.loginServlet?username=aaa' or '1=1&password=xxxxxx * 2.SQL注入漏洞真正的解决方案:PreparedStatement.对SQL进行预编译.参数的地方都可以使用?作为占位符. * select * from user where username = ? and password = ?; * 再次输入aaa' or '1=1 拿到参数之后,or一些特殊的字符不当成SQL的关键字,只是当成普通的字符串进行解析; * 提前进行编译.编译后的SQL的格式就已经固定.
6. JDBC工具类的抽取
将数据库驱动加载,连接获取,资源释放做成一个工具类,方便调用.
将数据库的加载驱动类和连接数据库信息写在配置文件中,通过读取配置文件获取.
配置文件的编写:
项目src资源目录下创建jdbc.properties文件(内容如下): driverClass=com.mysql.jdbc.Driver -- 驱动名称 url=jdbc\:mysql\://localhost\:3306/jdbc -- 数据库连接url userName=root -- 数据库用户名 passWord=root -- 数据库密码
工具类中读取配置文件:
/** * JDBC工具类,读取配置文件 */ public class JDBCUtils { // 数据库驱动 private static final String DRIVER_CLASS; // 数据库url private static final String URL; // 数据库用户名 private static final String USERNAME; // 数据库密码 private static final String PASSWORD; // 静态代码块用于初始化 static { Properties prop = null; try { InputStream is = new FileInputStream("src/jdbc.properties"); prop = new Properties(); prop.load(is); } catch (Exception e) { e.printStackTrace(); } DRIVER_CLASS = prop.getProperty("driverClass"); URL = prop.getProperty("url"); USERNAME = prop.getProperty("userName"); PASSWORD = prop.getProperty("passWord"); } // 私有构造方法,禁止创建该类的对象 private JDBCUtils(){} /** * 加载数据库驱动程序 */ private static void loadDriver() { try { Class.forName(DRIVER_CLASS); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 获取数据库的连接 * @return */ public static Connection getConnection() { // 先加载数据库驱动程序 loadDriver(); Connection conn = null; try { // 获取数据库连接并返回 conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); } catch (SQLException e) { e.printStackTrace(); } return conn; } /** * 释放数据库的连接,用于查询语句的数据库连接释放 * @param rs 查询结果集 * @param stat 执行SQL对象 * @param conn 数据库连接对象 */ public static void release(ResultSet rs, Statement stat, Connection conn) { if(rs != null) { try { rs.close(); } catch (Exception e) { e.printStackTrace(); } rs = null; } if(stat != null) { try { stat.close(); } catch (Exception e) { e.printStackTrace(); } stat = null; } if(conn != null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); } conn = null; } } /** * 释放数据库的连接,用于释放没有结果集的连接 * @param stat 执行SQL语句对象 * @param conn 数据库连接对象 */ public static void release(Statement stat, Connection conn) { if(stat != null) { try { stat.close(); } catch (Exception e) { e.printStackTrace(); } stat = null; } if(conn != null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); } conn = null; } } }
转载于:https://my.oschina.net/u/3481752/blog/898644
- JavaWeb数据库开发知识总结(jdbc基础)
- 传智播客——javaWEB开发基础知识
- Mars老师的Android学习基本路线Linux、数据库、网络协议、服务器端开发知识基础知识
- 微信小程序开发基础知识总结
- SQL点滴17—使用数据库引擎存储过程,系统视图查询,DBA,BI开发人员必备基础知识
- 数据库基础知识总结
- 基础知识(C#语法、数据库SQL Server)回顾与总结
- 软件开发综合实践实训总结(SQL基础知识学习与Web前后端实践学习)
- Mars老师的Android学习基本路线Linux、数据库、网络协议、服务器端开发知识基础知识
- java开发中基于JDBC连接数据库实例总结
- 全国计算机技术与软件专业技术资格(水平)考试【软件评测师】-考试内容总结(三)数据库基础知识
- Spring JDBC-Spring事务管理之数据库事务基础知识
- Java数据库连接——JDBC基础知识(操作数据库:增删改查)
- 数据库学习总结之 基础知识
- Java数据库连接——JDBC基础知识(操作数据库:增删改查)【转】
- SQL 数据库基础知识总结(一)
- JDBC基础知识总结
- iOS开发基础知识总结
- JavaWeb开发基础:JDBC