文章目录
JDBC的介绍:
- JDBC(Java Database Connection)为java开发者使用数据库提供了统一的编程接口,它由一组java类和接口组成。是java程序与数据库系统通信的标准API。JDBC API使得开发人员可以使用纯java的方式来连接数据库,并执行操作。
- sun公司由于不知道各个主流商用数据库的程序代码,因此无法自己写代码连接各个数据库,因此, sun公司决定,自己提供一套api ,凡是数据库想与Java进行连接的,数据库厂商自己必须实现JDBC这套接口。而数据库厂商的JDBC实现,我们就叫他此数据库的数据库驱动。
访问数据库流程:
JDBC常用接口
Driver接口
- Driver接口由数据库厂家提供,对于java开发者而言,只需要使用Driver接口就可以了。
- 在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序。不同的数据库有不同的装载方法。
- 驱动:就是各个数据库厂商实现的Sun公司提出的JDBC接口。 即对Connection等接口的实现类的jar文件
- 装载MySq|驱动
Class.forName(“com.mysql.jdbc.Driver”);
加载驱动类, jdk13后会自动加载 8.0后的写法 :
Class.forName(“com.mysql.cj.jdbc.Driver”); - 装载Oracle驱动
Class.forName(“oraclejdbc.driver.OracleDriver”);
DriverManager接口
- DriverManager是JDBC的管理层,作用于用户和驱动程序之间。
- DriverManager跟踪可用的驱动程序,并在数据库和相应的驱动程序之间建立连接。
Connection接口
- Connection与特定数据库的连接(会话) ,在连接上下文中执行SQL语句并返回结果。
- DriverManager的getConnection()方法建立在JDBC URL中定义的数据库Connection连接上
- 连接MYSQL数据库:
Connection con =
DriverManager.getConnection("jdbc:mysq://host:port/database","user","tpassword");
Statement接口
- 用于执行静态SQL语句并返回它所生成结果的对象。
三种Statement类 :
Statement
:
由createStatement创建,用于发送简单的SQL语句。(不带参数的)PreparedStatement
:
继承自Statement接口,由prepareStatement创建,用于发送含有一个或多个输入参数的sq|语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入。我们一般都用PreparedStatement.CallableStatement
:
继承自PreparedStatement。由方法prePareCall创建,用于调用存储过程。
常用的Statement方法:
execute()
:运行语句,返回是否有结果集。使用较少。executeQuery()
:运行select语句,返回ResultSet结果集。executeUpdate()
: 运行insert/update/delete操作,返回更新的行数。
测试数据库的连接和SQL注入
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 测试与数据库的连接
*/
public class Demo01 {
public static void main(String[] args) {
try {
//加载驱动类, jdk13后会自动加载 8.0后的写法:com.mysql.cj.jdbc.Driver
Class.forName("com.mysql.jdbc.Driver");
long start = System.currentTimeMillis();
//建立链接(链接对象内部其实包含了Socket对象,是一个远程的连接,所以建立链接是比较耗时的)
//为了提好效率可以使用连接池来管理链接对象!
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/create","root","hc199854");
long end = System.currentTimeMillis();
System.out.println(conn);
//这是进行本地链接,如果是远程链接会更加耗时
System.out.println("建立链接花费的时间:"+(end-start));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
执行结果:
com.mysql.jdbc.JDBC4Connection@181afa3
建立链接花费的时间:372
测试Statement和SQL注入:
SQL注入, 这是使用Statement的一个严重缺点,因为使用的参数,比如id参数是其他人传进来的,如果传进的是来一个恶意的参数可能会破坏我们的数据库。
/**
* 测试执行SQL语句,以及SQL注入问题
*/
public class Demo02 {
public static void main(String[] args) {
try {
//加载驱动类, jdk13后会自动加载
Class.forName("com.mysql.jdbc.Driver");
//建立链接(链接对象内部其实包含了Socket对象,是一个远程的连接,所以建立链接是比较耗时的)
//为了提好效率可以使用连接池来管理链接对象!
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","hc199854");
Statement stmt = conn.createStatement();
//字符串最后不需要添加分号
// String sql = "insert into t_user(username, pwd, regTime) values('赵六',6666,now())";
// stmt.execute(sql);
//测试SQL注入, 这是使用Statement的一个严重缺点,因为使用的参数,比如id参数
//是其他人传进来的,如果传进的是来一个恶意的参数比如id = "5 or 1=1"
//然后我们把参数拼接执行,会把所有的数据都删除(由于1=1恒为真)
String id = "5";
String sql = "delete from t_user where id=" + id;
stmt.execute(sql);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
测试执行PreparedStatement的基本用法
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 测试执行PreparedStatement的基本用法
*/
public class Demo03 {
public static void main(String[] args) {
try {
//加载驱动类, jdk13后会自动加载
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","hc199854");
String sql = "insert into t_user(username, pwd, regTime) values(?,?,?)"; //?表示占位符,后面再设置
PreparedStatement ps = conn.prepareStatement(sql);
//防止SQL注入
ps.setString(1, "会程");//参数索引是从1开始的,而不是0
ps.setString(2, "12345678");
//也可以使用ps.setObject(2, "12345678");从而不需要考虑数据类型问题
ps.setObject(3, new Date(System.currentTimeMillis()));
ps.execute();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
ResultSet接口
- Statement执行SQL语句时返回ResultSet结果集。
- ResultSet提供的检索不同类型字段的方法,常用的有:
getString()
:获得在数据库里是varchar. char等数据类型的对象。
getFloat()
:获得杂数据库里是Float类型的对象。
getDate()
:获得在数据库里面是Date类型的数据。
getBoolean()
:获得在数据库里面是Boolean类型的数据
关闭使用对象及连接的顺序
ResultSet→Statement→Connection
测试ResultSet的基本用法
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 测试ResultSet的基本用法
*/
public class Demo04 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//加载驱动类, jdk13后会自动加载
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","hc199854");
String sql = "select * from t_user where id>?"; //?表示占位符,后面再设置
ps = conn.prepareStatement(sql);
ps.setObject(1, 2); //查询id大于2的用户数据
rs = ps.executeQuery(); //获取SQL语句执行后的结果集
while(rs.next()){ //查看是否有下一行,每执行一次表示行号的游标就会加一,游标从0开始
//getInt(i)表示获取第i列的数据
System.out.println(rs.getInt(1)+"--"+rs.getString(2)+"--"+rs.getString(3));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally{
//关闭顺序:resultset-->statment-->connection 三个try catch一定要分开写
try {
if(rs!=null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps!=null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试结果:
3–赵六–6666
4–赵六–6666
5–会程–12345678
批处理
- Batch
- 对于大量的批处理,建议使用Statement ,因为PreparedStatement的预编译空间有限,当数据星特别大时 ,会发生异常。
测试批处理Batch的基本用法
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 测试批处理Batch的基本用法
*/
public class Demo05 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//加载驱动类, jdk13后会自动加载
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","hc199854");
conn.setAutoCommit(false); //设置为手动提交
stmt = conn.createStatement();
long start = System.currentTimeMillis();
for(int i=0; i<20000; i++){
stmt.addBatch("insert into t_user(username,pwd,regTime) values('hc"+i+"', '123',now())");
}
stmt.executeBatch();
conn.commit();//提交事务
long end = System.currentTimeMillis();
System.out.println("插入20000条需要的时间:"+(end-start));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally{
//关闭顺序:resultset-->statment-->connection 三个try catch一定要分开写
try {
if(rs!=null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(stmt!=null)
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试结果:
插入20000条需要的时间:5773
事务介绍
概念:
- 组要么同时执行成功,要么同时执行失败的SQL语句。是数据库操作的一个执行单元!
事务开始于:
- 连接到数据库上,并执行一条DML语句(INSERT、UPDATE或DELETE)。
- 前一个事务结束后,又输入了另外-条DML语句。
事务结束于:
- 执行COMMIT或ROLLBACK语句。
- 执行条DDL语句,例如CREATE TABLE语句;在这种情况下,会自动执,行COMMIT语句。
- 执行条DCL语句,例如GRANT语句;在这种情况下,会自动执行COMMIT语句。
- 断开与数据库的连接。
- 执行了一条DML语句,该语句却失败了;在这种情况中,会为这个无效的DML语句执行ROLLBACK语句。
SQL语句的分类:
数据定义语言DDL
数据查询语言DQL
数据操纵语言DML
数据控制功能DCL
分类详情
事务的四大特点( ACID )
- atomicity (原子性)
表示一个事务内的所有操作是一个整体,要么全部成功,要么全失败。 - consistency ( 一致性)
表示个事务内有一 个操作失败时,所有的更改过的数据都必须回滚到修改前的状态。 - isolation (隔离性)
事务查看数据时数据所处的状态,要么是另一 并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。 - durability (持久性)
持久性事务完成之后,它对于系统的影响是永久性的。
注:JDBC里默认自动提交事务。
事务隔离级别从低到高:
- 读取未提交( Read Uncommitted)
- 读取已提交(Read Committed)
- 可重复读( Repeatable Read)
- 序列化( serializable)
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 测试事务
*/
public class Demo06 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps1 = null;
PreparedStatement ps2 = null;
ResultSet rs = null;
try {
//加载驱动类, jdk13后会自动加载
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","hc199854");
conn.setAutoCommit(false); //设置手动提交(JDBC默认自动提交)
ps1 = conn.prepareStatement("insert into t_user(username, pwd) values(?,?)");
ps1.setObject(1, "张三");
ps1.setObject(2, "12345");
ps1.execute();
System.out.println("已插入一条数据");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ps2 = conn.prepareStatement("insert into t_user(username, pwd) values(?,?,?)");
ps2.setObject(1, "李四");
ps2.setObject(2, "12345");
ps2.execute();
System.out.println("已插入第二条数据");
conn.commit(); //提交事务
} catch (ClassNotFoundException e) {
e.printStackTrace();
//当插入发生异常时,进行回滚(这是进行模拟,由于设置了手动提交,一个事务中发生错误时,会自动发生回滚)
try {
conn.rollback(); //回滚
} catch (SQLException e1) {
e1.printStackTrace();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
//关闭顺序:resultset-->statment-->connection 三个try catch一定要分开写
try {
if(rs!=null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps1!=null)
ps1.close();
if(ps2!=null)
ps2.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试结果:
当插入第二条数据发生错误时,发生回滚,第一条数据也不会插入数据库中。
JDBC中的Date操作
插入随机的日期:
/**
* 向数据库中插入随机的日期
*/
public class Demo07 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//加载驱动类, jdk13后会自动加载
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","hc199854");
for(int i=0; i<1000; i++){
ps = conn.prepareStatement("insert into t_user(username,pwd,regTime) values(?,?,?)");
ps.setString(1, "hc");
ps.setString(2, "12345678");
int rmd = 100000000 + new Random().nextInt(1000000000); //产生一个100000000到1000000000的数
java.sql.Date date = new Date(System.currentTimeMillis() - rmd);
ps.setObject(3, date);
ps.execute();
}
System.out.println("插入完成");
} catch (ClassNotFoundException e) {
e.printStackTrace();
//当插入发生异常时,进行回滚(这是进行模拟,由于设置了手动提交,一个事务中发生错误时,会自动发生回滚)
try {
conn.rollback(); //回滚
} catch (SQLException e1) {
e1.printStackTrace();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
//关闭顺序:resultset-->statment-->connection 三个try catch一定要分开写
try {
if(rs!=null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps!=null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
查询指定日期范围的数据:
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Random;
/**
* 查找指定日期范围的数据
*/
public class Demo08 {
/**
* 将字符串代表的日期转换为long类型数字(格式:yyyy-MM-dd hh:mm:ss)
*/
public static long str2Date(String dateStr){
DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
try {
return format.parse(dateStr).getTime();
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//加载驱动类, jdk13后会自动加载
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","hc199854");
ps = conn.prepareStatement("select * from t_user where regTime>? and regTime<?");
java.sql.Date start = new java.sql.Date(str2Date("2020-7-08 00:00:00"));
java.sql.Date end = new java.sql.Date(str2Date("2020-7-10 00:00:00"));
ps.setObject(1, start);
ps.setObject(2, end);
rs = ps.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id")+"--"+ rs.getString("username")+"--"+ rs.getObject("regTime"));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
//当插入发生异常时,进行回滚(这是进行模拟,由于设置了手动提交,一个事务中发生错误时,会自动发生回滚)
try {
conn.rollback(); //回滚
} catch (SQLException e1) {
e1.printStackTrace();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
//关闭顺序:resultset-->statment-->connection 三个try catch一定要分开写
try {
if(rs!=null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps!=null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
CLOB ( Character Large Object )
- 用于存储大量的文本数据。
- 大字段有些特殊,不同数据库处理的方式不一样,大字段的操作常常是以流的方式来处理的。而非一般的字段,一次即可读出数据。
Mysql中相关类型:
- TINYTEXT最大长度为255字符的TEXT列。
- TEXT[(M)]最大长度为65,535字符的TEXT列。
- MEDIUMTEXT最大长度为16,777.215字符的TEXT列。
- LONGTEXT最大长度为4,294,967,295或4GB字符的TEXT列。
测试CLOB 文本大对象的使用
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Random;
/**
* 测试CLOB 文本大对象的使用
* 包含:将字符串、文件内容插入数据库中的CLOB字段、将CLOB字段值取出来的操作
*/
public class Demo09 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//加载驱动类, jdk13后会自动加载
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","hc199854");
// //将文件内容输入到数据库的CLOB字段中
// ps = conn.prepareStatement("insert into t_user(username,myInfo) values(?,?)");
// ps.setString(1, "HC");
// ps.setClob(2, new FileReader(new File("d:/a.txt"))); //将文本内容总结输入到数据库中
//将程序中的字符串输入到数据库的CLOB字段中
// ps = conn.prepareStatement("insert into t_user(username,myInfo) values(?,?)");
// ps.setString(1, "HC");
// ps.setClob(2, new BufferedReader(new InputStreamReader(new ByteArrayInputStream("aaaaabbbbb".getBytes()))));
// ps.execute();
//将数据库中的CLOB字段读出
ps = conn.prepareStatement("select * from t_user");
rs = ps.executeQuery();
while(rs.next()){
Clob c = rs.getClob("myInfo");
Reader r = c.getCharacterStream();
int temp = 0;
while((temp=r.read())!=-1){
System.out.print((char)temp);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
//当插入发生异常时,进行回滚(这是进行模拟,由于设置了手动提交,一个事务中发生错误时,会自动发生回滚)
try {
conn.rollback(); //回滚
} catch (SQLException e1) {
e1.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//关闭顺序:resultset-->statment-->connection 三个try catch一定要分开写
try {
if(rs!=null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps!=null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试结果:
aaaaabbbbb
BLOB ( Binary Large Object )
- 用于存储大量的二进制数据。
- 大字段有些特殊,不同数据库处理的方式不-样,大字段的操作常常是以流的方式来处理的。而非一般的字段,一次即可读出数据。
Mysgl中相关类型:
- TINYBLOB最大长度为255字节的BLOB列。
- BLOB[(M)]最大长度为65,535字节的BLOB列。
- MEDIUMBLOB最大长度为16,777,215字节的BLOB列。
- LONGBLOB最大长度为4, 294,967,295或4GB字节的BLOB列。
测试BLOB 二进制大对象的使用
package jdbc;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Random;
/**
* 测试BLOB 二进制大对象的使用
*/
public class Demo10 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
InputStream is = null;
OutputStream os = null;
try {
//加载驱动类, jdk13后会自动加载
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","hc199854");
// //将图片存入数据库BLOB字段
// ps = conn.prepareStatement("insert into t_user(username,headImg) values(?,?)");
// ps.setString(1, "HC");
// ps.setBlob(2, new FileInputStream("E:/text.jpg"));
// ps.execute();
ps = conn.prepareStatement("select * from t_user");
rs = ps.executeQuery();
while(rs.next()){
Blob b = rs.getBlob("headImg");
is = b.getBinaryStream();
int temp =0;
while((temp=is.read())!=-1){
System.out.print((char)temp);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
//当插入发生异常时,进行回滚(这是进行模拟,由于设置了手动提交,一个事务中发生错误时,会自动发生回滚)
try {
conn.rollback(); //回滚
} catch (SQLException e1) {
e1.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//关闭顺序:resultset-->statment-->connection 三个try catch一定要分开写
try {
if(rs!=null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps!=null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}