JDBC(重点)
数据库驱动
我们的程序会通过数据库驱动和数据库打交道
JDBC
Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。
对于开发人员只需要使用jdbc即可
第一个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','zhangsan','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')
然后创建一个名为JDBC的普通java项目
创建一个lib文件夹
导入MySQL Connector/J » 5.1.47的jar包
https://mvnrepository.com/artifact/mysql/mysql-connector-java/5.1.47
然后右键add as library 点击ok
创建一个class
package com.jdbctest.lesson01;
import java.sql.*;
public class HelloJdbc {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 加载驱动
// 固定写法
Class.forName("com.mysql.jdbc.Driver");
//用户信息和url
String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
String username="root";
String password="123456";
// 连接成功数据库对象
Connection connection = DriverManager.getConnection(url, username, password);
// 执行sql的对象,去执行sql
Statement statement = connection.createStatement();
String sql = "SELECT * FROM users";
// 结果集封装了我们全部的查询结果
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
System.out.println(resultSet.getObject("id"));
System.out.println(resultSet.getObject("NAME"));
System.out.println(resultSet.getObject("PASSWORD"));
System.out.println(resultSet.getObject("email"));
System.out.println(resultSet.getObject("birthday"));
}
// 释放连接
resultSet.close();
statement.close();
connection.close();
}
}
DriverManager
// 加载驱动
Class.forName("com.mysql.jdbc.Driver"); //固定写法
URL
//jdbc:mysql://主机名:端口号/数据库名?参数1&参数2
String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8";
statement 执行sql的对象
statement.executeQuery(); // 查询
statement.execute(); //执行任何sql
statement.executeUpdate(); //更新 插入 删除都用这个,返回一个受影响行数
ResultSet 查询结果集,封装了所有的查询结果
//和数据库中的类型一一匹配
resultSet.getObject(); // 不知道列类型的情况下使用
// 如果知道列的类型就使用指定的类型
resultSet.getString();
resultSet.getDate();
resultSet.getInt();
resultSet.getDouble();
遍历指针
resultSet.beforeFirst(); //移动到最前面
resultSet.afterLast(); //移动到最后面
resultSet.next() // 移动到下一个
resultSet.previous(); // 移动到上一个
resultSet.absolute(); // 移动到指定行
释放资源
// 释放连接 十分耗费资源
resultSet.close();
statement.close();
connection.close();
statement对象
JDBC中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。
Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sql语句,executeUpdate执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。
Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。
CRUD操作-create
使用executeUpdate(String sql)方法完成数据添加操作,示例操作:
Statement st = connection.createStatement();
String sql = "insert into user(...)value(...)";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!");
}
CRUD操作-delete
使用executeUpdate(String sql)方法完成数据删除操作,示例操作:
Statement st = connection.createStatement();
String sql = "delete from user where id=1";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("删除成功!");
}
CRUD操作-update
使用executeUpdate(String sql)方法完成数据修改操作,示例操作:
Statement st = connection.createStatement();
String sql = "update user set name =''where name =''";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("修改成功!");
}
CRUD操作-read
使用executeUpdate(String sql)方法完成数据查询操作,示例操作:
Statement st = connection.createStatement();
String sql = "select * from user where id=1";
ResultSet rs = st.executeQuery(sql);
while ( rs.next()){
//根据获取列的数据类型,分别调用rs的相应方法映射到java对象中
}
首先在src目录下创建一个db.properties
driver = com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&useSSL=true
username =root
password=123456
在创建一个工具类
package com.jdbctest.lesson02.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
private static String driver;
private static String url;
private static String username;
private static String password;
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 (Exception 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) throws SQLException {
if(rs!=null) {
rs.close();
}
if(st!=null){
st.close();
}
if (conn!=null){
conn.close();
}
}
}
再创建测试类
package com.jdbctest.lesson02;
import com.jdbctest.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Test {
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','xiaobai','123456','789456158@qq.com','2021-10-22')";
// int i=st.executeUpdate(sql);
// if(i>0){
// System.out.println("插入成功");
// }
//删除数据
// String sql="DELETE FROM users WHERE id=4";
// int i=st.executeUpdate(sql);
// if(i>0){
// System.out.println("删除成功");
// }
//更新数据
// String sql="UPDATE users SET `NAME`='xiaohei' WHERE id=1";
// int i=st.executeUpdate(sql);
// if(i>0){
// System.out.println("修改成功");
// }
//查询数据
String sql="select * from users";
rs=st.executeQuery(sql);
while (rs.next()){
System.out.println(rs.getObject("id"));
System.out.println(rs.getObject("NAME"));
System.out.println(rs.getObject("PASSWORD"));
System.out.println(rs.getObject("email"));
System.out.println(rs.getObject("birthday"));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
MySQL注入问题
1.1、SQL注入的本质
web程序代码中对于用户提交的参数未做过滤就直接放到SQL语句中执行,导致参数中的特殊字符打破了SQL语句原有逻辑,黑客可以利用该漏洞执行任意SQL语句.
1.2、什么是SQL注入
1.是一种将SQL语句插入或添加到应用(用户)的输入参数中的攻击
2.这些参数传递给后台的SQL数据库服务器加以解析并执行.
1.3、注入原理
SQL注入的本质是恶意攻击者将SQL代码插入或添加到程序的参数中,而程序并没有对传入的参数进行正确处理,导致参数中的数据会被当做代码来执行,并最终将执行结果返回给攻击者
1.3、危害
1、数据库信息泄露:数据库中存放的用户的隐私信息的泄露
2、网页串改:通过数据库对特定的网页进行篡改(网页内容存储在数据库,通过修改内容达到网页篡改)
3、网站挂马,传播恶意软件:通过修改数据库一些字段的值,嵌入网马链接,进行网马攻击
4、数据库被恶意操作:数据库被攻击,数据库的系统管理员账户被篡改
5、获取webshell:利用数据库存在的权限分配缺陷获取webshell甚至是系统权限
package com.jdbctest.lesson02;
import com.jdbctest.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestInject {
public static void main(String[] args) throws SQLException {
login("'or' 1=1","'or' 1=1");
}
public static void login(String username,String password) throws SQLException {
Connection conn=null;
Statement st =null;
ResultSet rs=null;
try{
conn= JdbcUtils.getConnection();//获取连接
st=conn.createStatement();//获取sql的执行对象
//SELECT * FROM `users` WHERE `NAME`= "+"'"+username+"'"+" AND `PASSWORD`="+"'"+password+"'
String sql="SELECT * FROM `users` WHERE `NAME`= "+"'"+username+"'"+" AND `PASSWORD`="+"'"+password+"'";
rs=st.executeQuery(sql);
while (rs.next()){
System.out.println(rs.getObject("NAME"));
System.out.println(rs.getObject("PASSWORD"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
PreparedStatement对象
PreparedStatement可以防止SQL注入,更安全
package com.jdbctest.lesson03;
import com.jdbctest.lesson02.utils.JdbcUtils;
import java.sql.*;
import java.util.Date;
public class TestPS {
public static void main(String[] args) throws SQLException {
Connection conn=null;
PreparedStatement st =null;
ResultSet rs=null;
try{
conn= JdbcUtils.getConnection();//获取连接
// //插入数据
// //使用?占位符代替参数
// String sql="INSERT INTO `users`(`id`,`NAME`,`PASSWORD`,`email`,`birthday`) VALUES (?,?,?,?,?)";
// //预编译mysql,先写sql,然后不执行
// st=conn.prepareStatement(sql);
// //手动给参数复制
// st.setInt(1,4);
// st.setString(2,"xiaohei");
// st.setString(3,"123456");
// st.setString(4,"789524631@qq.com");
// // 注意点:sql.date 数据库
// // util.Date new Date().getTime() 获取时间戳
// st.setDate(5,new java.sql.Date(new Date().getTime()));
// int i=st.executeUpdate();
// if(i>0){
// System.out.println("插入成功");
// }
//删除数据
// String sql="DELETE FROM users WHERE id=?";
// st = conn.prepareStatement(sql);
// st.setInt(1,4);
// int i=st.executeUpdate();
// if(i>0){
// System.out.println("删除成功");
// }
//更新数据
// String sql="UPDATE users SET `NAME`=? WHERE id=?";
// st=conn.prepareStatement(sql);
// st.setString(1,"xiaohei");
// st.setInt(2,1);
//
// int i=st.executeUpdate();
// if(i>0){
// System.out.println("修改成功");
// }
//查询数据
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.getObject("id"));
System.out.println(rs.getObject("NAME"));
System.out.println(rs.getObject("PASSWORD"));
System.out.println(rs.getObject("email"));
System.out.println(rs.getObject("birthday"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
防止sql注入
package com.jdbctest.lesson03;
import com.jdbctest.lesson02.utils.JdbcUtils;
import java.sql.*;
public class TestPSInject {
public static void main(String[] args) throws SQLException {
// login("lisi","123456");
login("'' or 1=1", "'' or 1=1");
}
public static void login(String username, String password) throws SQLException {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();//获取连接
//st = conn.createStatement();//获取sql的执行对象
//SELECT * FROM `users` WHERE `NAME`= "+"'"+username+"'"+" AND `PASSWORD`="+"'"+password+"'
String sql = "SELECT * FROM `users` WHERE `NAME`=? and `PASSWORD`=?";
// PreparedStatement防l:SQL注入的本质,把传递进来的参数当做字符
// 假设其中存在转义字符,就直接忽略,
st=conn.prepareStatement(sql);
st.setString(1,username);
st.setString(2,password);
rs = st.executeQuery();
while (rs.next()) {
System.out.println(rs.getObject("NAME"));
System.out.println(rs.getObject("PASSWORD"));
System.out.println("=======================");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
创建数据库
创建查询
更新数据库
切换数据库
事务
ACID原则
原子性: 要么全都成功,要么全都失败
一致性: 结果不变,总数一直
隔离性: 多个进程互不干扰
持久性: 一旦提交不可逆,持久化到数据库了
隔离性的问题:
脏读 :一个事务读取了另一个没有提交的事务
不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变
虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来的结果不一致
代码实现
package com.jdbctest.lesson04;
import com.jdbctest.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestTransation {
public static void main(String[] args) throws SQLException {
Connection conn=null;
PreparedStatement st=null;
ResultSet rs=null;
try{
conn= JdbcUtils.getConnection();
//关闭数据库的自动提交,会自动开启事务
conn.setAutoCommit(false);
String sql1="UPDATE account set money=money-100 where name ='A'";
st=conn.prepareStatement(sql1);
st.executeUpdate();
String sql2="UPDATE account set money=money+100 where name ='B'";
st=conn.prepareStatement(sql2);
st.executeUpdate();
//业务完毕提交事务
conn.commit();
System.out.println("转账成功");
} catch (Exception e) {
//回滚
conn.rollback();
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
数据库连接池
数据库连接–执行完毕–释放 十分浪费系统资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
最小连接数: 10
最大连接数:20
等待超时:100ms
编写连接池,实现一个接口 DataSource
开源数据源实现
DBCP
C3P0
Druid:阿里巴巴
使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了!
DBCP
需要用到的jar包都可以在阿里云仓库找到 commons-dbcp-1.4.jar commons-pool-1.6.jar
DBCP连接池配置
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:【属性名=property;】
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF8
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
从数据源中获取连接
package com.jdbctest.lesson05.utils;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class jdbcutils_dbcp {
private static DataSource source=null;
static {
try {
InputStream in =jdbcutils_dbcp.class.getClassLoader().getResourceAsStream("dbconfig.properties");
Properties properties = new Properties();
properties.load(in);
//创建数据源 工厂模式--> 创建对象
source = BasicDataSourceFactory.createDataSource(properties);
}catch (Exception e){
e.printStackTrace();
}
}
// 获取连接
public static Connection getConnection() throws SQLException {
//从数据源中获取连接
return source.getConnection();
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs) throws SQLException {
if(conn!=null) conn.close();
if(st!=null) st.close();
if(rs!=null) rs.close();
}
}
测试
package com.jdbctest.lesson05;
import com.jdbctest.lesson05.utils.jdbcutils_dbcp;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class test {
public static void main(String[] args) throws SQLException {
Connection connection=null;
PreparedStatement statement=null;
ResultSet rs=null;
try {
//获取连接
connection = jdbcutils_dbcp.getConnection();
//sql
String sql="SELECT * from users WHERE id=?";
//预编译sql
statement= connection.prepareStatement(sql);
//设置参数
statement.setObject(1,1);
//执行sql
rs=statement.executeQuery();
//遍历结果
while (rs.next()){
System.out.println(rs.getObject("NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
jdbcutils_dbcp.release(connection,statement,rs);
}
}
}
C3P0
导入jar包 c3p0-0.9.1.2.jar mchange-commons-java-0.2.9.jar
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!--
c3p0的缺省(默认)配置
如果在代码中"ComboPooledDataSource ds=new ComboPooledDataSource();"这样写就表示使用的是c3p0的缺省(默认)-->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&uesSSL=true&serverTimezone=UTC</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="acquiredIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<!--
c3p0的命名配置
如果在代码中"ComboPooledDataSource ds=new ComboPooledDataSource("MySQL");"这样写就表示使用的是mysql的缺省(默认)-->
<named-config name="MySQL">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&uesSSL=true&serverTimezone=UTC</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="acquiredIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</named-config>
</c3p0-config>
配置
package com.jdbctest.lesson05.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class jdbcutils_c3p0 {
private static ComboPooledDataSource source=null;
static {
try {
source= new ComboPooledDataSource("MySQL");
//创建数据源 工厂模式--> 创建对象
}catch (Exception e){
e.printStackTrace();
}
}
// 获取连接
public static Connection getConnection() throws SQLException {
//从数据源中获取连接
return source.getConnection();
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs) throws SQLException {
if(conn!=null) conn.close();
if(st!=null) st.close();
if(rs!=null) rs.close();
}
}
测试
package com.jdbctest.lesson05;
import com.jdbctest.lesson05.utils.jdbcutils_c3p0;
import com.jdbctest.lesson05.utils.jdbcutils_dbcp;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Test2 {
public static void main(String[] args) throws SQLException {
Connection connection=null;
PreparedStatement statement=null;
ResultSet rs=null;
try {
//获取连接
connection = jdbcutils_c3p0.getConnection();
//sql
String sql="SELECT * from users WHERE id=?";
//预编译sql
statement= connection.prepareStatement(sql);
//设置参数
statement.setObject(1,1);
//执行sql
rs=statement.executeQuery();
//遍历结果
while (rs.next()){
System.out.println(rs.getObject("NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
jdbcutils_dbcp.release(connection,statement,rs);
}
}
}