10 JDBC(重点)
10.1 数据库驱动
程序需要通过数据库驱动和数据库打交道
10.2 JDBC
- SUN公司为了简化开发人员对数据库的统一操作,提供了一个Java操作数据库的规范,俗称JDBC。这些规范的实现由具体的厂商去做。
- 对于开发人员来说,只需要掌握JDBC接口的操作即可。
- java.sql
- javax.sql
- 还需要导入一个数据库驱动包
10.3 第一个JDBC程序
-
创建测试数据库
CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci; USE jdbcStudy; CREATE TABLE `users`( id INT PRIMARY KEY, NAME VARCHAR(40), PASSWORD VARCHAR(40), email VARCHAR(60), birthday DATE ); INSERT INTO `users`(id,NAME,PASSWORD,email,birthday) VALUES(1,'zhansan','123456','zs@sina.com','1980-12-04'), (2,'lisi','123456','lisi@sina.com','1981-12-04'), (3,'wangwu','123456','wangwu@sina.com','1979-12-04')
-
创建一个普通项目
-
导入数据库驱动,然后右键lib->add as library……
-
编写测试代码
package com.kuang.lesson01; import java.sql.*; //我的第一个jdbc程序 public class JdbcFirstDemo { public static void main(String[] args) throws ClassNotFoundException, SQLException { //1.加载驱动 Class.forName("com.mysql.jdbc.Driver");//固定写法 //2.连接用户信息和url //useUnicode=true&characterEncoding=utf8&useSSL=true 1.使用Unicode编码 2.使用utf-8 3.安全连接true:打开 false:关闭 String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false"; String username="root"; String password="123456"; //3.连接成功,数据库对象 Connerction 代表数据库 Connection connection=DriverManager.getConnection(url,username,password); //4.执行sql的对象 statement 执行sql的对象 Statement statement=connection.createStatement(); //5.执行sql的对象,去执行sql,可能存在结果,查看返回结果 String sql="SELECT * FROM users"; ResultSet resultSet= statement.executeQuery(sql);//返回的结果集,结果集中封装了我们全部的查询出来的结果 while (resultSet.next()){ System.out.println("id="+resultSet.getObject("id")); System.out.println("name="+resultSet.getObject("NAME")); System.out.println("password="+resultSet.getObject("PASSWORD")); System.out.println("email="+resultSet.getObject("email")); System.out.println("birth="+resultSet.getObject("birthday")); System.out.println("======"); } //6.释放连接 resultSet.close(); statement.close(); connection.close(); } }
步骤总结:
- 加载驱动
- 连接数据库DriverManager
- 获得执行sql的对象Statement
- 获得返回的结果集
- 释放连接
DriverManager
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");//固定写法
Connection connection=DriverManager.getConnection(url,username,password);
//connection 代表数据库
//数据库设置自动提交
connection.setAutoCommit();
//事务提交
connection.commit();
//事务回滚
connection.rollback();
URL
String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
//mysql--3306
//jdbc:mysql://localhost:3306/数据库名?参数1&参数2
//oralce--1521
//jdbc:oracle:thin:@localhost:1521:sid
//协议://主机地址:端口号/数据库名?参数1&参数2
Statement 执行sql的对象 PrepareStatement 执行sql的对象
-
执行所有的sql
statement.execute();
-
执行查询
statement.executeQuery();
-
执行更新、插入、删除,返回受影响的行数
statement.executeUpdate();
ResultSet 查询的结果集:封装了所有的查询结果
-
可以获得指定的数据类型
-
获得——使用遍历
resultSet.beforeFirst();//移动到最前面 resultSet.afterLast();//移动到最后面 resultSet.next();//移动到下一个数据 resultSet.previous();//移动到前一行 resultSet.absolute();//移动到指定行
释放资源
//6.释放连接
resultSet.close();
statement.close();
connection.close();
10.4 statement对象
jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。
statement对象的executeUpdate方法,用于向数据库发送增、删、改的sql语句,executeUpdate执行完后,将会返回一个整数(即增删改查语句导致了数据库几行数据发生了变化)。
Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。
Statement st=conn.createStatement();
String sql="......";
int num=st.executeUpdate(sql);
if(num>0){
Sysout.out.println("......");
}
代码实现
1.提取工具类
package com.kuang.lesson02.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
private static String driver=null;
private static String url=null;
private static String username=null;
private static String password=null;
static {
try{
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(in);
driver=properties.getProperty("driver");
url=properties.getProperty("url");
username=properties.getProperty("username");
password=properties.getProperty("password");
//1.驱动只用加载一次
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs) {
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
2.编写增删改的方法,executeUpdate
package com.kuang.lesson02;
import com.kuang.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestInsert {
public static void main(String[] args) {
Connection conn=null;
Statement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st=conn.createStatement();//获得sql的执行对象
String sql="INSERT INTO users(id,`NAME`,`PASSWORD`,`email`,`birthday`)" +
"VALUES(4,'kuangshen','123456','1234565@qq.com','2020-01-01')";
int i = st.executeUpdate(sql);
if(i>0){
System.out.println("插入成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils.release(conn,st,rs);
}
}
}
package com.kuang.lesson02;
import com.kuang.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestDelete {
public static void main(String[] args) {
Connection conn=null;
Statement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st=conn.createStatement();//获得sql的执行对象
String sql="DELETE FROM users WHERE id=4";
int i = st.executeUpdate(sql);
if(i>0){
System.out.println("删除成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils.release(conn,st,rs);
}
}
}
package com.kuang.lesson02.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestUpdate {
public static void main(String[] args) {
Connection conn=null;
Statement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st=conn.createStatement();//获得sql的执行对象
String sql="UPDATE users SET `NAME`='kuangshen',`email`='444444@qq.com' WHERE id=1";
int i = st.executeUpdate(sql);
if(i>0){
System.out.println("更新成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils.release(conn,st,rs);
}
}
}
3.查询executeQuery
package com.kuang.lesson02;
import com.kuang.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestSelect {
public static void main(String[] args) {
Connection conn=null;
Statement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();
st=conn.createStatement();
String sql="select * from users where id=1";
rs=st.executeQuery(sql);//查询完毕会返回一个结果集
while (rs.next()){
System.out.println(rs.getString("NAME"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
sql注入的问题
sql存在漏洞,导致数据泄露。
sql会被拼接——or
package com.kuang.lesson02;
import com.kuang.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SQLInjection {
public static void main(String[] args) {
//login("kuangshen","123456"); //正常登录
login("' or'1=1","123456");
}
//登录
public static void login(String username,String password){
Connection conn=null;
Statement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();
st=conn.createStatement();
String sql="select * from users where `NAME`='"+username+"'and `PASSWORD` ='"+password+"'";
rs=st.executeQuery(sql);//查询完毕会返回一个结果集
while (rs.next()){
System.out.print(rs.getString("NAME"));
System.out.print(" ");
System.out.println(rs.getString("PASSWORD"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
10.5 PreparedStatement对象
PreparedStatement可以防止sql注入,并且效率更高。
PrepareStatement防止SQL注入的本质,把传递进来的参数当作字符——都用引号包起来了——假设其中存在转义字符,就直接忽略
- 插入
package com.kuang.lesson03;
import com.kuang.lesson02.utils.JdbcUtils;
import java.sql.*;
import java.util.Date;
public class TestInset {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();
//区别
//使用? 占位符代替参数
String sql="INSERT INTO users(id,`NAME`,`PASSWORD`,`email`,`birthday`)values(?,?,?,?,?)";
st=conn.prepareStatement(sql);//需要预编译sql,先写sql,然后不执行
//手动给参数赋值
st.setInt(1,4);
st.setString(2,"qinjiang");
st.setString(3,"123456");
st.setString(4,"4444444@qq.com");
//注意点:sql.date util.date
st.setDate(5,new java.sql.Date(new Date().getTime()));//时间戳转化格式
//执行
int i = st.executeUpdate();
if(i>0){
System.out.println("插入成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
- 更新
package com.kuang.lesson03;
import com.kuang.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestUpdate {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();
//区别
//使用? 占位符代替参数
String sql="update users set `NAME`=? where id=?";
st=conn.prepareStatement(sql);//需要预编译sql,先写sql,然后不执行
//手动给参数赋值
st.setString(1,"狂神");
st.setInt(2,1);
//执行
int i = st.executeUpdate();
if(i>0){
System.out.println("更新成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
- 删除
package com.kuang.lesson03;
import com.kuang.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
public class TestDelete {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();
//区别
//使用? 占位符代替参数
String sql="delete from users where id=?";
st=conn.prepareStatement(sql);//需要预编译sql,先写sql,然后不执行
//手动给参数赋值
st.setInt(1,4);
//执行
int i = st.executeUpdate();
if(i>0){
System.out.println("删除成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
- 查询
package com.kuang.lesson03;
import com.kuang.lesson02.utils.JdbcUtils;
import java.sql.*;
public class TestSelect {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();
String sql="Select * from users where id =?";
st=conn.prepareStatement(sql);
st.setInt(1,1);
rs=st.executeQuery();
while (rs.next()){
System.out.println(rs.getString("NAME"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
时间戳转化:
//注意点:sql.date util.date
st.setDate(5,new java.sql.Date(new Date().getTime()));//时间戳转化格式
10.7 使用idea连接数据库
10.8 事务
要么都成功,要么都失败
- 原子性:要么全部完成,要么都不完成
- 一致性:总数不变
- 隔离性:多个进程互不干扰
- 持久性:一旦提交不可逆,持久化到数据库了
隔离性的问题
- 脏读:一个事务读取了另一个没有提交的事务
- 不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变
- 虚读/幻读:在一个事务内,读取到了别人插入的数据,导致前后读出来的结果不一样
代码实现
- 开启事务
- 一组业务执行完毕,提交事务
- 可以在catch语句中显示的定义回滚语句,但默认就会回滚
10.9 数据库连接池
数据库连接……执行完毕…… 这一过程十分浪费系统资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
- 最小连接数
- 最大连接数 业务最高承载上限
- 等待超时
编写连接池,实现一个接口:DataSource
开源数据源实现
-
DBCP
-
C3P0
-
Druid:阿里巴巴的
使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了
DBCP
需要用到的jar包:
-
commons-dbcp-1.4
-
commons-pool-1.6
-
DBCP2.0还需要commons logging这个jar包————待验证
运行失败