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')
-
创建一个普通项目
-
导入数据库驱动
-
编写测试代码
package jdbcdemo1;
import java.sql.*;
public class jdbcdemo2 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1. 加载驱动
Class.forName("com.mysql.jdbc.Driver");//固定写法
//2. 用户信息和url
//useUnicode=true&characterEncoding=utf8&&useSSL=true
String url ="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&&useSSL=false";
String username = "root";
String password = " ";
//3. 连接成功,返回数据库对象 connection代表数据库
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("birthday:"+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,name,password);
//connection代表数据库
//数据库设置自动提交
//事务提交
//事务回滚
connection.rollback();
connection.commit();
connection.setAutoCommit();
URL
String url ="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&&useSSL=false";
//mysql 默认3306
//协议://主机地址:端口号/数据库名?参数1&参数2&参数3
//Oracle 1521
//jdbc:oralce:thin:@localhost:1521:sid
statement 执行SQL的对象 pPrepareStatement 执行SQL的对象
String sql="SELECT * FROM users";//编写Sql
statement.executeQuery();
statement.execute();
statement.executeUpdate(); //更新,插入,删除,返回一个受影响的行数
ResultSet 查询的结果集,封装了所以的查询结果
-
获得指定的数据类型
ResultSet resultSet = statement.executeQuery(sql);//返回的结果集,结果集中封装了我们全部查询的结果 resultSet.getObject();//在不知道列类型下使用 resultSet.getString();//如果知道则指定使用 resultSet.getInt();
-
遍历,指针
resultSet.next(); //移动到下一个 resultSet.afterLast();//移动到最后 resultSet.beforeFirst();//移动到最前面 resultSet.previous();//移动到前一行 resultSet.absolute(row);//移动到指定行
释放内存
//6. 释放连接
resultSet.close();
statement.close();
connection.close();//耗资源
10.4 statement对象
-
Jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。
-
Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sq|语句, executeUpdate执行完后, 将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。
-
Statement.executeQuery方法用于向数据库发生查询语句,executeQuery方法返回代表查询结果的ResultSet对象。
//CRUD操作-create
//使用executeUpdate(String sql)方法完成数据添加操作,示例操作:
Statement statement = connection.createStatement();
String sql = "insert into user(...) values(...)";
int num = statement.executeUpdate(sql);
if(num>0){
System.out.println("插入成功");
}
//CRUD操作-delete
//使用executeUpdate(String sql)方法完成数据删除操作,示例操作:
Statement statement = connection.createStatement();
String sql = "delete from user where id =1";
int num = statement.executeUpdate(sql);
if(num>0){
System.out.println("删除成功");
}
//CURD操作-update
//使用executeUpdate(String sql)方法完成数据修改操作,示例操作:
Statement statement = connection.createStatement();
String sql = "update user set name ='' where name = ''";
int num = statement.executeUpdate(sql);
if(num>0){
System.out.println("修改成功");
}
//CURD操作-read
//使用executeUpdate(String sql)方法完成数据查询操作,示例操作:
Statement statement = connection.createStatement();
String sql = "select * from user where id =1";
ResultSet rs= statement.executeQuery(sql);
if(rs.next()){
System.out.println("");
}
10.4.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 e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } //2.获取连接 public static Connection getConnection() throws Exception{ return DriverManager.getConnection(url, username, password); } //3.释放资源 public static void release(Connection conn, Statement st, ResultSet rs) throws SQLException { if(rs!=null){ rs.close(); } if (st!=null){ st.close(); } if(conn!=null){ conn.close(); } } }
-
编写增删改的方法,exectueUpdate
package com.kuang.lesson02.utils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import static com.kuang.lesson02.utils.JdbcUtils.*; public class TestInnsert { public static void main(String[] args){ Connection conn =null; Statement st = null; ResultSet rs =null; try { conn = getConnection();//获取连接 st = conn.createStatement();//获取SQL执行对象 String sql = "INSERT INTO users(id,`NAME`,`PASSWORD`,`email`,`birthday`)" + "VALUES(5,'sanjin','123456','233223@qq.com','2020-01-01')"; int i = st.executeUpdate(sql); if(i>0){ System.out.println("插入成功"); } JdbcUtils.release(conn,st,rs); } catch (Exception e) { e.printStackTrace(); } } }
-
查询 executeQuery
package jdbcdemo1; import jdbcdemo1.utis.jdbcutis; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import static jdbcdemo1.utis.jdbcutis.*; public class TestInnsert { public static void main(String[] args) throws SQLException { Connection conn =null; Statement st = null; ResultSet rs =null; try { conn = getConnection();//获取连接 st = conn.createStatement();//获取SQL执行对象 String sql = "select * from users"; rs=st.executeQuery(sql);//查询完毕返回结果集 while (rs.next()){ System.out.println(rs.getString("NAME")); } JdbcUtils.release(conn,st,rs); } catch (Exception e) { e.printStackTrace(); }finally { JdbcUtils.release(conn,st,rs); } } }
10.4.2 SQL注入问题
sql存在漏洞,会被攻击导致数据泄露 SQL会被拼接
package jdbcdemo1;
import jdbcdemo1.utis.jdbcutis;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import static jdbcdemo1.utis.jdbcutis.*;
public class SQL注入 {
public static void main(String[] args) {
//SQL注入
login("' or '1=1","123456");
}
public static void login(String name,String password){
Connection conn =null;
Statement st = null;
ResultSet rs =null;
try {
conn = getConnection();//获取连接
st = conn.createStatement();//获取SQL执行对象
String sql = "select * from users where `NAME`='"+ name +"' AND `PASSWORD`='"+ password +"'" ;
rs=st.executeQuery(sql);//查询完毕返回结果集
while (rs.next()){
System.out.println(rs.getString("NAME"));
}
JdbcUtils.release(conn,st,rs);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
JdbcUtils.release(conn,st,rs);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
10.5 PreparedStatement对象
PreparedStatement 可以防止SQL注入 ,效率更高。
新增
package jdbcdemo1;
import jdbcdemo1.utis.jdbcutis;
import java.sql.*;
import java.util.Date;
import static jdbcdemo1.utis.jdbcutis.*;
public class preparestatement {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement st=null;
try {
conn=jdbcutis.getConnection();
String sql="INSERT INTO users(id,`NAME`,`PASSWORD`,`email`,`birthday`) values (?,?,?,?,?)";
st= conn.prepareStatement(sql);
st.setInt(1,7);
st.setString(2,"fulang");
st.setString(3,"123456");
st.setString(4,"fulang@sina.com");
st.setDate(5,new java.sql.Date(new Date().getTime()));
//zhixing
int i = st.executeUpdate();
if(i>0)
{
System.out.println("Insert Success");
}
jdbcutis.release(conn,st,null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package jdbcdemo1;
import jdbcdemo1.utis.jdbcutis;
import java.sql.*;
import java.util.Date;
import static jdbcdemo1.utis.jdbcutis.*;
public class preparestatement {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement st=null;
ResultSet rs=null;
try {
conn=jdbcutis.getConnection();
String sql="select * from users where id=?";
st= conn.prepareStatement(sql);
st.setInt(1,6);
//zhixing
rs= st.executeQuery();
while (rs.next()){
System.out.println("id:"+rs.getObject("id"));
System.out.println("name:"+rs.getObject("NAME"));
System.out.println("password:"+rs.getObject("PASSWORD"));
System.out.println("email:"+rs.getObject("email"));
System.out.println("birthday:"+rs.getObject("birthday"));
}
jdbcutis.release(conn,st,null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
防止SQL注入本质,传递字符 带有“ ”,转义字符会被转义
package jdbcdemo1;
import jdbcdemo1.utis.jdbcutis;
import java.sql.*;
import static jdbcdemo1.utis.jdbcutis.*;
public class zhuru {
public static void main(String[] args) {
login("fulang","123456");
}
public static void login(String username,String password) {
Connection conn=null;
PreparedStatement st=null;
ResultSet rs=null;
try {
conn = jdbcutis.getConnection();//获取连接
String sql="select * from users where `NAME`=? AND `PASSWORD`=?" ;
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"));
}
jdbcutis.release(conn,st,rs);
} catch (Exception e) {
e.printStackTrace();
}
}
}
10.6 使用IDEA连接数据库
- 连接成功后,可以选择数据库
- 双击数据库
- 更新数据
- 编写sql代码的地方
10.7 JDBC事务
要么都成功,要么都失败
ACID原则
-
原子性:要么全部完成,要么都不完成
-
一致性:结果总数不变
-
隔离性:多个进程互不干扰
-
持久性:一旦提交不可逆,持久化到数据库了
-
隔离性的问题:
-
脏读: 一个事务读取了另一个没有提交的事务
-
不可重复读:在同一个事务内,重复读取表中的数据,表发生了改变
-
虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来的结果不一致
代码实现
- 开启事务
conn.setAutoCommit(false);
- 一组业务执行完毕,提交事务
- 可以在catch语句中显示的定义回滚,但是默认失败会回滚
package com.kuang.lesson04;
import com.kuang.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Action {
public static void main(String[] args) {
Connection conn =null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
//关闭数据库的自动提交功能, 开启事务
conn.setAutoCommit(false);
//自动开启事务
String sql = "update account set money = money-500 where id = 1";
ps =conn.prepareStatement(sql);
ps.executeUpdate();
String sql2 = "update account set money = money-500 where id = 2";
ps=conn.prepareStatement(sql2);
ps.executeUpdate();
//业务完毕,提交事务
conn.commit();
System.out.println("操作成功");
} catch (Exception e) {
try {
//如果失败,则默认回滚
conn.rollback();//如果失败,回滚
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
try {
JdbcUtils.release(conn,ps,rs);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
10.8 数据库连接池
数据库连接–执行完毕–释放
连接–释放 十分浪费资源
池化技术: 准备一些预先的资源,过来就连接预先准备好的
常用连接数 100
最少连接数:100
最大连接数 : 120 业务最高承载上限
排队等待,
等待超时:100ms
编写连接池,只需要实现一个接口 DateSource
开源数据源实现(拿来即用)
DBCP
C3P0
Druid: 阿里巴巴
使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了
DBCP(需要用到的jar包)
-- 配置文件
#DBCP数据源中定义好的名字
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSl=true
username=root
password=123456
#初始化连接数
initialSize=10
#最大连接数
maxActive=50
#最大空闲连接
maxIdle=20
#最小空闲连接
minIdle=5
#最长等待超时时间 以毫秒为单位
maxWait=60000
connectionProperties=useUnicode=true;characterEncoding=UTF8
defaultAutoCommit = true
defaultReadOnly = defaultTransactionIsolation=READ_UNCOMMITTED
//工具类
package jdbcdemo1.utis;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class jdbcutisDbcp {
private static DataSource datasource=null;
static {
try{
InputStream in = jdbcutis.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties properties = new Properties();
properties.load(in);
//创建数据源 工厂模式
datasource=BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//2.获取连接
public static Connection getConnection() throws Exception{
return datasource.getConnection();//数据源中获取连接
}
//3.释放资源
public static void release(Connection conn, Statement st, ResultSet rs) throws SQLException {
if(rs!=null){
rs.close();
}
if (st!=null){
st.close();
}
if(conn!=null){
conn.close();
}
}
}
//TestDBCP
package jdbcdemo1;
import jdbcdemo1.utis.jdbcutisDbcp;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestDBCP {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement st=null;
ResultSet rs=null;
try {
conn=jdbcutisDbcp.getConnection();
String sql="select * from users where id=?";
st= conn.prepareStatement(sql);
st.setInt(1,1);
//zhixing
rs= st.executeQuery();
while (rs.next()){
System.out.println("id:"+rs.getObject("id"));
System.out.println("name:"+rs.getObject("NAME"));
System.out.println("password:"+rs.getObject("PASSWORD"));
System.out.println("email:"+rs.getObject("email"));
System.out.println("birthday:"+rs.getObject("birthday"));
}
jdbcutisDbcp.release(conn,st,rs);
} catch (Exception e) {
e.printStackTrace();
}
}
}
结论
无论使用什么数据源,本质(使用DataSource接口)是不变的,DateSource接口不会变,方法就不会变