今日内容
1. PreparedStatement
PreparedStatement
--预编译
步骤
1)注册驱动
2)获取数据库连接对象
3)准备sql语句
--不需要拼接
--需要的参数全部使用 ? 占位符
4)通过数据库连接对象,获取预编译对象,同时将sql语句房费数据库,将参数和参数类型都存储在预编译中
Connection中的方法
PreparedStatement prepareStatement(String sql)
5)给参数赋值
void setXX(int Index,XX实际值)
index -- 代表第几个参数
实际值 -- 就是参数的实际值
6)执行预编译对象 --在这里不用将sql语句给进去,因为第4步已经将语句传过去了,只需要执行即可
int executeUpdate()
ResultSet executeQuery()
7)释放资源
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class preparedStatementTest {
public static void main(String[] args) throws SQLException {
Connection connection = JdbcUtils.getConnection();
String sql = "insert into student values (?,?,?,?,?) ;" ;
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,5);
preparedStatement.setString(2,"胡桃");
preparedStatement.setInt(3,18);
preparedStatement.setString(4,"女");
preparedStatement.setString(5,"往生堂");
preparedStatement.executeUpdate();
JdbcUtils.close(preparedStatement,connection);
1.1 面试题
Statement和PreparedStatement的区别
1)Statement
--每次书写一条sql就需要通过Statment将sql语句发送给数据库
-效率低并且数据库的压力大!
--发送的sql语句存在字符串拼接
-非常不安全--sql注入!--获取全部数据!
2)PreparedStatement
--将参数化的sql语句发送给数据库,进行预编译,以后执行语句只需要赋值即可,不需要重新传sql
-效率高,数据库压力小
--参数化的sql语句中,参数全部使用?占位符来代替,不存在拼接
-安全
1.2 SQL注入
SQL注入安全问题
在JDBC使用Statement获取执行对象,并将sql语句发送给数据库的过程中
-用户利用sql拼接的漏洞,将用户名和密码全部绕过!
举例
select * from user where username='helloworld' and password = 'hello 'or '1'='1' ;
--利用or的"或"特性--1是常量,恒成立--直接绕过用户名密码--直接访问其他人全部数据!
解决方案
全部使用PreparedStatement来进行后续过程!
2. JDBC方式控制事务
JDBC方式控制事务
JDBC中如果不主动设置,默认自动提交!
public void setAutoCommit(boolean autoCommit)
--手动设置是否自动提交
参数boolean autoCommit
-true,自动提交
-false,手动提交
public void rollBack()
--事务回滚,撤销之前的所有更新操作--前提是必须手动提交!
public void commit()
--手动提交
JDBC自动提交可能会出现的问题
不控制事务
--执行多个sql语句期间出问题了,会造成数据紊乱!
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JDBCTransaction {
public static void main(String[] args) throws SQLException {
Connection connection = JdbcUtils.getConnection();
String sql1 = "update student set age = age - 100 where id = ? ; " ;
String sql2 = "update student set age = age + 100 where id = ? ; " ;
PreparedStatement ps1 = connection.prepareStatement(sql1);
PreparedStatement ps2 = connection.prepareStatement(sql2);
解决方案
手动开启提交,利用回滚解决安全问题
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JDBCTransaction {
public static void main(String[] args) {
Connection connection = null ;
PreparedStatement ps1 = null ;
PreparedStatement ps2 = null ;
connection = JdbcUtils.getConnection();
String sql1 = "update student set age = age - 100 where id = ? ; " ;
String sql2 = "update student set age = age + 100 where id = ? ; " ;
ps1 = connection.prepareStatement(sql1);
ps2 = connection.prepareStatement(sql2);
connection.setAutoCommit(false);
} catch (SQLException throwables) {
} catch (SQLException e) {
throwables.printStackTrace();
JdbcUtils.close(ps1,connection);
JdbcUtils.close(ps2,connection);
3. 数据库连接池DateSource
数据库连接池DataSource
DataSource可以看作数据源,它封装了数据库参数,连接数据库
-程序中操作DataSource对象即可对数据库进行增删改查操作
DataSource连接池--类比线程池
连接池在创建的时候会带默认参数--从配置文件中获取
默认创建一定数量的数据库连接对象
每当使用完毕后会回到连接池中,等到下次继续使用!
Druid 德鲁伊!
连接池工具--jar包
来实现Java提供的DataSource接口
参数
driverClassName 创建驱动链接--com.mysql.jdbc.Driver
url 连接数据库链接--jdbc:mysql://localhost:3306/库名
username 数据库用户名
password 数据库密码
initialSize 定义初始化连接数
maxAction 最大连接数量
maxWait 连接等待时间(毫秒值)--超过等待时间直接结束
Druid获取连接数据库对象过程
1)导包
2)配置文件--严格按照Druid要求书写
3)创建属性列表集合
Properties
4)读取配置文件
5)将读取的字节输入流文件加载到属性列表中
6)从连接池获取对象
com.alibaba.druid.pool.DruidDataSourceFactory工厂类提供了方法
--public static DataSource createDataSource(配置文件名)
--创建数据源DataSource数据源接口对象
本质
DruidDataSource实现了DataSource接口
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
InputStream input = druidTest.class.getClassLoader()
.getResourceAsStream("druid.properties");
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
Connection connection = dataSource.getConnection();
System.out.println(connection);
4. ThreadLocal
ThreadLocal--线程变量--与连接池连用,保护数据安全!
隔离线程
synchronized
--使多个线程安全的抢占同一资源--数据共享
ThreadLocal
--使每个线程都使用自己的资源--数据隔离
格式
ThreadLocal<存储类型> 名 = new ThreadLocal<>()
方法
public T get()
--获取当前线程中执行的值
public void set(T value)
--将任意内容绑定当前线程,不为空则更新,为空则直接赋值
public void remove()
--线程使用完毕之后,需要将内容从中解绑--否则可能会造成数据泄露
5. 优化Utils工具类-Druid-ThreadLocal
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;
public class DruidUtils {
private static DataSource dataSource = null ;
private static ThreadLocal threadLocal = new ThreadLocal<>();
Properties properties = new Properties();
InputStream inputStream = DruidUtils.class.getClassLoader()
.getResourceAsStream("druid.properties");
properties.load(inputStream);
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
public static DataSource getDataSource(){
public static Connection getConnection() {
Connection connection = null ;
connection = threadLocal.get() ;
connection = dataSource.getConnection();
threadlocal.set(connection) ;
} catch (SQLException throwables) {
throwables.printStackTrace();
public static void controlTransaction(){
Connection connection = getConnection();
connection.setAutoCommit(false);
} catch (SQLException throwables) {
throwables.printStackTrace();
public static void rollBack(){
Connection connection = getConnection() ;
} catch (SQLException throwables) {
throwables.printStackTrace();
public static void commit(){
Connection connection = getConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
public static void close(PreparedStatement preparedStatement, Connection connection){
if (preparedStatement!=null){
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
public static void close(ResultSet resultSet,PreparedStatement preparedStatement,Connection connection){
} catch (SQLException throwables) {
throwables.printStackTrace();
if (preparedStatement!=null){
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();