需求
早期的数据库应⽤程序开发,因为没有通⽤的针对于数据库的编程接⼝,所以,开发⼈员需要学习相关数据库的API,才可以进⾏应⽤程序,这样增加了学习成本和开发周期。因此整个开发市场⼀直在呼吁有⼀套通⽤的编程接⼝
ODBC
因为有市场需要,微软定义了⼀组⽤于数据库应⽤程序的编程接⼝ODBC(open database connectivity)。这⼀套⽅案⼤⼤缩短了程序的开发周期,可以让开发⼈员只需要调⽤同⼀套编程接⼝,⽆需考虑具体实现。
ODBC分为四个部分:
1. 应⽤程序:开发⼈员所写的代码,ODBC提供的调⽤接⼝
2. 驱动程序管理器:⽤于管理驱动程序的。
3. 驱动程序:对接⼝的实现部分,各个数据库⼚商来完成的。
4. 数据源:就是连接数据库的⼀些参数:url,username,password
JDBC
Sun公司参考了ODBC⽅案,制定了⼀组专⻔为java语⾔连接数据库的通⽤接⼝JDBC。⽅便了java开发⼈员,开发⼈员不需要考虑特定的数据库的DBMS。JDBC不直接依赖于DBMS,⽽是通过驱动程序将sql语句转发给DBMS,由DBMS进⾏解析并执⾏,处理结果返回。
**注意:**驱动程序:由数据库⼚商⾃⼰实现,程序员只需要拿来使⽤即可。
JDBC的⼯作原理
第⼀步:注册驱动程序
第⼆步: 请求连接
第三步: 获取执⾏sql语句的对象,发送给DBMS
第四步:返回结果集,程序员进⾏处理
第五步: 关闭连接操作
JDBC中常⽤的接⼝和类
概述
JDBC与数据库驱动的关系:
接⼝与实现的关系。
JDBC规范(掌握四个核⼼对象):
DriverManager:⽤于注册驱动
Connection: 表示与数据库创建的连接
Statement: 操作数据库sql语句的对象
ResultSet: 结果集或⼀张虚拟表
开发⼀个JDBC程序的准备⼯作:
JDBC规范在哪⾥:
JDK中:
java.sql.;
javax.sql.;
数据库⼚商提供的驱动:jar⽂件
*.ja
java.sql.DriverManager
a.注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());不建议使⽤
原因有2个:
导致驱动被注册2次。
强烈依赖数据库的驱动jar
解决办法://5.0使用方式
Class.forName(“com.mysql.jdbc.Driver”);
//8.0版本使用方式
Class.forName(“com.mysql.cj.jdbc.Driver”);
b.与数据库建⽴连接
⽅法名字:
static Connection getConnection(String url, String user, String
password)
试图建⽴到给定数据库 URL 的连接。
url: 连接指定数据库的地址 jdbc:mysql://ip:port/dbname
user: 连接⽤户名
password:密码
getConnection("jdbc:mysql://localhost:3306/day06", "root","root");
对URL的解释:
URL:SUN公司与数据库⼚商之间的⼀种协议。
jdbc:mysql://localhost:3306/day06
//上面URL是5.0版本使用的方式,下面为8.0增加一个时间戳
jdbc:mysql://localhost:3306/oa_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
协议 ⼦协议 IP :端⼝号 数据库
mysql: jdbc:mysql://localhost:3306/day14 或者
jdbc:mysql:///day14(默认本机连接)
oracle: jdbc:oracle:thin:@localhost:1521:sid
示例:
Connection con =
DriverManager.getConnection("jdbc:mysql://localhost:3306/day14?user=root&password=root");
java.sql.Connection接⼝
接⼝的实现在数据库驱动中。所有与数据库交互都是基于连接对象的。
Statement createStatement();
作⽤:⽤于获取Statement对象
java.sql.Statement接⼝
作⽤:操作sql语句,并返回相应结果的对象(⼩货⻋)
接⼝的实现在数据库驱动中。⽤于执⾏静态 SQL 语句并返回它所⽣成结果的对象。
ResultSet executeQuery(String sql) 根据查询语句返回结果集。只能执⾏select语句。
int executeUpdate(String sql) 根据执⾏的DML(insert update delete)语句,返回受影响的⾏数。
boolean execute(String sql) 此⽅法可以执⾏任意sql语句。返回boolean值,表示是否返回ResultSet结果集。仅当执⾏select语句,且有返回结果时返回true, 其它语句都返回false;
这个execute⽅法不好,因为执⾏查询时返回true,其他操作返回false,⽆法知道是否删除或插⼊或修改成功.
execute(String sql):通常⽤于DDL
executeUpdate(String sql):通常⽤于DML
executeQuery(String sql):⽤于DQL
java.sql.ResultSet接⼝
表示结果集(客户端存表数据的对象)
a.封装结果集
常规
提供⼀个游标,默认游标指向结果集第⼀⾏之前。
调⽤⼀次next(),游标向下移动⼀⾏。
提供⼀些get⽅法。
封装数据的⽅法
Object getObject(int columnIndex); 根据序号取值,索引从1开始
Object getObject(String ColomnName); 根据列名取值。
将结果集中的数据封装到javaBean中
java的数据类型与数据库中的类型的关系
常⽤⽅法
boolean next() 将光标从当前位置向下移动⼀⾏
int getInt(int colIndex) 以int形式获取ResultSet结果集当前⾏指定列号值
int getInt(String colLabel) 以int形式获取ResultSet结果集当前⾏指定列名值
float getFloat(int colIndex) 以float形式获取ResultSet结果集当前⾏指定列号值
float getFloat(String colLabel) 以float形式获取ResultSet结果集当前⾏指定列名值
String getString(int colIndex) 以String 形式获取ResultSet结果集当前⾏指定列号值
String getString(String colLabel) 以String形式获取ResultSet结果集当前⾏指定列名值
Date getDate(int columnIndex);
Date getDate(String columnName);
void close() 关闭ResultSet 对象
b.可移动游标的⽅法
boolean next() 将光标从当前位置向前移⼀⾏。 boolean previous()将光标移动到此 ResultSet 对象的上⼀⾏。
boolean absolute(int row) 参数是当前⾏的索引,从1开始
根据⾏的索引定位移动的指定索引⾏。
void afterLast()
将光标移动到末尾,正好位于最后⼀⾏之后。
void beforeFirst()
将光标移动到开头,正好位于第⼀⾏之前。
示例
JDBC⼊⻔编程
准备⼯作
数据库准备
直接使⽤⽂档(数据库⾼级)中创建的雇员表emp
代码编写步骤
1. 创建项⽬,加载相应的静态资源,如图⽚、第三⽅jar包(.jar⽂件)等
注意:要对架包进⾏buildPath操作----idea中需要选中jar包所在的⽬录--右键---add as libaray,在出现提示框后,直接点击确定.
具体实现:将mysql-connector-java-5.0.8-bin.jar jdbc连接包导⼊⼯程
步骤:
1.1 在⼯程下新建⼀个⽂件夹(mybin)
1.2 将mysql-connector-java-5.0.8-bin.jar导⼊mybin或者直接拷⻉粘贴
1.3 选中mybin,右击选择add as libaray,出现提示框后,点击确定.
2. 注册驱动
3. 建⽴连接
4. 获取执⾏对象
5. 处理结果集
6. 关闭连接
jdbc基本实现
public class Demo1 {
public static void main(String[] args) throws SQLException {
//1.注册mysql的驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2.创建连接对象--connection对象
Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2","root","123456");
//3.建⽴⼩⻋并绑定sql语句
Statement statement = connection.createStatement();
//绑定sql语句
String sql = "select empno,ename,job from emp";
ResultSet set = statement.executeQuery(sql);
//4.将数据放⼊箱⼦,拉回客户端,并完成卸货.
//原理:类似于迭代器,开始指针指向表头,调⽤next⽅法会使指针向下移动⼀
⾏,判断当前⾏是否有数据,如果有,返回true,没有返回false
while (set.next()){
//第⼀种:根据sql语句中字段的下标取值,默认从1开始
//set⾥对应的是虚拟表的数据,所以我们这⾥的顺序跟虚拟表中字段的顺序⼀致
// Object obj1 = set.getObject(1);
//第⼆种:通过字段名字(key)取值
// Object obj2 = set.getObject("empno");
// System.out.println(obj1+" "+obj2);
//完全通过字段名字(key)取值
int empno = set.getInt("empno");
String name = set.getString("ename");
String job = set.getString("job");
System.out.println("empno:"+empno+"
ename:"+name+" job:"+job);
}
//5.关闭资源
connection.close();
statement.close();
set.close();
}
}
jdbc相关内容
内容提示:
1.不再使⽤new,直接使⽤反射
new的缺点:导致驱动被注册2次;强烈依赖数据库的驱动jar
替代⽅案:使⽤反射实现
Class.forName(“com.mysql.jdbc.Driver”);
2.创建连接对象的⽅式有三种
3.4.前⾯实现的是查找(⽤executeQuery())
ResultSet set = statement.executeQuery(sql);
现在实现的是增删改(统⼀⽤executeUpdate()
int num = statement.executeUpdate(sql);
代码实现
public class Demo2 {
public static void main(String[] args) throws SQLException,ClassNotFoundException {
//1.注册mysql的驱动
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//注册使⽤反射--节省空间,开发⽅便
Class.forName("com.mysql.jdbc.Driver");
//2.创建连接对象--connection对象
//第⼀种:三个参数
//Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2","root","123456");
//第⼆种:两个参数
// Properties properties = new Properties();
// properties.setProperty("user","root");
// properties.setProperty("password","123456");
// Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2",properties);
//第三种:⼀个参数
Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2?
user=root&password=123456");
//3.建⽴⼩⻋并绑定sql语句
Statement statement = connection.createStatement();
//增删改---executeUpdate()
//增加
String sql = "insert into emp(empno,ename,job)
values(100,'bing','演员')";
int num = statement.executeUpdate(sql);
if (num != 0){
System.out.println("插⼊成功");
}
//5.关闭资源--可选
connection.close();
statement.close();
//set.close();
}
}
jdbc的模型封装
内容提示
1.对于从数据库接收到数据,我们⼀般是需要保存到模型中,⼀个模型对应数据库中的
⼀张表.下⾯的代码实现了这个功能.
2.在编写项⽬时,我们需要创建很多特定功能的包,让⼯程的结构清晰,增加代码可读性,
易于编程实现.
注意:这⾥的servlet层,service层,dao层分别对应的是MVC开发模式中的三层,jdbc⾼级中会讲
DBUtil⼯具类的封装
说明:
1.编写代码时使⽤⾯向对象的思想,尽量将代码进⾏封装,这⾥将数据库的链接和关闭等共同的操作放⼊了DBUtils⼯具类中
2.对于共享的DBUtils⼯具类,不能每次发⽣⼀些变动(⽐如:更换数据库或者修改密码等操作),都去更改代码.所以我们⼜将变化的部分封装了配置⽂件DBConfig.properties.
配置⽂件DBConfig.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb2
user=root
pwd=123456
DBUtils代码
package com.qf.util;
import java.sql.*;
import java.util.ResourceBundle;
public class DBUtils {
static String mydriver;
static String myurl;
static String myuser;
static String mypwd;
static {
ResourceBundle bundle = ResourceBundle.getBundle("DBConfig");
mydriver = bundle.getString("driver");
myurl = bundle.getString("url");
myuser = bundle.getString("user");
mypwd = bundle.getString("pwd");
try {
Class.forName(mydriver);
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(myurl,myuser,mypwd);
}
public static void closeAll(Connection connection, Statement statement, ResultSet set){
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (set != null){
try {
set.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
jdbc的批处理
概念
每⼀次的sql操作都会占⽤数据库的资源。如果将N条操作先存储到缓存区中,然后再⼀次性刷到数据库中,这就减少了与数据库的交互次数。因此可以提⾼效率。
Statement
addBatch(String sql):将sql语句添加到缓存中
executeBatch():将缓存中的sql⼀次性刷到数据库中
代码
@Test
public void testBatch(){
Connection conn = null;
Statement stat = null;
try{
conn = DBUtils.getConnection();
stat = conn.createStatement();
int num = 0;
while(num<1003){
String sql = "insert into testbatch values(null,'zs"+num+"','f')";
stat.addBatch(sql);//将sql语句添加到缓存中,
if(num%50==0){
stat.executeBatch();//缓存中每有50条都刷新⼀次。
}
num++;
}
stat.executeBatch();//循环结束后,将缓存中剩余的不⾜50条的全都刷新出去
}catch (Exception e){
e.printStackTrace();
}finally {
DBUtils.closeAll(conn,stat,null);
}
}
SQL注⼊问题
登陆案例演示
登陆案例需求分析
1.需求:输⼊⽤户名和密码后,实现跳转到主⻚⾯的功能
2.逻辑分析:
- 客户端: 接收⽤户名和密码,并将这些信息发送到服务端
- 服务端:接收到客户端传过来的⽤户名和密码后,进⾏数据库校验是否存在这样的数据,如果存在,就将⽤户名对应的这⼀条记录返回,并封装成⼀个User对象。返回给客户端。
- 客户端收到返回信息后,判断User对象是否存在,如果存在,就实现跳转.....
3.注意:因为没有学习真正的服务器知识,所以这⾥是通过⽅法调⽤模拟客户端与服务器端的通信
测试
1.先运⾏服务器端
2.运⾏客户端,输⼊测试数据
姓名:bingbing
密码:123456
结果:登录成功
SQL注⼊问题(安全隐患)
注意:
当输⼊
⽤户名:chen
密码:’ or 1='1时,报错.
原因
Statament对象发送的语句可以被改变结构,即如果之前在where中设置的是两个条件,那么可以通过⼀些参数 ⽐如 添加or 后⾯再跟其他条件。此时,where⼦句中是三个条件。这种情况就叫做SQL注⼊。有安全隐患问题。
PreparedStatement类
预编译类的简介
- PreparedStatement是Statement的⼦类型
- 此类型可以确定SQL语句的结构,⽆法通过其它⽅式来增减条件。
- 此类型还通过占位符 "?"来提前占位,并确定语句的结构。
- 提供了相应的赋值⽅式:
ps.setInt(int index,int value)
ps.setString(int index,String value)
ps.setDouble(int index,double value)
ps.setDate(int index,Date value)
index:表示sql语句中占位符?的索引。从1开始
value:占位符所对应的要赋予的值
- 执⾏⽅法:
ps.execute() ;------⽤于DDL和DML
ps.executeUpdate();-----⽤于DML
ps.executeQuery();-----⽤于DQL
编译类的简介
- PreparedStatement是Statement的⼦类型
- 此类型可以确定SQL语句的结构,⽆法通过其它⽅式来增减条件。
- 此类型还通过占位符 "?"来提前占位,并确定语句的结构。
- 提供了相应的赋值⽅式:
ps.setInt(int index,int value)
ps.setString(int index,String value)
ps.setDouble(int index,double value)
ps.setDate(int index,Date value)
index:表示sql语句中占位符?的索引。从1开始
value:占位符所对应的要赋予的值
- 执⾏⽅法:
ps.execute() ;------⽤于DDL和DML
ps.executeUpdate();-----⽤于DML
ps.executeQuery();-----⽤于DQL