操作
步骤记录:
- 加载驱动
- 链接数据库,获取数据库对象 DriverManager
- 获取执行SQL的对象 Statement
- 获得返回的结果集
- 释放连接
关键词:
Connection 数据库链接对象
Statement 执行SQL语句的对象
ResultSet 返回的结果集对象
加载驱动DriverManager
// 1.加载驱动
// com.mysql.jdbc.Driver,
// Class.forName("com.mysql.jdbc.Driver");
// 报错
// /*
// Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'.
// The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
// */
// 改成
Class.forName("com.mysql.cj.jdbc.Driver");
URL
//2.用户信息和url
//useUniconde = true&characterEncoding= utf8&useSSL=true
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUniconde=true&characterEncoding= utf8&useSSL=true";
String username = "root";
String password = "1234";
//3.连接成功,数据库对象 (对象不为空代表数据库对象已经取得) Connection表示数据库对象
Connection connection = DriverManager.getConnection(url, username, password);
//第二种合并写法
//链接数据库对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcstudy","root","1234");
//mysql -- 3306
//协议://jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3;
//oralce -- 1521
//jdbc:oracle:thin:@localhost:1521:sid
链接数据库
//3.连接成功,数据库对象 (对象不为空代表数据库对象已经取得) Connection表示数据库对象
Connection connection = DriverManager.getConnection(url, username, password);
//connection 代表数据库
connection.rollback();//撤消在当前事务中所做的所有更改,并释放此 Connection对象当前持有的任何数据库锁。
connection.commit();//
connection.setAutoCommit();
执行SQL的对象 Statement /prepareStatement
//4.执行SQL的对象
Statement statement = connection.createStatement();
// PreparedStatement preparedStatement = connection.prepareStatement();
//执行查询操作返回结果集
statement.executeQuery("");
//执行任何sql语句;
statement.execute("");
//执行更新,插入,删除操作,返回一个受影响的行数(int类型)
statement.executeUpdate("");
//执行多个语句
statement.executeBatch();
查询结果集 ResultSet
String sql = "SELECT * FROM users";
//返回的结果集合(封装了所有查询结果)
ResultSet resultSet = statement.executeQuery(sql);
//不清楚类型的
resultSet.getObject();
//有类型的
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
//移动到最前面
resultSet.beforeFirst();
//移动到最后面
resultSet.afterLast();
//移动到下一个
resultSet.next();
//移动到上一个
resultSet.previous();
//移动指定行
resultSet.absolute(2);
//遍历输出数据
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("birthday = " + resultSet.getObject("birthday"));
}
释放资源(链接十分消耗资源)
resultSet.close();
statement.close();
connection.close();
statement对象
jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。
配置文件:
配置日志文件名:db.properties
driver = com.mysql.cj.jdbc.Driver
url =jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf-8&useSSL=true
username=root
password=1234
工具类:
package com.kuang.lesson02;
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");
//加载驱动
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
public static void relese(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();
}
}
}
}
}
CRUD操作- create 添加/插入
package com.kuang.lesson02;
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();
//获得SQL 的执行对象
st = conn.createStatement();
String sql = "INSERT INTO users(id,NAME,PASSWORD,email,birthday)" +
"VALUES(4,'kuangshen','123456','247367430@qq.com','2020-01-01')";
int i = st.executeUpdate(sql);
if (i > 0) {
System.out.println("添加成功");
}
System.out.println(i);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.relese(conn, st, rs);
}
}
}
使用executeUpdate(String sql)方法进行数据操作
CRUD操作- delete 删除
package com.kuang.lesson02;
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();
//获得SQL 的执行对象
st = conn.createStatement();
String sql = "DELETE FROM users WHERE id = 4";
int i = st.executeUpdate(sql);
if (i > 0) {
System.out.println("删除成功");
}
System.out.println(i);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.relese(conn, st, rs);
}
}
}
CRUD操作- update 修改
package com.kuang.lesson02;
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();
//获得SQL 的执行对象
st = conn.createStatement();
String sql = "UPDATE users SET NAME = 'df',email = 'gg@qq.com' WHERE id = 1";
int i = st.executeUpdate(sql);
if (i > 0) {
System.out.println("修改呢");
}
System.out.println(i);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.relese(conn, st, rs);
}
}
}
CRUD操作- read 查询
使用executeQuery(String sql)方法进行数据操作
package com.kuang.lesson02;
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 re = null;
try {
//从工具类 JdbcUtils 获取 数据库链接对象 赋值给conn
conn = JdbcUtils.getConnection();
//获取执行sql语句的对象
st = conn.createStatement();
String sql = "SELECT * FROM users";
re = st.executeQuery(sql);
while (re.next()) {
//查询大小写随意
System.out.println("ID为" + re.getObject("id") + "名字为 " + re.getObject("name"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.relese(conn,st,re);
System.out.println("资源关闭成功");
}
}
}
SQL注入问题
sql存在都懂,会被攻击数据泄露 原因有sql会被拼接 or 等判断条件
package com.kuang.lesson02;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SQL注入 {
public static void main(String[] args) {
String username = "kuangshen";
String password = "123456";
login(username,password);
}
public static void login(String username ,String password){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
//因为条件 1=1 永远满足,所以查询的时候匹配任何数据都是可以显示的.
String sql = "SELECT * FROM users WHERE NAME = 'kuangshen'OR 1=1 AND PASSWORD = '123456' OR 1=1";
// String sql = "SELECT * FROM users WHERE NAME = 'kuangshen' AND PASSWORD = '123456' ";
rs = st.executeQuery(sql);
while (rs.next()){
System.out.println(rs.getString("name"));
System.out.println(rs.getString("password"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.relese(conn,st,rs);
}
}
}
PreparedStatement对象
可以防止SQL注入并且效率更高.
使用statement对象后进行sql注入导致数据泄露的例子:
package com.kuang.lesson03;
import com.kuang.lesson02.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SQL注入 {
public static void main(String[] args) {
String username = "kuangshen";
String password = "123456";
login(username,password);
}
public static void login(String username ,String password){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
//因为条件 1=1 永远满足,所以查询的时候匹配任何数据都是可以显示的.
String sql = "SELECT * FROM users WHERE NAME = 'kuangshen'OR 1=1 AND PASSWORD = '123456' OR 1=1";
// String sql = "SELECT * FROM users WHERE NAME = 'kuangshen' AND PASSWORD = '123456' ";
rs = st.executeQuery(sql);
while (rs.next()){
System.out.println(rs.getString("name"));
System.out.println(rs.getString("password"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.relese(conn,st,rs);
}
}
}
使用PreparedStatement的例子
package com.kuang.lesson03;
import com.kuang.lesson02.JdbcUtils;
import java.sql.*;
public class SQL注入 {
public static void main(String[] args) {
String username = "kuangshen";
String password = "123456";
// login(username,password);
//查询出空结果;
login("'' or 1=1", "123456");
}
public static void login(String username, String password) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
//因为条件 1=1 永远满足,所以查询的时候匹配任何数据都是可以显示的.
String sql = "SELECT * FROM users WHERE NAME = ? AND PASSWORD = ?";
//PreparedStatement 能防止sql注入的本事,就是吧传递进来的参数当做字符
//如果存在转义字符,就直接被忽略掉,从而防止sql注入
// String sql = "SELECT * FROM users WHERE NAME = 'kuangshen' AND PASSWORD = '123456' ";
st = conn.prepareStatement(sql);
st.setString(1, username);
st.setString(2, password);
rs = st.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("name"));
System.out.println(rs.getString("password"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils.relese(conn, st, rs);
}
}
}
增加数据
package com.kuang.lesson03;
import com.kuang.lesson02.JdbcUtils;
import java.sql.Connection;
import java.util.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = JdbcUtils.getConnection();
//使用 ? 占位符代替参数
String sql = "insert into users(id,NAME,PASSWORD,email,birthday) values (?,?,?,?,?)";
//区别为需要一个预编译的操作;
//手动给参数
st = conn.prepareStatement(sql);
st.setInt(1,1);
st.setString(2,"qiangjian");
st.setString(3,"1232456");
st.setString(4,"787com");
//setDate方法需要java.sql.Date 类型
st.setDate(5,new java.sql.Date(new Date().getTime()));
int i = st.executeUpdate();
if(i>0){
System.out.println("成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
JdbcUtils.relese(conn,st,null);
}
}
}
删除数据
package com.kuang.lesson03;
import com.kuang.lesson02.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
public class TestDelete {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = JdbcUtils.getConnection();
//使用 ? 占位符代替参数
String sql = "delete from users where id = ?";
//区别为需要一个预编译的操作;
st = conn.prepareStatement(sql);
//手动给参数
st.setInt(1, 1);
int i = st.executeUpdate();
if (i > 0) {
System.out.println("删除");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.relese(conn, st, null);
}
}
}
修改数据
package com.kuang.lesson03;
import com.kuang.lesson02.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestUpdate {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = JdbcUtils.getConnection();
//使用 ? 占位符代替参数
String sql = "update users set name = ? where id = ?;";
//区别为需要一个预编译的操作;
st = conn.prepareStatement(sql);
//手动给参数
st.setString(1,"erzi");
st.setInt(2, 2);
int i = st.executeUpdate();
if (i > 0) {
System.out.println("更新");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.relese(conn, st, null);
}
}
}
查询数据
package com.kuang.lesson03;
import com.kuang.lesson02.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
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";
//预编译
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.relese(conn,st,rs);
}
}
}
事务
//要么都成功,要么都失败
acid原则
- 原子性 : 要么全部完成,要么都不完成
- 一致性 : 总数不变
- 隔离性 : 多个进程互不干扰 - 持久性 : 一旦提交不可逆,就持久化到数据库了
隔离性的问题:
脏读 : 一个事务内读取了另一个没有提交的事务
不可重复读 : 在同一个事务内,重复读取表中的数据,表数据发生改变.
虚读(幻读) : 在一个事务内 ,读取到了别人插入的数据,导致前后结果不一致
开启事务
业务执行完毕,提交事务
在catch语句中显式调用
package com.kuang.lesson04;
import com.kuang.lesson02.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestTransaction1 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql1 = "update jdbcstudy.acconut set money = money-100 where name = 'A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
System.out.println("ok1");
//在使用这种语句表名 在运行中SQL语句的运行失败.
int x = 1/0;
//如果不使用事务的情况下在运行sql1语句后报错,从而导致整个事务没有运行完毕,造成数据丢失
String sql2 = "update jdbcstudy.acconut set money = money+100 where name = 'B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
// 业务完毕,提交事务
System.out.println("ok2");
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.relese(conn,st,rs);
}
}
}
原始数据
如果不使用事务,在运行到中间的时候就会因为int x = 1/0而报错,从而只运行了sql语句,没有运行sql2语句,在实际使用情况下.会造成数据的损失.
package com.kuang.lesson04;
import com.kuang.lesson02.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestTransaction1 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
//事务要关闭数据库的自动提交,也会同时开启事务
conn.setAutoCommit(false);
String sql1 = "update jdbcstudy.acconut set money = money-100 where name = 'A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
System.out.println("ok1");
//在使用这种语句表名 在运行中SQL语句的运行失败.
int x = 1/0;
//如果不使用事务的情况下在运行sql1语句后报错,从而导致整个事务没有运行完毕,造成数据丢失
String sql2 = "update jdbcstudy.acconut set money = money+100 where name = 'B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
// 业务完毕,提交事务
conn.commit();;
System.out.println("ok");
} catch (SQLException throwables) {
// 如果失败也会默认回滚
// try {
// conn.rollback();
// } catch (SQLException e) {
// e.printStackTrace();
// }
throwables.printStackTrace();
}finally {
JdbcUtils.relese(conn,st,rs);
}
}
}
报错提示
可以发现数据并没有变化
数据库链接池
数据库链接 — 执行完毕 — 释放
但是链接 到 释放这个过程十分消耗系统资源.
所以我们要是用 池化技术 : 将数据库链接预留下来放入池中,以备下次使用.
参数:
最小连接数 : 10(多与常用连接数相同)
最大连接数 : 15 通常为最高承载上限
等待超时 : 100ms
编写连接池 实现一个接口 DayeSource
开源数据库
DBCP
C3P0
Druid : 阿里巴巴
使用了这些数据库连接池之后,我们在项目开发就不需要编写链接数据库的代码!
DBCP
需要用到的jar包
commonts-dbcp-1.4 , commons-pool-1.6;