首页 > 代码库 > IT十八掌 JDBC
IT十八掌 JDBC
JDBC
Jdbc介绍
快速入门
Jdbc api详情
Sql注入
PreparedStatement
JDBC处理大数据
JDBC批处理
JDBC介绍
dbc是什么?
JDBC(Java Data Base Connectivity,java数据库连接)
简单说:就是可以直接通过java语言,去操作数据库。
jdbc是一套标准,它是由一些接口与类组成的。
学习中涉及到的类与接口
它们主要在两个包下
java.sql
类:DriverManger
接口 Connection Statement ResultSet PreparedStatement
CallableStatement(它是用于调用存储过程)
javax.sql
接口 DataSource
什么是驱动?
两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信
JDBC体验
l 编程从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
);
一、搭建实验环境 :
1、在mysql中创建一个库,并创建user表和插入表的数据。
2、新建一个Java工程,并导入数据驱动。
二、编写程序,在程序中加载数据库驱动
DriverManager. registerDriver(Driver driver)
三、建立连接(Connection)
Connection conn = DriverManager.getConnection(url,user,pass);
四、创建用于向数据库发送SQL的Statement对象,并发送sql
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
五、从代表结果集的ResultSet中取出数据,打印到命令行窗口
六、断开与数据库的连接,并释放相关资源
1.下载驱动
将驱动jar包复制到lib下.
2.创建一个JdbcDemo1类
// 1.注册驱动
DriverManager.registerDriver(new Driver());
// 2.获取连接对象
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/day17", "root", "abc");
// 3.通过连接对象获取操作sql语句Statement
Statement st = con.createStatement();
// 4.操作sql语句
String sql = "select * from user";
// 操作sql语句(select语句),会得到一个ResultSet结果集
ResultSet rs = st.executeQuery(sql);
// 5.遍历结果集
// boolean flag = rs.next(); // 向下移动,返回值为true,代表有下一条记录.
// int id = rs.getInt("id");
// String username=rs.getString("username");
// System.out.println(id);
// System.out.println(username);
while(rs.next()){
int id=rs.getInt("id");
String username=rs.getString("username");
String password=rs.getString("password");
String email=rs.getString("email");
System.out.println(id+" "+username+" "+password+" "+email);
}
//6.释放资源
rs.close();
st.close();
con.close();
JDBC编程步骤
JDBC访问数据库的流程
通过DriverManager加载驱动程序driver;//
通过DriverManager类获得表示数据库连接的Connection类对象;
通过Connection对象绑定要执行的语句,生成Statement类对象;
执行SQL语句,接收执行结果集ResultSet;
可选的对结果集ResultSet类对象的处理;
必要的关闭ResultSet、Statement和Connection
DriverManager介绍
加载可以通过反射加载
// DriverManager.registerDriver(new Driver()); //加载了两个驱动
Class.forName("com.mysql.jdbc.Driver"); // 加载mysql驱动
Class.forName("oracle.jdbc.driver.OracleDriver");// 加载oracle驱动
怎么知道是加载mysql还是oracal数据呢?
url作用:就是用于确定使用哪一个驱动.
mysql url: jdbc:mysql://localhsot:3306/数据库名.
oralce url: jdbc:oracle:thin:@localhost:1521:sid
总结
1.注册驱动
DriverManager.registDriver(new Driver());
1.DriverManager类
它是java.sql包下的一个驱动管理的工具类,可以理解成是一个容器(Vector),可以装入很多数据库驱动
它的registDriver方法分析
public static synchronized void registerDriver(java.sql.Driver driver)
参数:java.sql.Driver
我们传递的是 com.mysql.jdbc.Driver;
在com.mysql.jdbc.Driver类中有一段静态代码块:
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can‘t register driver!");
}
}
上述代码的问题:
1.在驱动管理器中会装入两个mysql驱动.
解决方案:使用反射
Class.forName("com.mysql.jdbc.Driver");
分析:使用反射的方式来加载驱动有什么好处?
1.只加载一次,装入一个驱动对象.
2.降低耦合,不依赖于驱动.
2.可以通过DriverManager来获取连接对象
Connection con=DriverManager.getConection(String url,String user,String password);
url作用:就是用于确定使用哪一个驱动.
mysql url: jdbc:mysql://localhsot:3306/数据库名.
oralce url: jdbc:oracle:thin:@localhost:1521:sid
总结:DriverManager作用:
1.注册驱动
2.获取连接Connection
关于url
url格式
主协议 子协议 主机 端口 数据库
jdbc : mysql ://localhost:3306/day24?
mysql的url可以简写:
前提:主机是localhost 端口是3306
jdbc:mysql:///day17
了解:在url后面可以带参数
useUnicode=true&characterEncoding=UTF-8
Connection详解
java.sql.Connection,它代表的是一个连接对象。简单说,就是我们程序与数据库连接。
Connection作用:
1.可以通过Connection获取操作sql的Statement对象。
Statement createStatement() throws SQLException
示例:
Statement st=con.createStatement();
了解:
1.可以获取执行预处理的PreparedStatement对象.
PreparedStatement prepareStatement(String sql) throws SQLException
2.可以获取执行存储过程的 CallableStatement
CallableStatement prepareCall(String sql) throws SQLException
2.操作事务
setAutoCommit(boolean flag);开启事务
rollback();事务回滚
commit();事务提交
Statement详解
java.sql.Statement用于执行sql语句.
Statement作用:
1.执行sql
DML:insert update delete
int executeUpdate(String sql)
利用返回值判断非0来确定sql语句是否执行成功。
DQL:select
ResultSet executeQuery(String sql)
可以通过execute方法来执行任何sql语句.
execute(String sql):用于向数据库发送任意sql语句
2.批处理操作
addBatch(String sql); 将sql语句添加到批处理
executeBatch();批量执行
clearBatch();清空批处理.
案例:
//演示executeUpdate方法
public class JdbcDemo3 {
public static void main(String[] args) throws SQLException,
ClassNotFoundException {
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver"); // 加载mysql驱动
String url = "jdbc:mysql:///day17";
// 2.获取连接对象
Connection con = DriverManager.getConnection(url, "root", "root");
// 3.通过连接对象获取操作sql语句Statement
Statement st = con.createStatement();
// 4.执行update语句
int row = st.executeUpdate("update user set password=‘135‘ where id=1");
System.out.println(row);
// 6.释放资源
st.close();
con.close();
}
}
ResultSet详解
java.sql.ResultSet它是用于封装select语句执行后查询的结果。
常用API
1.next()方法
public boolean next();用于判断是否有下一条记录。如果有返回true,并且让游标向下移动一行。
如果没有返回false.
2.可以通过ResultSet提供的getXxx()方法来获取当前游标指向的这条记录中的列数据。
常用:
getInt()
getString()
getDate()
getDouble()
参数有两种
1.getInt(int columnIndex);
2.getInt(String columnName);
如果列的类型不知道,可以通过下面的方法来操作
getObject(int columnIndex);
getObject(String columnName);
思考:如果SQL语句可能会返回一行数据,也可能查不到任何记录时,代码应该怎么写?
程序详解—释放资源
Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。
特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
//对异常进行try-catch
public class JdbcDemo5 {
public static void main(String[] args) {
Connection con = null;
Statement st = null;
ResultSet rs = null;
try {
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
con = DriverManager.getConnection("jdbc:mysql:///day17", "root",
"abc");
// 3.获取操作sql语句对象Statement
st = con.createStatement();
// 4.执行sql
rs = st.executeQuery("select * from user");
// 5.遍历结果集
while (rs.next()) {
int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.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 {
// 6.释放资源
try {
if (rs != null) {
rs.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();
}
}
}
}
CURD
//jdbc的crud操作
public class JdbcDemo6 {
@Test
public void findByIdTest() {
// 1.定义sql
String sql = "select * from user where id=1";
Connection con = null;
Statement st = null;
ResultSet rs = null;
try {
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
con = DriverManager.getConnection("jdbc:mysql:///day17", "root",
"abc");
// 3.获取操作sql语句对象Statement
st = con.createStatement();
// 4.执行sql
rs = st.executeQuery(sql);
// 5.遍历结果集
while (rs.next()) {
int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.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 {
// 6.释放资源
try {
if (rs != null) {
rs.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();
}
}
}
// 添加操作
@Test
public void addTest() {
// 定义sql
String sql = "insert into user values(null,‘张三‘,‘123‘,‘zs@163.com‘)";
Connection con = null;
Statement st = null;
ResultSet rs = null;
try {
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
con = DriverManager.getConnection("jdbc:mysql:///day17", "root",
"abc");
// 3.获取操作sql语句对象Statement
st = con.createStatement();
// 4.执行sql
int row = st.executeUpdate(sql);
if (row != 0) {
System.out.println("添加成功");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6.释放资源
try {
if (rs != null) {
rs.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();
}
}
}
// update操作
@Test
public void updateTest() {
// 将id=3的人的password修改为456
String password = "456";
String sql = "update user set password=‘" + password + "‘ where id=3";
// 1.得到Connection
Connection con = null;
Statement st = null;
try {
con = JdbcUtils1.getConnection();
// 3.获取操作sql语句对象Statement
st = con.createStatement();
// 4.执行sql
int row = st.executeUpdate(sql);
if (row != 0) {
System.out.println("修改成功");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 关闭资源
try {
if (st != null)
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (con != null)
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// delete测试
@Test
public void deleteTest() {
// 将id=3的人删除
String sql = "delete from user where id=2";
// 1.得到Connection
Connection con = null;
Statement st = null;
try {
con = JdbcUtils.getConnection();
// 3.获取操作sql语句对象Statement
st = con.createStatement();
// 4.执行sql
int row = st.executeUpdate(sql);
if (row != 0) {
System.out.println("删除成功");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 关闭资源
try {
JdbcUtils.closeStatement(st);
JdbcUtils.closeConnection(con);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
抽取
public class JdbcUtils1 {
public static Connection getConnection() throws ClassNotFoundException,
SQLException {
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
Connection con = DriverManager.getConnection("jdbc:mysql:///day17",
"root", "abc");
return con;
}
}
第二种抽取:
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql:///day17
username=root
password=abc
#driverClass=oracle.jdbc.driver.OracleDriver
#url=jdbc:oracle:thin:@localhost:1521:XE
#username=system
#password=system
抽取的类
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
//使用配置文件
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 {
// 2.获取连接
Connection con = DriverManager.getConnection(URL, USERNAME, PASSWORD);
return con;
}
//关闭操作
public static void closeConnection(Connection con) throws SQLException{
if(con!=null){
con.close();
}
}
public static void closeStatement(Statement st) throws SQLException{
if(st!=null){
st.close();
}
}
public static void closeResultSet(ResultSet rs) throws SQLException{
if(rs!=null){
rs.close();
}
}
}
展示层代码:
业务成代码代码
DAO层代码
问题
Select * from user where usename=‘tom’ or 1=1 and password=‘12345’会出现什么
SQL注入
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL 关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。
String sql = select * from user where username =‘‘ and password =‘‘ ;
例如:
一、输入 username: 老李‘ or ‘1‘=‘1 password 随意
select * from user where username =‘老李‘ or ‘1‘=‘1‘ and password =‘‘;
* and 优先级 执行 高于 or
解决SQL注入:使用PreparedStatement 取代 Statement
PreparedStatement 解决SQL注入原理,运行在SQL中参数以?占位符的方式表示
select * from user where username = ? and password = ? ;
将带有?的SQL 发送给数据库完成编译 (不能执行的SQL 带有?的SQL 进行编译 叫做预编译),在SQL编译后发现缺少两个参数
PreparedStatement 可以将? 代替参数 发送给数据库服务器,因为SQL已经编译过,参数中特殊字符不会当做特殊字符编译,无法达到SQL注入的目的
// 使用PreparedStatement来完成操作,它可以解决sql注入.
public User findUser(User user) throws SQLException {
// 1.sql语句
String sql = "select * from user where username=? and password=?";
// 2.执行sql
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
con = JdbcUtils.getConnection();
pst = con.prepareStatement(sql);
pst.setString(1, user.getUsername());
pst.setString(2, user.getPassword());
rs = pst.executeQuery();// 无参数
if (rs.next()) { // 如果可以next,代表查找到了这个用户的信息,就将结果集中的信息封装到User对象中.
User u = new User();
u.setId(rs.getInt("id"));
u.setUsername(rs.getString("username"));
u.setPassword(rs.getString("password"));
u.setEmail(rs.getString("email"));
return u;
}
}finally {
try {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(pst);
JdbcUtils.closeConnection(con);
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
总结
sql注入
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL 关键字,
达到改变SQL运行结果的目的,也可以完成恶意攻击。
示例:
在输入用户名时 tom‘ or ‘1‘=‘1
这时就不会验证密码了。
解决方案:
PreparedStatement(重点)
它是一个预处理的Statement,它是java.sql.Statement接口的一个子接口。
总结PreparedStatement使用:
1.在sql语句中,使用"?"占位
String sql="select * from user where username=? and password=?";
2.得到PreparedStatement对象
PreparedStatement pst=con.prepareStatement(String sql);
3.对占位符赋值
pst.setXxx(int index,Xxx obj);
例如:
setInt()
setString();
参数index,代表的是"?"的序号.注意:从1开始。
4.执行sql
DML: pst.executeUpdate();
DQL: pst.executeQuery();
注意:这两方法无参数
关于PreparedStatement优点:
1.解决sql注入(具有预处理功能)
2.不需要在拼sql语句。
jdbc处理大数据
mysql中有大数据
blob 大二进制TINYBLOB(255)、BLOB(64kb)、MEDIUMBLOB(16m)和LONGBLOB(4g)
text(clob) 大文本TINYTEXT(255)、TEXT(64kb)、MEDIUMTEXT(16m)和LONGTEXT(4g)
对于大数据操作,我们一般只有两种 insert select
演示1:
大二进制操作
create table myblob(
id int primary key auto_increment,
content longblob
)
max_allowed_packet
注意:如果文件比较大,那么需要在my.ini文件中配置
max_allowed_packet=64M
总结:
存
pst.setBinaryStream(1, fis, (int) (file.length()));
取
InputStream is = rs.getBinaryStream("content");
案例大文本
// 添加
@Test
public void save() throws SQLException, IOException {
String sql = "insert into myblob values(null,?)";
// 1.获取Connection
Connection con = JdbcUtils.getConnection();
// 2.获取PreparedStatement
PreparedStatement pst = con.prepareStatement(sql);
// 3.插入值
File file = new File("D:\\java1110\\day17-jdbc\\视频\\3.jdbc快速入门.avi");
FileInputStream fis = new FileInputStream(file);
pst.setBinaryStream(1, fis, (int) (file.length()));
int row = pst.executeUpdate();
if (row != 0) {
System.out.println("插入成功");
}
// 4.释放资源
fis.close();
pst.close();
con.close();
}
// 获取
@Test
public void get() throws SQLException, IOException {
String sql = "select * from myblob where id=?";
// 1.获取Connection
Connection con = JdbcUtils.getConnection();
// 2.获取PreparedStatement
PreparedStatement pst = con.prepareStatement(sql);
pst.setInt(1, 1);
// 3.得到结果集
ResultSet rs = pst.executeQuery();
// 4.遍历结果集
if (rs.next()) {
// System.out.println(rs.getInt("id"));
InputStream is = rs.getBinaryStream("content");// 得到的这个输入流它的源可以理解成就是数据库中的大二进制信息
FileOutputStream fos = new FileOutputStream("d:/a.avi");
int len = -1;
byte[] b = new byte[1024 * 100];
while ((len = is.read(b)) != -1) {
fos.write(b, 0, len);
fos.flush();
}
fos.close();
is.close();
}
// 5.关闭
rs.close();
pst.close();
con.close();
}
处理文本
演示:存储大文本
create table mytext(
id int primary key auto_increment,
content longtext
)
存储
File file = new File("D:\\java1110\\workspace\\day17_3\\a.txt");
FileReader fr = new FileReader(file);
pst.setCharacterStream(1, fr, (int) (file.length()));
获取:
Reader r = rs.getCharacterStream("content");
public class MyTextTest {
// 存储
@Test
public void save() throws SQLException, FileNotFoundException {
String sql = "insert into mytext values(null,?)";
// 1.获取Connection
Connection con = JdbcUtils.getConnection();
// 2.获取PreparedStatement
PreparedStatement pst = con.prepareStatement(sql);
// 3.插入值
File file = new File("D:\\java1110\\workspace\\day17_3\\a.txt");
FileReader fr = new FileReader(file);
pst.setCharacterStream(1, fr, (int) (file.length()));
pst.executeUpdate();
// 4.释放资源
pst.close();
con.close();
}
// 获取
@Test
public void get() throws SQLException, IOException {
String sql = "select * from mytext where id=?";
// 1.获取Connection
Connection con = JdbcUtils.getConnection();
// 2.获取PreparedStatement
PreparedStatement pst = con.prepareStatement(sql);
pst.setInt(1, 1);
// 3.得到结果集
ResultSet rs = pst.executeQuery();
// 4.遍历结果集
if (rs.next()) {
Reader r = rs.getCharacterStream("content");
FileWriter fw = new FileWriter("d:/笔记.txt");
int len = -1;
char[] ch = new char[1024 * 100];
while ((len = r.read(ch)) != -1) {
fw.write(ch, 0, len);
fw.flush();
}
fw.close();
r.close();
}
pst.close();
con.close();
}
}
批量操作
一次可以执行多条sql语句.
在jdbc中可以执行sql语句的对象有Statement,PreparedStatement,它们都提供批处理.
1.Statement执行批处理
addBatch(String sql); 将sql语句添加到批处理
executeBatch(); 执行批处理
clearBatch();
2.PreparedStatement执行批处理
addBatch();
executeBatch();
clearBatch();
以上两个对象执行批处理区别?
1.Statement它更适合执行不同sql的批处理。它没有提供预处理功能,性能比较低。
2.PreparedStatement它适合执行相同sql的批处理,它提供了预处理功能,性能比较高。
注意;mysql默认情况下,批处理中的预处理功能没有开启,需要开启
1.在 url下添加参数
url=jdbc:mysql:///day17?useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true
2.注意驱动版本
Mysql驱动要使用mysql-connector-java-5.1.13以上
public class StatementBatchTest {
public static void main(String[] args) throws SQLException {
// 定义sql语句
String sql1 = "create table person(id int,name varchar(20))";
String sql2 = "insert into person values(1,‘tom‘)";
String sql3 = "insert into person values(2,‘fox‘)";
String sql4 = "insert into person values(3,‘tony‘)";
String sql5 = "update person set name=‘张三‘ where id=1";
String sql6 = "delete from person where id=3";
Connection con = JdbcUtils.getConnection();
// 得到一个Statement对象
Statement st = con.createStatement();
// 使用批处理执行sql
st.addBatch(sql1);
st.addBatch(sql2);
st.addBatch(sql3);
st.addBatch(sql4);
st.addBatch(sql5);
st.addBatch(sql6);
// 执行批处理
st.executeBatch();
st.close();
con.close();
}
}
public class PreparedStatementBatchTest {
public static void main(String[] args) throws SQLException {
// 向person表中插入1000条数据
String sql = "insert into person values(?,?)";
Connection con = JdbcUtils.getConnection();
PreparedStatement pst = con.prepareStatement(sql);
// 批处理
long l=System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
pst.setInt(1, i);
pst.setString(2, "name" + i);
pst.addBatch();
if(i%1000==0){
pst.executeBatch();
pst.clearBatch(); //清空缓存。
}
}
//执行批处理
pst.executeBatch();
pst.close();
con.close();
System.out.println(System.currentTimeMillis()-l);
}
}
IT十八掌 JDBC