JDBC
一.JDBC的本质
本身是一个普通的java类,能够实现sun公司提供的一套接口规范:
java.sql.Driver
java.sql.Connection
java.sql.Statement
二.JDBC的七大操作步骤
//1.导入驱动包
//2.注册驱动--加载驱动类
Class.forName(com.mysql.jdbc.Driver);
//3.获取数据库的连接对象
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/库名","root","password")
//4.准备sql语句
String sql = "";
//5.通过connection连接对象获取数据库的执行对象
statement stmt = con.createStatement();
//6.执行sql语句
int count = stmt.executeUpdate(sql);
//7.释放资源
stmt.close();
con.close();
1.1JDBC所涉及的API
1)导包---导入msyql驱动包
2)注册驱动--->
Class.forName("com.mysql.jdbc.Driver") ; //为了向后兼容 ---在加载类
本身注册驱动需要用到java.sql.DriverManager:驱动管理类(管理诸如JDBC的服务)
提供静态功能:
1)注册驱动public static void registerDriver(Driver driver) throws SQLException
参数为:java.sql.Drvier---->需要接口的实现类---驱动jar包里面com.mysql.jdbc.Drvier
如果上面的写法使用 DriverManager.registerDriver(new com.mysql.jdbc.Driver());这种方式,会多注册一遍
com.mysql.jdbc.Driver的原码里面
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static { //静态代码块
try {
DriverManager.registerDriver(new Driver()); //注册驱动了
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
获取数据库的连接对象,返回值 java.sql.Connection
2)public static Connection getConnection(String url,
String user,
String password)
throws SQLException
url: 协议://域名:端口/库名 (统一资源定位符号)
jdbc:mysql://localhost:3306/库名
user:mysql的用户名: root用户
password:密码
java.sql.Connection:与特定数据库连接会话 接口
Statement createStatement() throws SQLException; 获取执行对象
返回返回的数据库执行对象--->里面创建具体子实现类对象
public class StatementImpl implements Statement
java.sql.Statement:用于执行静态SQL语句并返回其生成的结果的对象。
静态sql语句:
弊端
1)硬编码
2)存在sql字符串拼接
String sql = "update account set balance = balance + 500 where id =1 " ;
两种通用操作
针对DDL语句(针对表/库相关的操作),DML语句i的nsert ,update,delete 更新操作
public int executeUpdate(String sql)
针对DQL语句:数据库查询语句 ,返回表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
ResultSet executeQuery(String sql) throws SQLException
java.sql.ResultSet 据库结果集的数据表
boolean next():是否下一个结果集
三.工具类(构造方法私有,目的就是为了让外界不能new对象)
3.1JdbcUtils工具类
public class JdbcUtils {
//成员变量声明三个变量
private static String url = null ;
private static String user = null ;
private static String password = null ;
private static String driverClass = null ;
//模拟 驱动jar包---Driver驱动类---提供静态代码块
static{
try {
//想办法获取这些参数---->提供配置文件 后缀名.properties---->放在src下面
//1)读取配置文件内容
InputStream inputStream = JdbcUtils.class.getClassLoader()
.getResourceAsStream("jdbc.properties");
//2)创建一个属性集合列表Properties
Properties prop = new Properties() ;
//System.out.println(prop) ;//测试 ---肯定空列表
//3)将1)获取资源文件的输入流对象---加载到属性集合列表中
prop.load(inputStream);
// System.out.println(prop) ; //测试--->有属性列表内容
//4)通过key获取value
driverClass = prop.getProperty("driverClass");
url = prop.getProperty("url") ;
user = prop.getProperty("user") ;
password = prop.getProperty("password") ;
//5)加载驱动类
Class.forName(driverClass) ;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private JdbcUtils(){}
/**
* 这个方法,获取数据库的连接对象
* @return
*/
public static Connection getConnection(){
try {
//需要驱动管理DriverManager获取连接对象
Connection connection = DriverManager.getConnection(url, user, password);//获取这三个参数的内容
return connection ;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null ;
}
/**
* 释放资源,针对DQL语句操作释放的相关资源对象
* @param rs
* @param stmt
* @param conn
*/
public static void close(ResultSet rs,Statement 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语句
* @param stmt
* @param conn
*/
public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);
}
public static void main(String[] args) {
Connection connection = JdbcUtils.getConnection();
System.out.println(connection);
}
}
封装步骤
1)准备配置文件 jdbc.properties
url=jdbc:mysql://localhost:3306/库名
driverClass=com.mysql.jdbc.Driver
user=root
password=123456
2)当前类中提供静态代码块
static{
1) 读取配置文件
InputStream inputStream = 前类名.class.getClassLoader().getResourceAsStream("jdbc.properties") ;
2)创建属性集合列表
Properties prop = new Properties() ;
//3)加载字节输入流对象到属性集合列表中
prop.load(inputStream) ;
//4)通过key获取value
//public String getProperty(String key)
获取四个key----给成员变量进行赋值
5)加载驱动
Class.forName(驱动类的全限定名称获取到);
}
3)静态方法----获取Connection
Connection conn = DriverManager.getConnection(三个参数直接传进去) ;
4)静态方法--->释放资源
针对DML语句 释放Statment以及Connection
针对DQL语句:释放ResultSet Statement,Connection
获取src下面的配置文件.properties内容
InputStream inputStrema = 前类名.class.getClassLoader().getResourceAsStream("jdbc.properties") ;
// 2)创建属性集合列表
Properties prop = new Properties() ;
//3)加载字节输入流对象到属性集合列表中
prop.load(inputStream) ;
//4)通过key获取value
//public String getProperty(String key)
3.2DruidJdbcUtils工具类
加入Druid连接池的工具类
1)现在目的就创建数据源--javax.sql.DataSource接口--->通过DruidDataSourceFactory创建数据源
2)模拟真实场景
public class DruidJdbcUtils {
//声明数据源
private static DataSource ds = null ;
//模拟线程 :每一个线程使用自己的Conneciton
private static ThreadLocal<Connection> t1 = new ThreadLocal<>();
//构造方法私有化,外界类不能new
private DruidJdbcUtils(){}
//静态代码块
static{
try {
//当前工具类一加载,读取src下面的druid.properties配置文件
InputStream inputStream = DruidJdbcUtils.class.getClassLoader().
getResourceAsStream("druid.properties");
//创建属性集合列表,将配置文件资源输入流加载属性集合列表中
Properties prop = new Properties() ;
prop.load(inputStream) ;
//通过DruidDataSourceFactory创建DataSource对象---->DruidDataSource具体子实现类
给ds重写赋值
ds = DruidDataSourceFactory.createDataSource(prop);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取数据源,就是将配置文件的内容加载到了DataSource
* @return
*/
public static DataSource getDataSource(){
return ds ;
}
/**
* 从连接池中获取连接对象 ---->首先需要创建DataSource接口对象
* @return
*/
public static Connection getConnection(){
try {
//1)从当前线程获取Conection
Connection conn = t1.get();
if(conn==null){
//2)当前线程中没有连接对象
//需要从DataSource连接池获取连接对象
conn = ds.getConnection();
//3)将当前连接对象绑定在当前线程上
t1.set(conn);
}
return conn ;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null ;
}
//释放资源 针对DQL语句
public static void close(ResultSet rs, PreparedStatement ps ,Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close(); //归还连接池中
//需要从当前线程ThreadLocal进行解绑
t1.remove();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
/**
* 针对DDL或者DML语句
* @param ps
* @param conn
*/
public static void close( PreparedStatement ps ,Connection conn){
close(null,ps,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();
t1.remove(); //解绑
}
//提交事务
public static void commitAndClose() throws SQLException {
Connection conn = getConnection();
conn.commit(); //提交事务
conn.close();
t1.remove(); //解绑
}
public static void main(String[] args) {
// System.out.println(DruidJdbcUtils.getDataSource());
System.out.println(DruidJdbcUtils.getConnection());
}
}
通过DruidDataSourceFactory如何创建数据源
//1)读取连接池的配置文件
InputStream inputStream =当前类名.class.getClassLoader().getResourceAsStream("druid.properties") ;
//2)创建属性集合列表
Properties prop = new Properties() ;
//3)加载字节输入流到属性列表中
prop.load(inputStream) ;
//4)创建数据源
DataSource ds = DruidDataSourceFactory.createDataSource(prop) ; //方法本质就是创建的DruidDataSource子实现类对象
德鲁伊配置文件.properties
#德鲁伊的加载驱动的名称driverClassName
driverClassName=com.mysql.jdbc.Driver
#连接的库的地址
url=jdbc:mysql://localhost:3306/ee_2211_02
#用户名
username=root
#密码
password=123456
#连接池启用之后初始化的连接数量5个
initialSize=5
#连接池中最大激活数量10个
maxActive=10
#最大等待时间 3000毫秒(3秒),当连接池如果中的连接数数量超过了最大就激活数量,等待3秒中连接,如果超过3秒,拒绝连接对象进入连接池
maxWait=3000
四.Statement、PreparedStatement、dbutils
4.1Statement和PreparedStatement的区别
Statement执行sql的效率相对PreparedStatement非常低
1)每次书写一条sql就需要通过Statmeent将sql语句发送给数据库,效率低;同时,数据库的压力大!
2)发送的sql语句存在字符串拼接,就会出现安全问题--->SQL注入,恶意攻击数据库,造成安全漏洞!
PreparedStatement执行sql语句效率相对来说很高
1)将一条参数化 的sql语句发送给数据库,进行预编译将编译结果存储在预编译对象中,下一次直接赋值,
而且赋值很多次,发送一次sql,执行不同的参数!
2)参数化的sql语句不存在支持拼接,有效防止SQL注入,开发中使用PreparedStatement来对数据库CRUD