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: 结果集或一张虚拟表
注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());不建议使用
原因有2个:
> 导致驱动被注册2次。
> 强烈依赖数据库的驱动jar
解决办法:
Class.forName("com.mysql.jdbc.Driver");
与数据库建立连接
试图建立到给定数据库 URL 的连接。
url: 连接指定数据库的地址 jdbc:mysql://ip:port/dbname
user: 连接用户名
password:密码
getConnection("jdbc:mysql://localhost:3306/day06", "root", "root");
Statement createStatement();
作用:用于获取Statement对象
java.sql.Statement接口
作用:操作sql语句,并返回相应结果的对象(小货车)
查找执行的是executeQuery,增删改执行的是executeUpdate
execute(String sql):通常用于DDL
executeUpdate(String sql):通常用于DML
executeQuery(String sql):用于DQL
java.sql.ResultSet接口
表示结果集(客户端存表数据的对象)
提供一个游标,默认游标指向结果集第一行之前。
调用一次next(),游标向下移动一行。
提供一些get方法。
jdbc基本实现
//1.注册mysql的驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2.创建连接对象--connection对象
Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2","root","123
456");
//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"); //取int型的值
String name = set.getString("ename"); //取string型的值
String job = set.getString("job");
//循环外
//5.关闭资源
connection.close();
statement.close();
set.close();
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);
使用放射jdbc
//注册使用反射--节省空间,开发方便
Class.forName("com.mysql.jdbc.Driver");
jdbc三中创建连接对象的方法
1三个参数
//Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2","root","123
456");
2两个参数
Properties properties = new Properties();
properties.setProperty("user","root");
properties.setProperty("password","123456");
Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2",properties)
;
3一个参数
Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2?
user=root&password=123456");
绑定sql语句
Statement statement = connection.createStatement();
定义sql语句
String sql = "insert into emp(empno,ename,job)
values(100,'bing','演员')";
执行sql语句
int num = statement.executeUpdate(sql); //给一个返回值判断sql是否执行成功
//5.关闭资源
connection.close();
statement.close();
//set.close();
jdbc的模型封装
对于从数据库接收到数据,我们一般是需要保存到模型中,一个模型对应数据库中的一张表
Class.forName("com.mysql.jdbc.Driver");
Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2?
user=root&password=123456");
Statement statement = connection.createStatement();
String sql = "select empno,ename,job from emp";
ResultSet set = statement.executeQuery(sql);
List<Emp> list = new ArrayList<>();
while (set.next()){
Emp emp = new Emp(set.getInt("empno"),set.getString("ename"),set.getString("job"));
list.add(emp);
}
connection.close();
statement.close();
set.close();
emp实体
public Emp(int empno, String ename, String job) {
this.empno = empno;
this.ename = ename;
this.job = job;
}
jdbc的异常处理
这里异常不应该声明,而是使用trycatch
//1.注册mysql的驱动
//反射--节省空间,开发方便
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Connection connection = null;
Statement statement = null;
ResultSet set = null;
try {
connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2?
user=root&password=123456");
//3.建立小车并绑定sql语句
statement = connection.createStatement();
//绑定sql语句
String sql = "select empno,ename,job from emp";
//查
set = statement.executeQuery(sql);
List<Emp> list = new ArrayList<>();
while (set.next()) {
Emp emp = new Emp(set.getInt("empno"),
set.getString("ename"), set.getString("job"));
list.add(emp);
}
System.out.println(list);
}
catch (SQLException e) {
e.printStackTrace();
} finally {
//5.关闭资源--可选
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();
}
}
}
}
}
DBUtil工具类的封装
1.编写代码时使用面向对象的思想,尽量将代码进行封装,这里将数据库的链接和关闭等共同的
操作放入了DBUtils工具类中
2.对于共享的DBUtils工具类,不能每次发生一些变动(比如:更换数据库或者修改密码等操作),都
去更改代码.所以我们又将变化的部分封装了配置文件DBConfifig.properties.
配置文件DBConfifig.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb2
user=root
pwd=123456
//读取DBConfig文件的内容
//默认识别的路径是当前的工程
ResourceBundle resourceBundle =
ResourceBundle.getBundle("DBConfig");
mydriver = resourceBundle.getString("driver");
myurl = resourceBundle.getString("url");
myuser = resourceBundle.getString("user");
mypwd = resourceBundle.getString("pwd");
//1.注册mysql的驱动
//反射--节省空间,开发方便
try {
Class.forName(mydriver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//自定义dbutils工具类以方便直接调用
public static Connection getConnection() throws SQLException {
//2.创建连接对象--connection对象
return DriverManager.getConnection(myurl,myuser,mypwd);
}
jdbc的批处理
每一次的sql操作都会占用数据库的资源。如果将N条操作先存储到缓存区中,然后再一次性刷
到数据库中,这就减少了与数据库的交互次数。因此可以提高效率。
addBatch(String sql):将sql语句添加到缓存中
executeBatch():将缓存中的sql一次性刷到数据库中
代码
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注入问题
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
sql语句:select * from user where name='chen' and password='' or 1='1'"
当前的错误称为sql注入.
Connection connection = null;
PreparedStatement statement = null;
ResultSet set = null;
User user = null;
try {
connection = DBUtil.getConnection();
//statement = connection.createStatement();
String sql = "select * from user where name=? and
password=?";//?占位符
statement =connection.prepareStatement(sql);
statement.setString(1, name);//第一个参数是?在sql语句中的位置,默
认从1开始
statement.setString(2, pwd);
set = statement.executeQuery();
//遍历
if (set.next()) {
user = new User();
user.setId(set.getInt("id"));
user.setName(set.getString("name"));
user.setPassword(set.getString("password"));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
DBUtil.closeAll(connection, statement, set);
}
return user;
}