目录
3.准备sql语句---定义String类型,里面书写插入/修改/删除
4.获取数据库的连接对象 java.sql.Connnection
6. java.sql.ResultSet:获取数据表的结果集 (接口)
4.Statement和PreparedStatement的区别?
2.2 连接池的配置文件(druid.properties)
一、JDBC入门
1.jdbc的概念
JDBC(Java DataBase Connectivity:java数据库连接)
Java 连接数据库的规范(标准),可以使用 Java 语言连接数据库完成CRUD 操作。
2.jdbc的JDBC 核心思想
Java 中定义了访问数据库的接口,可以为多种关系型数据库提供统一的访问方式。
由数据库厂商提供驱动实现类(Driver 数据库驱动)。
3.JDBC七大步骤
1.导包
mysql5.1的jar包---com.mysql.jdbc.Driver 驱动类
mysql8.0的jar包---com.mysql.cj.jdbc.Driver 驱动类
2.注册驱动
Class.forName("com.mysql.jdbc.Driver") ; 获取正在运行的这个类的字节码文件对象;
3.准备sql语句---定义String类型,里面书写插入/修改/删除
静态sql语句,里面的参数要么键盘录入,要么直接给值,存在字符串拼接!
String sql = "insert into account(name,balance) values('文章',1000);" ;
4.获取数据库的连接对象 java.sql.Connnection
java.sql.DriverManager类:
提供静态方法:
public static Connection getConnection(String url,
String user,
String password)
throws SQLException
url:统一资源定位符
jdbc:mysql://本地默认localhost或者127.0.0.1:端口号/库名(mysql驱动包5.1jar包这个地址不需要带参数)
jdbc:mysql://本地默认localhost或者127.0.0.1:端口号/库名?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
mysql驱动包8.0jar包后面带参数
5.获取执行对象执行静态sql语句
Statement createStatement() java.sql.Statement:在数据库中执行sql语句的接口
6. java.sql.ResultSet:获取数据表的结果集 (接口)
针对ddl语句(创建表/修改表/删除表):返回值是0
int executeUpdate(String sql)throws SQLException
执行DQL语句,数据库查询语句 select语句
ResultSet executeQuery(String sql)throws SQLException
7.释放资源
rs.close();
stat.close();
conn.close();
二、JDBC各个功能类详解
1.DriverManager
jdk提供的DriverManager驱动管理类:管理jdbc服务的
public static void registerDriver(Driver driver)throws SQLException 注册驱动
参数:java.sql.Driver接口
书写代码中不需要上面的这个步骤,是因为
com.mysql.jdbc.Driver 这个类里面已经注册驱动了
里面有静态代码块
static {
try {
//注册驱动
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
随着类的加载就直接注册驱动了,所以我们只需要获取正在运行的这个类的字节码文件对象,就会立即注册驱动
2.Connection
Connection:数据库连接对象
获取执行者对象
获取普通执行者对象:Statement createStatement();
获取预编译执行者对象:PreparedStatement prepareStatement(String sql);
管理事务
开启事务:setAutoCommit(boolean autoCommit); 参数为false,则开启事务。
提交事务:commit();
回滚事务:rollback();
释放资源:void close();
3.Statement
Statement:执行sql语句的对象
执行DML语句:int executeUpdate(String sql);
返回值int:返回影响的行数。
参数sql:可以执行insert、update、delete语句。
执行DQL语句:ResultSet executeQuery(String sql);
返回值ResultSet:封装查询的结果。
参数sql:可以执行select语句。
释放资源:void close();
4.ResultSet
ResultSet:结果集对象
判断结果集中是否还有数据:boolean next();
有数据返回true,并将索引向下移动一行
没有数据返回false
获取结果集中的数据:getXXX getXXX(“字段名称”)/(索引);
例如:以int数据举例:
getInt(int columnIndex) 这个检索的当前行中指定列的值 ResultSet作为对象
getInt(String columnLabel) 此ResultSet对象的当前行中指定列的值。
XXX代表数据类型(要获取某列数据,这一列的数据类型
释放资源:void close();
三、PreparedStatement预编译对象
1.使用Statement的弊端
使用Statement完成用户登录---------->有一张user表
id username password
键盘录入用户名和密码,
User类:private int id;
private String username;
private String password;
定义一个UserDao接口---->boolean isLogin(String username,String password) ;
userDaoImpl接口实现类----->实现这个业务
select * from user where username = 'xxx' and passwod='xxxx' ;
纯sql拼接会出现安全漏洞!
select * from user where username = 'xxx' and passwod='xxxx' or '1'='1' ;
如果查询到了数据---->结果集对象.next()--->返回true
--- 效率低
--- 造成sql注入(用户名和随便输入,依然登录成功)
2.PreparedStatement
为了避免出现Statement造成的sql注入漏洞,所以我们使用PreparedStatement
预编译sql语句的执行者对象。在执行sql语句之前,将sql语句进行提前编译
明确sql语句的格式后,就不会改变了;剩余的内容都会认为是参数
参数使用?作为占位符
为参数赋值的方法:setXxx(参数1,参数2);
参数1:?的位置编号(编号从1开始)
参数2:?的实际参数
执行sql语句的方法
执行insert、update、delete语句:int executeUpdate();
执行select语句:ResultSet executeQuery();
3.核心步骤
1.获取数据库连接对象
Connection conn = JdbcUtils.getConnection();
2.准备sql---参数化的sql(预编译sql语句)(参数使用?占位符)
String sql = "select * from user where username = ? and password = ?" ; //英文符号
3.通过数据库的连接对象获取预编译对象
PreparedStatement stmt = conn.prepareStatement(sql) ;
4.使用预编译对象给参数进行赋值
stmt.setString(1,user.getUsername()) ;
stmt.setString(2,user.getPassword()) ;
5.通过预编译对象执行这些参数(在预编译对象中执行参数化的sql)
ResultSet resultSet = stmt.executeQuery();
6.关闭释放资源
4.Statement和PreparedStatement的区别?
1)共同点:
都是可以发送sql到数据库的,都是执行对象,后者继承前者(java.sql.包下的接口)
2)不同点:
2.1)是否会造成sql注入
Statement永远执行的是静态sql语句:语句中存在"硬编码",存在SQL的字符串拼接,就造成sql注入,不安全!
PreparedStatement:永远执行的是参数化的sql语句,全部参数都是"?",占位符号,有效防止sql的拼接,预防sql注入,提高执行sql的安全性!
2.2)是否会提高sql执行效率
Statement:不会提高sql执行效率,
获取执行对象,然后将sql语句发送数据库(没写一个sql,发一次!),频繁的操作访问数据库
PreparedStatement:大大提高SQL执行效率
参数化sql是在获取PreparedStatement,就已经发送给数据库了,然后在PreparedStatement赋值不同的值;
四、数据库的连接池
1.图解
2. 自定义连接池
/**
* Druid连接池的使用步骤:
* 1)导包 druid-1.1.10.jar包
* 2)准备好连接池的配置文件
* 3)从连接池获取连接对象
* 提供jar包--->com.alibaba.druid.pool.DruidDataSource--->本质实现了一个接口javax.sql.DataSource
* --->Connection getConnection()
*
*/
2.1导包
2.2 连接池的配置文件(druid.properties)
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/myee_2302
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000
2.3配置文件参数说明
#这些名称都是DruidDataSoure提供的参数
#连接数据库的驱动类
driverClassName=com.mysql.jdbc.Driver
#连接数据库的url地址:统一资源定位符
url=jdbc:mysql://localhost:3306/myee2302_db_2
#用户名
username=root
#密码
password=123456
#连接池一旦创建,初始化5个连接数量
initialSize=5
#最大连接数量值:默认值8个,自己设定值,和maxIdel:最大空闲数量相等
maxActive=10
#最大等待时间:为毫秒值,一旦连接池中创建连接对象超过了最大连接数量,等待3秒中,如果还连接不上,连接池会产生错误日志,提示"连接超时"
maxWait=3000
2.4 具体代码体现
public class DruidDemo {
public static void main(String[] args) throws Exception {
//3)创建数据源DruidDataSource对象
//德鲁伊提供的数据源工厂:DruidDataSourceFactory提供静态方法,返回值就是DataSource
//public static DataSource createDataSource(Properties properties)
//创建属性集合列表
Properties prop = new Properties() ;
//读取src下配置文件
InputStream inputStream = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
//将字节输入流加载进来
prop.load(inputStream) ;
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
for(int x = 1 ; x <=11 ; x++){
//System.out.println(dataSource.getConnection());
Connection conn = dataSource.getConnection() ;
if(x==3){
conn.close(); //暂时释放,归还连接池中
}
System.out.println(conn);
}
/**
* 上面这个方法底层已经集成好了
* public static DataSource createDataSource(Map properties) throws Exception {
* DruidDataSource dataSource = new DruidDataSource(); //创建德鲁伊的数据源
* config(dataSource, properties); //封装数据:将配置文件的内容封装到dataSource数据源对象汇总
* return dataSource;//返回
* }
*/
}
}
五、JDBC工具类
1.工具类的抽取
- 配置文件(在src下创建config.properties)
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db1
username=root
password=root
- 工具类:com.test.utils.JdbcUtils
package com.qf.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* 将jdbc步骤优化
* 自定义工具类,将获取连接对象,释放资源都放在静态方法中,方便去使用!
*/
public class JdbcUtils {
//统一资源定位符
private static String url = null ;
//用户名
private static String user = null ;
//密码
private static String password = null ;
//驱动类
private static String driverClassName = null ;
//定义静态代码块
static{
try {
//1)读取src下面的jdbc.properties文件
//创建一个属性集合列表
Properties prop = new Properties() ;
//System.out.println(prop) ;
InputStream inputStream = JdbcUtils.class.getClassLoader().
getResourceAsStream("jdbc.properties");
//将字节输入流加载到属性集合列表中
prop.load(inputStream) ;
//System.out.println(prop) ;
//2)通过key获取value
driverClassName = prop.getProperty("driverClassName");
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password");
//3)注册驱动
Class.forName(driverClassName) ;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//对外私有,不让外界new
private JdbcUtils(){}
//定义公共的静态方法,返回值就是Connection
//public static Connection getConnection(String url,String user,String password){ //一会还得需要传入参数,
public static Connection getConnection(){ //一会还得需要传入参数,
Connection conn = null ;
try {
conn = DriverManager.getConnection(url,user,password) ;//url,user,password
return conn;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null ;
}
//释放资源
//public static void close(ResultSet rs,Statement stmt,Connection conn){
public static void close(ResultSet rs,PreparedStatement stmt,Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//主要针对DDL/DML语句操作释放资源
/* public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);
}*/
public static void close(PreparedStatement stmt,Connection conn){
close(null,stmt,conn);
}
public static void main(String[] args) {
Connection conn = JdbcUtils.getConnection();
System.out.println(conn);
}
}
2.工具类优化 (使用数据库连接池并加入事务开启回滚提交)
package com.qianfeng.day37.test01.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
/**
* Jdbc方式---加入Druid连接池,----自动的封装配置文件的所有数据
* 封装一个静态方法--->获取Connection(从连接池获取)
* 获取DataSource:数据源----> 连接参数(数据库的信息)以及初始化数量,最大激活数量,最大等待时间
* 释放资源
*/
public class DruidJdbcUtils {
//静态实例--->java.lang.ThreadLocal<T> 模拟线程,每一个用户都有自己的Connection
private static ThreadLocal<Connection> tl = new ThreadLocal<>() ; //里面没有连接对象
//声明一个DataSource接口类型 变量
private static DataSource ds ;
//构造方法私有化
private DruidJdbcUtils(){}
//静态代码块
static{
try {
//1)读取德鲁伊的配置文件,让德鲁伊自己封装配置文件参数
InputStream inputStream = DruidJdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//创建属性集合列表
Properties prop = new Properties() ;
//加载属性集合列表
prop.load(inputStream) ;
//2)创建DruidDataSource(连接池)
ds = DruidDataSourceFactory.createDataSource(prop);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取DataSource数据源信息 (连接池的所有参数)
public static DataSource getDataSource(){
return ds ;
}
//从数据源(连接池) 获取连接对象
public static Connection getConnection(){
//1)线程本地线程中获取连接对象---ThreadLocal<T>--->T get():从当前线程中获取存储的内容
Connection conn = tl.get() ;
try {
//2)判断
//如果当前conn对象为null,说明当前线程中没有要操作的连接对象
if(conn==null){
//3)从数据源(连接池中)获取Connection
conn = ds.getConnection();
//4)将从数据源中获取到的conn连接对象,绑定当当前线程中
//ThreadLocal<T>--->public void set(T t):绑定
tl.set(conn) ;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return conn ;
}
//释放资源
public static void close(ResultSet rs, PreparedStatement stmt, Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
//从当前线程解绑
tl.remove(); //ThreadLocal<T> :从线程中移出
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//主要针对DDL/DML语句操作释放资源
/* public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);
}*/
public static void close(PreparedStatement stmt,Connection conn){
close(null,stmt,conn);
}
/**
* 开启事务
*
*/
public static void setAutoCommit() throws SQLException {
//获取连接对象
Connection conn = getConnection();
//设置手动提交
conn.setAutoCommit(false) ;
}
/**
* 事务回滚
*/
public static void rollbackAndClose() throws SQLException {
//获取连接对象
Connection conn = getConnection();
conn.rollback() ;
//释放连接对象,从当前线程中解绑
conn.close();
tl.remove() ;
}
/**
* 提交事务
*/
public static void commitAndClose() throws SQLException {
//获取连接对象
Connection conn = getConnection();
conn.commit(); ;
//释放连接对象,从当前线程中解绑
conn.close();
tl.remove() ;
}
public static void main(String[] args) {
System.out.println(DruidJdbcUtils.getDataSource());
System.out.println(DruidJdbcUtils.getConnection());
}
}
六、commons-dbutils的使用步骤
1)导包 commons-dbutils-1.6.jar
2)创建执行器 QueryRunner---> 底层PreparedStatement
public QueryRunner(DataSource ds) 参数就是数据源--->自定义工具获取到了数据源 (自动提交)
public QueryRunner():创建执行器,手动提交
3)准备好sql语句
DML语句---添加/修改/删除
insert into
update
delete from...
QueryRunner提供通用的更新操作:
public int update(Connection conn, String sql, Object... params) throws SQLException :手动提交
public int update(String sql, Object... params):自动提交
DQL语句--- 查询语句
QueryRunner提供的通用查询操作
public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException
第一个参数:查询的sql语句
第二个参数:ResultSetHandler结果集的处理
它的子实现类:BeanHandler---->将查询的某一条记录封装到实体了中(JavaBean:是具体类,属性私有,对外提供setXXX()/getXXX)
public BeanHandler(Class<T> type) ---> 参数针对查询的记录--封装到类名.class中
子实现类:BeanListHandler<T> ---将查询的多条记录封装到List集合中,List集合都是当前类对象
public BeanListHandler(Class<T> type)
子实现类:
ScalarHandler:通过聚合函数(count(列名称),其他max(xx),avg(xxx))查询单行单的列的数据
的结果封装到Object类中
第三个参数:就是赋值的实际参数params,没有参数可以不写
注意事项:
使用commons-dbutils工具库:无论添加/删除/修改/查询数据,
必须保证实体类的属性名称和表的字段名称一一对应,否则数据封装不上去的,永远是null!
如果不对应,可以在(查询语句)--->给这字段给别名(保证别名和实体类的属性名称一致)
实体类的属性名称不要出现大写字母,否则也可能是null值!