10,JDBC(重点)
10.1 数据库驱动
驱动:声卡,显卡,数据库
我们的程序会通过数据库驱动,和数据库打交道
10.2 JDBC
SUN 公司为了简化 开发人员的(对数据库的统一操作),提供了一个(java操作数据库的规范),俗称 JDBC
这些规范的实现由具体厂商去做~
对于开发人员来说,我们只需要掌握JDBC接口的操作即可!
架构——没有什么是加一层解决不了的
java.sql
javax.sql
还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar
第一个 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)
-
创建一个普通项目
-
导入数据库驱动
将包复制到lib文件夹中
右键——Add as libray
-
编写测试代码
package lesson01; import java.sql.*; // 我的第一个JDBC程序 public class JdbcFirstDemo { //1.加载驱动 /** * 固定写法加载驱动 * mysql 8.0的驱动写法: com.mysql.cj.jdbc.Driver */ public static void main(String[] args) throws ClassNotFoundException, SQLException { Class.forName("com.mysql.jdbc.Driver"); //2.用户信息和url // mysql8 要加时区 报错显示时区问题在url后面加&serverTimezone=GMT%2B8 连接错误就改成false String url="jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&useSSL=true"; String username="root"; String password="123456"; //3.连接成功,数据库对象 Connection connection=DriverManager.getConnection(url, username, password); //4.执行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.cancel(); connection.close(); } }
步骤总结:
- 加载驱动
- 连接数据库 DriverManager
- 获得执行sql的对象 statement
- 执行完了获得返回的结果集
- 释放连接
DriverManager
/**
* 固定写法加载驱动
* mysql 8.0的驱动写法: com.mysql.cj.jdbc.Driver
* 进行反射调用会默认进行类初始化
*/
// DriverManager.registerDriver(new com.mysql.jdbc.Driver);
Class.forName("com.mysql.jdbc.Driver"); //固定写法加载驱动
Connection connection=DriverManager.getConnection(url, username, password);
// connection 代表数据库
// 数据库设置自动提交
// 事务提交
// 事务回滚
connection.rollback();
connection.commit();
connection.setAutoCommit();
URL
String url="jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&useSSL=true";
// mysql 默认3306
// "jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3
// 协议://主机地址:端口号/数据库名?参数1&参数2&参数3
// oralce--1521
// jdbc:oracle:thin:@localhost:1521:sid
Statement 执行SQL的对象 prepareStatement 执行SQL对象
String sql="SELECT * FROM users"; //编写sql
statement.executeQuery();// 执行查询 返回ResultSet
statement.executeUpdate();//更新,插入,删除 都是这个,返回一个受影响的行数
statement.execute(); // 执行任何SQL
statement.executeBatch(); // 批处理
ResultSet 查询的结果集:封装了所有的查询结果
获得指定的数据类型
resultSet.getObject(); // 在不知道列类型的情况下使用
// 如果知道列的类型就使用指定的类型
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
遍历,指针
resultSet.beforeFirst(); // 移动到最前面
resultSet.afterLast(); // 移动到最后面
resultSet.next() // 移动到下一个数据
resultSet.previous() // 移动前一行
resultSet.absolute(row) //移动到指定行
释放资源
//6.释放连接
resultSet.close();
statement.cancel();
connection.close(); // 耗内存
10.4 statement对象
CRUD操作-create
使用 executeUpdate(String sql) 方法完成数据添加操作,示例操作:
Statement st=conn,createStatement();
String sql="insert into user(...) values(...)";
int nums=st.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!!!");
}
CURD操作-delete
使用executeUpdate(String sql)方法完成数据删除操作,示例操作:
Statement st=conn,createStatement();
String sql="delete from user where id=";
int nums=st.executeUpdate(sql);
if(num>0){
System.out.println("删除成功!!!");
}
CRUD操作-update
使用使用executeUpdate(String sql)方法完成数据修改操作,示例操作:
Statement st=conn,createStatement();
String sql="update user set name='' where name=''";
int nums=st.executeUpdate(sql);
if(num>0){
System.out.println("修改成功!!!");
}
CRUD 操作-read
使用使用executeQuery(String sql)方法完成数据查询操作,示例操作:
Statement st=conn,createStatement();
String sql=" select * from user where id=1 ";
ResultSet rs=st.executeQuery(sql);
while(rs.next()){
// 根据获取列的数据类型,分别调用rs的相应方法映射到java对象中
}
代码实现
unicode 为了实现中文
时区 ServerTimezone=Asia/Shanghai
目的类属性和功能分离,方法就是配置文件+工具类
-
提取工具类
-
编写增删改的方法,executeUpdate
以删除为例子
package lesson02; import 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(); String sql="DELETE FROM `users` WHERE `id`=4"; int i=st.executeUpdate(sql); if(i>0){ System.out.println("删除成功"); } } catch (SQLException e) { e.printStackTrace(); } finally { JdbcUtils.release(conn,st,rs); } } }
-
查询
静态代码块在内存加载的时候优先执行,也就是每次调用这个类都会去先执行静态代码块
package lesson02; import lesson02.utils.JdbcUtils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class TestQuery { 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 e) { e.printStackTrace(); } finally { JdbcUtils.release(conn,st,rs); } } }
SQL注入的问题
SQL 注入(SQL Injection) 是发生在 Web 程序中数据库层的安全漏洞,是网站存在最多也是最简单的漏洞。
主要原因是程序对用户输入数据的合法性没有判断和处理,导致攻击者可以在 Web 应用程序中事先定义好的 SQL 语句中添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步获取到数据信息。
因为正常的sql语句就是 where name =’测试用户1‘
例子
SELECT * FROM users WHERE `NAME`='wawa' AND `password`='123456'
10.5 PrepareStatement对象
PrepareStatement 可与防止SQl注入,效率更好
- 新增
- 删除
- 更新
- 查询
package com.hjy.lesson03;
public class TestInsert {
public static void main(String[] args){
Connection conn=null;
PrePareStatement st=null;
ResultSet rs=null;
try{
conn=JdbcUitls.getConnection();
// 区别
String sql="insert into users(`id`,`NAME`,`PASSWORD`,`email`,`birthday`) values(?,?,?,?,?)";、
st=conn.prepareStatement(sql);
conn.prepareStatement()// 预编译的SQL 先写SQL 但是不执行
// 手动给参数赋值
st.setInt(1,4);
st.setString(2,"huangjieyu");
st.setString(3,"123456");
st.setString(4,"112233@qq.com");
// 注意点:sql.Date 数据库
// util.Date java new Date().getTime() 获得时间戳
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.release(conn,st,null)
}
}
}
删除
String sql="delete from user where id=?";
st.setInt(1,4)
修改
String sql="update users set `NAME`=? where id=?";
st.setString(1,'huangjieyu');
st.setInt(2,1)
查询
package com.hjy.lesson03;
public class TestQuery {
public static void main(String[] args){
Connection conn=null;
PrePareStatement st=null;
ResultSet rs=null;
try{
conn=JdbcUitls.getConnection();
// PrepareStatement 防止SQL注入的本质,把传进去的参数当作字符
// 假设其中存在转义字符,就直接忽略,‘ 会被直接转义
// 区别
String sql="select * from users where id=?";
st=conn.prepareStatement(sql);
conn.prepareStatement()// 预编译的SQL 先写SQL 但是不执行
// 手动给参数赋值
st.setInt(1,4);
// 注意点:sql.Date 数据库
// util.Date java new Date().getTime() 获得时间戳
st.setDate(5,new java.sql.Date(new Date().getTime()));
// 执行
rs= st.executeQuery();
if(re.next()){
System.out.println(rs.getString("NAME"));
}
}catch(SQlException e){
e.printStackTrace();
}
finally{
JdbcUtils.release(conn,st,rs)
}
}
}
10.6 IDEA连接数据库
连接不成功
在URL栏中语句最后面添加?serverTimezone=UTC
设置-schemas 选择你要连接的数据库 双击数据库 查询内容
双击数据库里的列-提交 DB
连接失败,查看原因
10.7 事务
要么都成功,要么都失败
ACID原则
原子性:要么全部完成,要么都不完成
一致性:总数不变
隔离性:多个进程互不干扰
持久性:一旦提交不可逆,持久化到数据库了
隔离性的问题:
脏读:一个事务读取了另一个没有提交的事务
不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变
虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来结果不一致
代码实现
- 开启事务
conn.setAutoCommit(false);
- 一组业务执行完毕,提交事务
- 可以在catch语句中显式的定义 回滚语句,但默认失败就会回滚
package lesson04;
import lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestTransaction2 {
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 account set money=money-100 where name='A'";
String sql2="update account set money=money+100 where name='B'";
st=conn.prepareStatement(sql1);
st.executeUpdate();
st=conn.prepareStatement(sql2);
st.executeUpdate();
// int x=1/0; // 报错
// 业务完毕,提交事务
conn.commit();
System.out.println("成功!");
} catch (SQLException e) {
// 如果失败,则默认回滚
// 下面是显式定义
try {
conn.rollback(); // 如果失败就回滚
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
10.8 数据库连接池(DBCP-CP30连接池)
数据库连接—执行完毕—释放 连接—释放 十分浪费系统资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
例子:
—开门—服务—关门
—开门—业务员:等待连接—服务—
常用连接数 10
最小连接数:10
最大连接数:100 业务最高承载上限
超过最高承载上限,排队等待
等待超时:100ms(就自己走了)
编写连接池,实现一个接口 DataSource
开源数据源实现(拿来即用)
DBCP
C3P0
Druid:阿里巴巴 springboot
使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了!
DBCP
需要用到的jar包
commons-logging-1.2.jar
需要用到的jar包都可以在阿里云仓库找到 commons-dbcp-1.4.jar commons-pool-1.6.ja
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&uesSSL=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_COMMITTED
package 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("dbcp.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 lesson05;
import lesson05.utils.jdbcutils_dbcp;
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) 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.5.5.jar mchange-commons-java-0.2.19.jar
xml不用读 加载的时候可以自动匹配
<?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 lesson05.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtils_C3P0 {
private static ComboPooledDataSource source=null;
static {
try {
// 代码版配置
// source=new ComboPooledDataSource();
// source.setDriverClass();
// source.setUser();
// source.setPassword();
// source.setJdbcUrl();
// source.setMaxPoolSize();
// source.setMinPoolSize();
//创建数据源 工厂模式--> 创建对象
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 lesson05;
import lesson05.utils.JdbcUtils_C3P0;
import lesson05.utils.jdbcutils_dbcp;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class TestC3P0 {
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_C3P0.release(connection,statement,rs);
}
}
}
结论
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_C3P0.release(connection,statement,rs);
}
}
}
> 结论
无论使用什么数据源,本质还是一样的,DataSource接口还是一样的,方法就不会变
课程笔记都是看的狂神的课程做的,感谢。