一、JDBC:用于执行SQL语句的Java API
1、Statement:
(1)Statement继承自Wrapper。
(2)Statement接口提供了执行语句和获取结果的基本方法。
(3)普通的不带参的查询SQL;支持批量更新,批量删除
2、PreparedStatement:
(1)PreparedStatement继承自Statement。
(2)添加了处理 IN 参数的方法。
(3)可变参数的SQL,编译一次,执行多次,效率高; 安全性好,有效防止Sql注入等问题; 支持批量更新,批量删除。
sql注入:
百度解答:通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
简单理解:恶意用户通过在表单中填写包含SQL关键字的数据来使数据库执行非常规代码的过程。即,我们在数据项中加入了某些SQL语句关键字(比如说SELECT、DROP等等),这些关键字就很可能在数据库写入或读取数据时得到执行。
举例:创建一个学生档案表。
// SQL数据库操作示例: import sqlite3; // 连接数据库: conn = sqlite3.connect(‘test.db‘); //建立新的数据表: conn.executescript(‘‘‘DROP TABLE IF EXISTS students; CREATE TABLE students (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL);‘‘‘) //插入学生信息: students = [‘Paul‘,‘Tom‘,‘Tracy‘,‘Lily‘] for name in students: query = "INSERT INTO students (name) VALUES (‘%s‘)" % (name) conn.executescript(query); //检视已有的学生信息: cursor = conn.execute("SELECT id, name from students") print(‘IDName‘) for row in cursor: print(‘{0}{1}‘.format(row[0], row[1])) conn.close();
sql注入:
conn = sqlite3.connect(‘test.db‘) // 插入包含注入代码的信息 name = "Robert‘);DROP TABLE students;--" query = "INSERT INTO students (name) VALUES (‘%s‘)" % (name) conn.executescript(query) // 检视已有的学生信息 cursor = conn.execute("SELECT id, name from students") print(‘IDName‘) for row in cursor: print(‘{0}{1}‘.format(row[0], row[1])) conn.close(); /** 最后执行的sql语句为: * * "INSERT INTO students (name) VALUES (‘Robert‘);DROP TABLE students;--" * * DROP TABLE:删除表数据。 * * sql执行完了insert之后,直接执行了DROP TABLE,把表删除了。故而会出现错误提示: * * 表单students无法找到! * */
3、CallableStatement:
(1)CallableStatement继承自PreparedStatement。
(2)添加了处理 OUT 参数的方法。
(3)继承自PreparedStatement,支持带参数的SQL操作; 支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持。
Statement和PreparedStatement的区别:
Statement每次执行sql,数据库都要执行sql的编译,所以适用于仅执行一次查询并返回结果的一条sql时,比PreparedStatement效率高。
PreparedStatement是预编译的,使用PreparedStatement有几个好处 :
①在执行可变参数的一条SQL时,PreparedStatement比Statement的效率高,因为DBMS预编译一条SQL当然会比多次编译一条SQL的效率要高。
② 安全性好,有效防止Sql注入等问题。
③ 对于多次重复执行的语句,使用PreparedStament效率会更高一点,并且在这种情况下也比较适合使用batch(批处理)。
④ 代码的可读性和可维护性更高。
二、JDBC调用存储过程
/** 首先,JDBC 的 Statement,CallableStatement 和 PreparedStatement 接口定义的方法和属性, * 可以让你发送 SQL 命令到数据库,并从你的数据库接收数据, * 其中CallableStatement被用来执行数据库访问存储过程的时候。 * * 原文链接:https://blog.csdn.net/weixin_40803477/java/article/details/80234048 */ // 创建存储过程: mysql> CREATE DATABASE EMP $$ mysql> DROP PROCEDURE IF EXISTS `EMP`.`getEmpName` $$ mysql> CREATE PROCEDURE `EMP`.`getEmpName` -> (IN EMP_ID INT, OUT EMP_FIRST VARCHAR(255)) -> BEGIN -> SELECT first INTO EMP_FIRST -> FROM Employees -> WHERE ID = EMP_ID; -> END $$ mysql> DELIMITER ; // 创建CallableStatement对象,调用存储过程 import java.sql.CallableStatement; import java.sql.Connection; import java.sql.SQLException; import java.sql.Types; import org.apache.commons.dbcp.BasicDataSource; public class JdbcConnectedPro { private static Connection connection=null; private static CallableStatement callableStatement = null; private static String sql = null; //创建connection对象 public static Connection getConnection() { //创建dataSource BasicDataSource dataSource = new BasicDataSource(); //加载数据库驱动 dataSource.setDriverClassName("org.gjt.mm.mysql.Driver"); //设置用户名和密码 dataSource.setUrl("jdbc:mysql://localhost:3306/emp"); dataSource.setUsername("root"); dataSource.setPassword("root"); try { connection=dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return connection; } //基于存储过程实例化CallableStatement对象 public static CallableStatement getStatement() { try { callableStatement = connection.prepareCall(sql); } catch (SQLException e) { e.printStackTrace(); } return callableStatement; } //关闭资源 public static void close() { if (null != callableStatement) { try { callableStatement.close(); if (null != connection) { connection.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { sql = "{call getEmpName (?, ?)}"; try { getConnection(); getStatement(); callableStatement.setInt(1, 1); callableStatement.registerOutParameter(2, Types.VARCHAR); callableStatement.execute();//执行 int result = callableStatement.getInt(2); System.out.println("返回结果为"+result); } catch (SQLException e) { e.printStackTrace(); } } }
三、class.forname和ClassLoader
两个都是类加载器,一般情况下,spring框架的IOC的使用就是ClassLoader ,而JDBC 时通常是使用 Class.forName() 方法来加载数据库连接驱动。
四、用 JDBC 查询学生成绩单, 把主要代码写出来
class Untitled { public static void main(String[] args) { Collection ct = null; PreprareStatement ps = null; ResultSet rs = null; try{ Class.forName(driveClassName);//载入JDBC驱动程序 cn = DriverManager.getConnerction(url,user,password); ps = cn.prepareStatement("select sc.* from score,student s where sc.stId = s.stId and s.name = ? "); ps.setString(1,studentName);// 1对应第一个问号,即把传入的参数代入到sql中对应的问号。 rs = ps.executeQuery(); //遍历输出结果 while(rs.next()){ //getInt():通过索引或者列名来获得查询结果集中的某一列的值。 system.out.println(rs.getInt("student_name") + " " + rs.getFloat("score");) } } catch(Exception e){ e.printStackTrace(); } finally{ try(){ cn.close(); ps.close(); rs.close(); }catch(SQLException e){ e.printStackTrace(); } } } }
//注意事项:
变量的定义放在try语句块外面
要用finally语句来关闭哥哥对象
五、数据连接池的工作机制
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
具体工作机制如下:
J2EE服务器启动时会建立一定数量的池连接,即数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
六、为什么要使用ORM技术?和 JDBC 有何不一样?
1、代码繁琐:
用JDBC的API编程访问数据库,代码量较大,特别是访问字段较多的表的时候,代码显得繁琐、累赘,容易出错。 例如:
public void addAccount(final Account account) throws DAOException { final Connection conn=getConnection(); PreparedStatement pstmt=con.prepareStatment("insert into account value(?,?,?,?,?,?,?,?,?)"); pstmt.setString(1,account.getUserName()); pstmt.setInt(2,account.getPassWord()); pstmt.setString(3,account.getSex()); pstmt.setString(4,account.getQq()); ...... pstmt.execute(); conn.Close(); }
ORM则建立了Java对象与数据库对象之间的影射关系,程序员不需要编写复杂的SQL语句,直接操作Java对象即可,从而大大降低了代码量,也使程序员更加专注于业务逻辑的实现。
2、数据库对象连接问题
关系数据对象之间,存在各种关系,包括1对1、1对多、多对1、多对多、级联等。在数据库对象更新的时候,如果采用JDBC编程,程序员必须十分小心处理这些关系,以保证维持这些关系不会出现错误。
ORM建立Java对象与数据库对象关系影射的同时,也自动根据数据库对象之间的关系创建Java对象的关系,并且提供了维持这些关系完整、有效的机制。
3、系统架构问题
现在的应用系统,一般由展示层、业务逻辑层、数据访问层、数据库层等组成,各层次功能划分非常清晰。JDBC属于数据访问层,但是使用JDBC编程时,程序员必须知道后台是用什么数据库、有哪些表、各个表有有哪些字段、各个字段的类型是什么、表与表之间什么关系、创建了什么索引等等与后台数据库相关的详细信息。相当于软件程序员兼职数据库DBA。
使用ORM技术,可以将数据库层完全隐蔽,呈献给程序员的只有Java的对象,程序员只需要根据业务逻辑的需要调用Java对象的Getter和 Setter方法,即可实现对后台数据库的操作,程序员不必知道后台采用什么数据库、有哪些表、有什么字段、表与表之间有什么关系。
4、性能问题
采用JDBC编程,在很多时候存在效率低下的问题,比如sql执行很多次时,会导致运行效率低。
采用ORM技术,ORM框架将根据具体数据库操作需要,会自动延迟向后台数据库发送SQL请求,或者根据实际情况,将数据库访问操作合成,尽量减少不必要的数据库操作请求。
/** 首先,JDBC 的 Statement,CallableStatement 和 PreparedStatement 接口定义的方法和属性, * 可以让你发送 SQL 命令到数据库,并从你的数据库接收数据, * 其中CallableStatement被用来执行数据库访问存储过程的时候。 * * 原文链接:https://blog.csdn.net/weixin_40803477/java/article/details/80234048 */
// 创建存储过程:mysql> CREATE DATABASE EMP $$
mysql> DROP PROCEDURE IF EXISTS `EMP`.`getEmpName` $$
mysql> CREATE PROCEDURE `EMP`.`getEmpName` -> (IN EMP_ID INT, OUT EMP_FIRST VARCHAR(255)) -> BEGIN -> SELECT first INTO EMP_FIRST -> FROM Employees -> WHERE ID = EMP_ID; -> END $$mysql> DELIMITER ;
// 创建CallableStatement对象,调用存储过程import java.sql.CallableStatement;import java.sql.Connection;import java.sql.SQLException;import java.sql.Types;
import org.apache.commons.dbcp.BasicDataSource;
public class JdbcConnectedPro { private static Connection connection=null; private static CallableStatement callableStatement = null; private static String sql = null; //创建connection对象 public static Connection getConnection() { //创建dataSource BasicDataSource dataSource = new BasicDataSource(); //加载数据库驱动 dataSource.setDriverClassName("org.gjt.mm.mysql.Driver"); //设置用户名和密码 dataSource.setUrl("jdbc:mysql://localhost:3306/emp"); dataSource.setUsername("root"); dataSource.setPassword("root"); try { connection=dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return connection; } //基于存储过程实例化CallableStatement对象 public static CallableStatement getStatement() { try { callableStatement = connection.prepareCall(sql); } catch (SQLException e) { e.printStackTrace(); } return callableStatement; } //关闭资源 public static void close() { if (null != callableStatement) { try { callableStatement.close(); if (null != connection) { connection.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { sql = "{call getEmpName (?, ?)}"; try { getConnection(); getStatement(); callableStatement.setInt(1, 1); callableStatement.registerOutParameter(2, Types.VARCHAR); callableStatement.execute();//执行 int result = callableStatement.getInt(2); System.out.println("返回结果为"+result); } catch (SQLException e) { e.printStackTrace(); } }}