# JDBC介绍
- (java数据基础连接,java Database Connectivity )是标准的java访问数据库的API。
- sun公司为了简化,统一对数据库的操作,定义了一套java操作数据库的规范,称为JDBC
- 驱动:两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。
- 如果没有JDBC,java开发人员需要面向各个数据库驱动接口编程,开发复杂;Sun公司提供一套统一jdbc接口规范,java程序只需要使用JDBC就可以操作任何数据库,JDBC实现类由各个数据库厂商提供。
涉及到的包
- java.sql
- 类:DriverManger
- 接口:Connection ResultSet Statement PreparedStatement
- CallableStatement (用于调用存储过程)
- javax.sql
- 接口:DateSource
快速入门
Jdbc入门代码,完成对数据库操作
--编程从User表中读取数据,并打印在命令行窗口中
create table user(
id int primary key auto_increment,
username varchar(20) unique not null,
password varchar(20) not null,
email varchar(40) not null
);
import com.mysql.jdbc.Driver;
import java.sql.*;
import java.sql.Connection;
import java.sql.Statement;
/**
* Created by zst on 2018/6/2.
*/
public class Mysql1 {
public static void main(String[] args) throws SQLException {
DriverManager.registerDriver(new Driver());
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/zst_db1", "root", "root");
//System.out.println(con);
Statement statement = con.createStatement();
//因为主键设置了自增模式,所以插入数据时 主键用null 表示,不能手动写主键值
String sql1="insert into user values (null,'xiaofu1','8709','678')";
String sql2="select * from user";
int i = statement.executeUpdate(sql1);
ResultSet resultSet = statement.executeQuery(sql2);
while(resultSet.next()){
int anInt = resultSet.getInt("id");
System.out.println(anInt);
}
//释放资源
re.close();
st.close();
con.close();
}
}
jdbc api 详解
DriverManager
JDBC程序中的DriverManger用于加载驱动,并创建与数据库的链接
该API常用的方法:
- DriverManager.registerDriver(new Driver())
- registerDriver方法分析
- public static synchronized void registerDriver(java.sql.Driver driver)
- DriverManager.getConnction(url,user,password)
注意:在实际开发中并不推荐采用registerDriver方法注册驱动。原因如下:
- 查看Driver的源代码可以知道,如果采用此种方式,会导致驱动程序注册两次。
//com.mysql.jdbc.Driver类中的一段静态代码块
static {
try{
java.sql.DriverManager.registerDriver(new Driver());
}catch (SQLException E){
throw new RuntimeException("Can not register driver!");
}
}
- 程序依赖MySQL的API。脱离MySQL的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。
- 推荐方式:Class.forName(“com.mysql.jdbc.Driver”);
- 只加载一次,装入一个驱动对象。
- 降低耦合,不依赖与驱动
- 采用此种方式不会导致驱动对象在内存中重复出现,并且采用此种方式,程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。
通过DriverManager来获取连接对象
- Connection con = DriverManager.getConection(String url,String user,String password);
- url的作用:用于确定使用哪一个驱动
- MySQL url:jdbc:mysql://localhost:3306/数据库名
- Oracle url:jdbc:oracle:thin:@localhost:1521:sid
DriverManager 的作用
- 注册驱动
- 获取连接 Connection
url
- url格式
- 主协议 子协议 主机 端口 数据库
- jdbc:mysql:://localhost:3306/ 数据库名
- MySQL的url可以简写
- 前提:主机是localhost 端口号是3306
- 简写:jdbc:mysql:///数据库名
- 在url后面可以带参数
- useUnicode = true&characterEncoding=UTF-8
Connection
java.sql.Connection 代表一个连接对象,即程序与数据库的连接
Connection作用
- 通过Connection 获取操作SQL的Statement对象
- Statement createStatement() throws SQLException
- 示例:Statement st = con.createStatement();
- 了解:
- 可以获取执行预处理的PreparedStatement对象
- PreparedStatement prepareStatement(String sql)throws SQLException
- 可以获取执行存储过程的 CallableStatement
- CallableStatement prepareCall(String sql )throws SQLException
- 操作事务
- setAutoCommit(boolean flag);开启事务
- rollback();事务回滚
- commit();事务提交
Statement
java.sql.Statement 用于执行SQL语句
- Statement 作用
- 执行SQL
- DML:insert update delete
- int executeUpdate(String sql)
- 利用返回值判断非0来确定SQL语句是否执行成功
- DQL:select
- ResultSet executeQuery(String sql)
- 可以通过execute方法来执行任何SQL语句
- execute(String sql):用于向数据库发送任意sql语句
- 批处理操作(多条SQL语句一起执行)
- addBatch(String sql);将SQL语句添加到批处理
- executeBatch();批量执行
- clearBatch();清空批处理
ResultSet
java.sql.resultSet 它是用于封装select语句执行后查询的结果
jdbc程序中的resultSet用于代表Sql语句的执行结果。ResultSet封装执行结果时,采用的类似于表格的方式,ResultSet对象维护一个指向表格数据行的游标cursor,初始的时候,游标在第一行之前,调用ResultSet.next()方法,可以使游标指向具体的数据行,进而调用方法获取该行的数据。
//遍历结果集
while(rs.next()){
int id=rs.getInt("id");
String username=rs.getString("usename");
String password=re.getString("password");
String email = rs.getString("email");
System.out.println(id+""+username+""+password+""+email);
}
常用API
- next()方法
- public boolean next();
- 用于判断是否有下一条记录,如果有返回true,并且让游标向下移动一行。如果没有返回false。
- 可以通过ResultSet提供的getXXX()方法来获取当前游标指向的这条记录中的列数据。
- 常用:
- getInt()
- getString()
- getDate()
- getDouble()
- 参数有两种
- getInt(int columnIndex);
- getInt(String columnName);
- 如果列的类型不知道,可以通过下面的方法来操作
- getObject(int columnIndex);
- getObject(String columnName);
释放资源
- jdbc程序运行完后,要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet,Statement和Connection对象
- 特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时,正确的关闭,极易导致系统宕机。Connection 的使用原则是尽量晚创建,尽量早释放。
- 为确保资源释放代码能运行,资源释放代码一定要放在finally语句中
public class MyException{
public static void main(String[] args){
Connection con = null;
Statement st=null;
ResultSet rs=null;
try{
Class.forName("com.mysql.jdbc.Driver");
con=DriverManager.getConnection("jdbc:mysql:///zst_db1","root","root")
st=con.createStatement();
rs=st.executeQuery("select * from user");
while(rs.next()){
int id=rs.getInt("id");
String username=rs.getString("usename");
String password=re.getString("password");
String email = rs.getString("email");
System.out.println(id+""+username+""+password+""+email);
}
}catch(ClassNotFoundException e){
e.printStackTrace();
}catch(SQLException e){
e.printStackTrace();
}finally{
try{
if(rs != null){
re.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try{
if(st != null){
st.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try{
if(con != null){
con.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
工具类
- 抽取Connection,因为不能确定用哪个Statement对象
- 局限性
- 只能针对MySQL数据库
- 每一次调用getConnection,都会注册一次驱动。
public class JdbcUtils{
public static Connection getConnection() throws ClassNotFoundException,SQLException{
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/zst_db1","root","root");
return con;
}
}
对JdbcUtils进行修改
- 通过配置文件进行改进
//jdbc.properties
driverClass = com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/zst_db1
username=root
password=123
public class JdbcUtils{
private static final String DRIVERCLASS;
private static final String URL;
private static final String USERNAME;
private static final String PASSWORD;
static{
DRIVERCLASS=ResourceBundle.getBundle("jdbc").getString("driverClass");
URL=ResourceBundle.getBundle("jdbc").getString("url");
USERNAME=ResourceBundle.getBundle("jdbc").getString("username");
PASSWORD=ResourceBundle.getBundle("jdbc").getString("password");
}
//静态代码块注册驱动,保证驱动只加载一次
static{
try{
Class.forName(DRIVERCLASS);
}catch(ClassNotFoundException e){
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException{
Connection con = DriverManager.getConnection(URL,USERNAME,PASSWORD);
return con;
}
}
//关闭操作
public static void closeConnection(Connection con ) throws SQLException{
if(con != null){
con.close();
}
}
滚动结果集
默认得到的ResultSet只能向下遍历(next()),对于ResultSet 它可以设置成是滚动的,可以向上遍历,或者直接定位到一个指定的物理行号。
- 如何得到一个滚动结果集
- Statement st = con.createStatement();
- ResultSet rs = st.executeQuery(sql);
- 这是一个默认的结果集:只能向下执行,并且只能迭代一次。
- 创建Statement对象时,不使用createStatement();,而使用带参数的createStatement(int,int)
Statement createStatement(int resultSetType,int resultSetConcurrency)throws SQLException
- resultSetType = 结果集类型,它可以是下列中的一个
- ResultSet.TYPE_FORWARD_ONLY
- 该常量指示光标只能向前移动的ResultSet对象的类型
- ResultSet.TYPE_SCROLL_INSENSITIVE
- 该常量指示可滚动,但ResultSet 对象的类型不受ResultSet底层数据更改的影响
- ResultSet.TYPE_SCROLL_SENSITIVE
- 该常量指示可滚动,但ResultSet 对象的类型受ResultSet底层数据更改的影响
- resultsetConcurrency -并发类型,它可以是下列中的一个
- ResultSet。CONCUR_READ_ONLY
- 该常量指示不可更新的ResultSet对象的并发模式
- ResultSet.CONCUR_UPDATABLE
- 该常量指示不可更新的ResultSet对象的并发模式
- 另外三种搭配模式
- ResultSet.TYPE_FORWARD_ONLY —ResultSet。CONCUR_READ_ONLY
- ResultSet.TYPE_SCROLL_INSENSITIVE —ResultSet。CONCUR_READ_ONLY
- ResultSet.TYPE_SCROLL_SENSITIVE—ResultSet.CONCUR_UPDATABLE
//创建滚动结果集
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSer.COMCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery(sql);
对滚动结果集进行滚动和更新的方法
- con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSer.COMCUR_UPDATABLE);
- next();移动到下一行
- previous();移动到前一行
- absolute(int row)移动到制定行
- beforeFirst();移动到ResultSet的最前面
- afterLast(0;移动到ResultSet的最后面
- updateRow();更新行数据
使用dao模式登录操作
- DAO模式(Data Access Object 数据访问对象):在持久层通过DAO将数据源操作完全封装起来,业务层通过操作java对象,完成对数据源操作
- 业务层无需知道数据源底层实现,通过java对象操作数据源
DAO模式结构
- 数据源(MySQL数据库)
- Business Object 业务层代码,调用DAO完成,对数据源操作
- DataAccessObject 数据访问对象,持久层DAO程序,封装对数据源增删改查,提供方法参数都是java对象
- TeansferObject 传输对象(值对象) 业务层通过向数据层传递 TO 对象,完成对数据源的增删改查
SQL注入
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时。在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。
String sql = select* from user where username = ' ' and password = ' ';
String sql = select* from user where username = ' ' or '1'='1' and password = ' ';
-- 输入username;'老李' or '1'='1' password 随意
解决方案
- PreparedStatement(重点)
- 它是一个预处理的Statement,是java.sql.Statement接口的一个子接口
- PreparedStatement的使用
- 在SQL语句中,使用“?”占位
- 得到PreparedStatement对象
- PreparedStatement pst = con.PrepareStatement(String sql );
- 对占位符赋值
- pst.setXxx(int index,Xxx obj);
- 举例:
- setInt()
- setString();
- 参数index,代表的是”?”的序号,注意:从1开始
- 执行SQL
- DML:pst.executeUpdate();
- DQL : pst.executeQuery();
- 注意:这两个方法无参数
- PreparedStatement优点
- 解决SQL注入(具有预处理功能)
- 不需要在拼SQL语句
jdbc处理数据块
MySQL中的数据块:
- blob 大二进制
- TINYBLOB(255)、BLOB(64kb)、MEDIUMBLOB(16m)和LONGBLOB(4g)
- text(clob)大文本
- TINYTEXT(255)、TEXT(64kb)、MEDIUMTEXT(16m)和LONGTEXT(4g)
- 两种操作:insert select
//存储大文本
create table mytext(
id int primary key auto_increment,
content longtext
);
//存储
File file = new File("D:\\java110\\workspace\\zst_db1\\a.txt");
FileReader fr = new FileReader(file);
pst.setCharacterStream(1,fr,(int)(file.length()));
//获取
Reader r = rs.getCharacterStream("content");
jdbc批处理
- 一次可以执行多条SQL语句
- 在jdbc中可以执行SQL语句的对象有Statement。PreparedStatement,它们都提供批处理
- Statement执行批处理
- addBatch(String sql);将SQL语句添加到批处理
- executeBatch();执行批处理
- clearBatch();
- PreparedStatement 执行批处理
- addBatch()
- executeBatch()
- clearBatch();
- 以上两个对象执行批处理区别
- Statement 更适合执行不同SQL的批处理,它没有提供预处理功能,性能比较低
- PreparedStatement 适合执行相同SQL的批处理,它提供了预处理功能,性能比较高
- 注意: