预编译对象
SQL注入的漏洞:
用户输入一些特殊的值,在拼接SQL字符串的时候,导致SQL的语法结构发生变化,从而绕过了SQL的条件。形成的安全问题,叫SQL注入漏洞。
如何避免SQL注入漏洞:使用预编译对象PreparedStatement
如:
1.什么是预编译对象:
预编译对象:java.sql.PreparedStatement
,是Statement的子接口,功能更强;是另外一种SQL执行平台对象,能够解决SQL注入漏洞;
1.1 Satement 和PreparedStatement的区别.
Satement 对象每执行一条SQL语句都会先将这条SQL语句发送给数据库编译,数据库再执行。如果有一万条语句就要执行一万次就很效率很低。
PreparedStatement()会先将SQL语句发送数据库编译, PreparedStatement 会引用着预编译后的结果。 可以多次传入不同的参数给 PreparedStatement 对象并执行。相当于调用方法多次传入不同的参数。
1.2PreparedSatement的好处
1. prepareStatement() 会先将SQL语句发送给数据库预编译。 PreparedStatement 会引用着预编译后的结果。 可以多次传入不同的参数给 PreparedStatement 对象并执行。减少SQL编译次数,提高效率。
2. 安全性更高,没有SQL注入的隐患。
3. 提高了程序的可读性
1.3PreparedSatement使用步骤
1. 编写SQL语句,未知内容使用?占位: "SELECT * FROM user WHERE name=? AND password=?;";
2. 获得PreparedStatement对象
3. 设置实际参数
4. 执行参数化SQL语句
5. 关闭资源
代码演示:
package com.luliang.PreparedSatement;
import com.luliang.JDBCbao.JdbcUtils1;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) throws Exception{
Scanner scanner = new Scanner(System.in);
System.out.println("请你输入账号:");
String name = scanner.nextLine();
System.out.println("请你输入密码:");
String password = scanner.nextLine();
//1.注册驱动获取连接
Connection connection = JdbcUtils1.getConnection();
//2.编写SQL语句,未知内容用?占位
String sql="SELECT*FROM user WHERE name='"+name+"' AND password='"+password+"'";
//2.1将PrepareStatement()会先将SQL语句发送给数据库预编译
//如果查询到数据,锁门登陆成功
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//错误的密码
ResultSet resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
String name1 = resultSet.getString("name");
System.out.println("恭喜你登陆:"+name1);
}else {
System.out.println("你输入的密码错误...");
}
//关闭资源
JdbcUtils1.close(resultSet,preparedStatement, connection);
}
}
PrepardSatement实现增删改查:
增:
package com.luliang.PreparedSatement;
import com.luliang.JDBCbao.JdbcUtils1;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
//增
public class Demo02 {
public static void main(String[] args) throws Exception{
//1.注册驱动,获取连接
Connection connection = JdbcUtils1.getConnection();
//2.编写SQL语句
String sql="INSERT INTO liang VALUES(NULL,?,?,?)";
//3.将PrepareStatement()会先将SQL语句发送给数据库预编译
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//设置参数
preparedStatement.setString(1,"刘德华");
preparedStatement.setString(2,"香港");
preparedStatement.setInt(3,44);
int resultSet = preparedStatement.executeUpdate();
//影响的行数
System.out.println("影响的行数:"+resultSet);
//关闭资源
JdbcUtils1.close(connection,preparedStatement);
}
}
删:
package com.luliang.PreparedSatement;
import com.luliang.JDBCbao.JdbcUtils1;
import java.sql.Connection;
import java.sql.PreparedStatement;
//删
public class Demo03 {
public static void main(String[] args) throws Exception{
//注册驱动,获取连接
Connection connection = JdbcUtils1.getConnection();
//编写SQL语句
String sql="DELETE FROM liang WHERE id=?";
//将PrepareStatement()会先将SQL语句发送给数据库预编译
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//设置参数
preparedStatement.setInt(1,3);
//执行DML语句:statement.executeUpdate(sql),返回int,表示影响行数
int i = preparedStatement.executeUpdate();
System.out.println("影响的行数:"+i);
//关闭资源
JdbcUtils1.close(connection,preparedStatement);
}
}
改:
package com.luliang.PreparedSatement;
import com.luliang.JDBCbao.JdbcUtils1;
import java.sql.Connection;
import java.sql.PreparedStatement;
//改
public class Demo04 {
public static void main(String[] args) throws Exception {
//1.注册驱动,数据连接
Connection connection = JdbcUtils1.getConnection();
//2.编写SQL代码
String sql="UPDATE liang SET salary=? WHERE id=?";
//3.将PrepareStatement()会先将SQL语句发送给数据库预编译
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//设置数据
//
preparedStatement.setDouble(1,10000);
preparedStatement.setInt(2,4);
//设置影响的行数
int i = preparedStatement.executeUpdate();
System.out.println("影响的行数:"+i);
}
}
查:
package com.luliang.PreparedSatement;
import com.luliang.JDBCbao.JdbcUtils1;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
//查
public class Demo05 {
public static void main(String[] args) throws Exception{
//1.注册驱动,数据连接
Connection connection = JdbcUtils1.getConnection();
//书写SQL语句
String sql="SELECT*FROM liang WHERE id<=?";
//将PrepareStatement()会先将SQL语句发送给数据库预编译
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//设置数据
preparedStatement.setInt(1,1);
//影响数据的行数
ResultSet resultSet = preparedStatement.executeQuery();
//创建集合存放多个resultSet对象
ArrayList<Employee> employees = new ArrayList<>();
while (resultSet.next()){
//移动到下一行有数据,取出这数据
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String residese = resultSet.getString("residese");
double salary = resultSet.getDouble("salary");
//创建Employee对象
Employee employee = new Employee(id, name, residese, salary);
//将创建好的员工添加到集合中
employees.add(employee);
}
//输出对象
for (Employee employee : employees) {
System.out.println(employee);
}
//关闭资源
JdbcUtils1.close(resultSet,preparedStatement,connection);
}
}
2.=怎么使用预编译=
//1.注册驱动
//2.获取连接//3.创建SQL执行平台:预编译对象PreparedStatement
//3.1 改造SQL语句:使用?代替SQL里的参数值
//3.2 预编译SQL语句,得到预编译对象
//3.3 设置SQL语句里的参数值//4.执行SQL语句
//5.处理结果
//6.释放资源
2.1SQL语句的改造
SQL语句里,所有的参数值,都要使用?代替掉。
2.2预编译SQL,得到的预编译对象
PreparedStatement pstmt = connection.prepareStatement(sql)
`
2.3设置SQL的参数值
-
SQL里有几个
?
,就要设置几个值 -
设置参数值的方法:
pstmt.setXXX(参数序号, 参数值)
2.4执行SQL语句
执行DQL语句:
pstmt.executeQuery()
执行DML语句:
pstmt.executeUpdate()
执行任意语句:
pstmt.execute()
注意:预编译对象执行SQL的所有方法,都是无参方法
3.预编译对象的原理和好处
好处:
能够解决SQL注入漏洞
效率高:SQL编译一次,执行多次
可读性高:SQL语句结构清晰易读
连接池:
什么是连接池:
连接池:存储了一批连接对象的容器。如果需要操作数据库,不用再创建Connection对象,而是从连接池里取出一个使用;使用完成之后,把连接对象归还到连接池。
好处:
性能高:拿出一个使用,比创建一个速度快
连接对象循环使用,连接数就少了:
不会导致数据库所有可用连接,都被占用
不会Connection对象过多,导致内存溢出
自定义连接池
数据库连接池相关的API
java为数据库连接池提供了公共的接口:javax.sql.DateSource,各个厂商需要自己的连接池实现这个接口,这样应用程序可以方便的切换不同厂商的连接池.
自定义的步骤:
1. 定义一个类实现 javax.sql.DataSource 接口
2. 实现接DataSource口中的抽象方法
3. 定义连接池相关参数
4. 创建容器保存连接
5. 提供获取连接方法
6. 提供关闭连接方法
连接池的使用:
1.1定义一个类实现接口
package com.luliang.lianjieci;
import com.luliang.JDBCbao.JdbcUtils1;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.*;
import java.util.LinkedList;
import java.util.logging.Logger;
public class MyPool implements DataSource {
private int initCount = 5;//初始化的个数
private int maxCount = 10;//最大的连接个数
private int curCount = 0;//当前已经创建的连接池个数
//储存连接的容器
private LinkedList<Connection> list = new LinkedList<>();
//构造方法,初始化连接池的,连接池一旦创建那么连接池中就应该要有指定的个数连接
public MyPool() throws Exception {
for (int i = 0; i < initCount; i++) {
Connection connection = createConnection();
//把连接存储到容器中
list.add(connection);
}
}
//创建容器保存连接
//创建Connection的方法
public Connection createConnection() throws Exception {
//注册驱动,获取连接
Connection connection = JdbcUtils1.getConnection();
curCount++;
return connection;
}
//提供获取连接的连接方式,提供关闭连接的方法
//陪人问你连接池要连接
@Override
public Connection getConnection() throws SQLException {
//情况1:先判断是否连接
if (list.size() > 0) {
//如果连接池有连接,那么直接取出连接,返回即可
Connection connection = list.removeFirst();
return connection;
}
//情况2:连接池中没有连接,然后先判断目前的连接个数是否已经草果了最大的连接的个数
if (curCount < maxCount) {
//创建连接
try {
Connection connection = createConnection();
} catch (Exception e) {
e.printStackTrace();
}
} else {
//没有连接,并将已经草果了最大的连接个数
throw new RuntimeException("已经达到连接的连接的个数.请等候.");
}
return null;
}
//回收Connection
public void close(Connection connection) {
list.add(connection);
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public ConnectionBuilder createConnectionBuilder() throws SQLException {
return null;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
public static void main(String[] args) throws Exception {
MyPool myPool = new MyPool();
System.out.println("连接池1:"+myPool.getConnection());
Connection connection=myPool.getConnection();
System.out.println("连接10:"+connection);
}
}
运行结果:
常见的连接池:
C3po-jar包和配置文件
第一步:
配置文件参数
使用步骤:
导入jar包:
c3p0-0.9.2-pre5.jar
和mchange-commons-java-0.2.3.jar
提供配置文件
编写代码,使用连接池
配置文件
配置文件名称必须是:c3p0-config.xml
配置文件的位置必须在:类加载路径下(src下)
c3p0-config>
<default-config>
<!-- 前边4个是必须的配置项 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///heima63</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 后边是可以不配置的配置项 -->
<!--最大等待时间-->
<property name="checkoutTimeout">30000</property>
<!--连接池初始化容量-->
<property name="initialPoolSize">10</property>
<!--最大空闲回收时间-->
<property name="maxIdleTime">30</property>
<!--连接池最大容量-->
<property name="maxPoolSize">100</property>
<!--连接池最小容量-->
<property name="minPoolSize">10</property>
</default-config>
<!-- This app is massive! -->
<named-config name="itcast">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///heima</property>
<property name="user">root</property>
<property name="password">root</property>
</named-config>
</c3p0-config>
常见的配置文件解释
运行代码:
package com.luliang.lianjieci;
import com.luliang.JDBCbao.JdbcUtils2;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class Demo03 {
public static void main(String[] args) throws Exception {
//查询
//获取连接
ComboPooledDataSource dataSource = new ComboPooledDataSource("c3p0-config.xml");
//从连接池中获取连接
Connection connection = dataSource.getConnection();
//使用连接操作数据
//从创建SQL执行平台
PreparedStatement preparedStatement = connection.prepareStatement("SELECT*FROM product1 WHERE price>?");
//添加数据
preparedStatement.setDouble(1,500.0);
//执行SQL语句
ResultSet resultSet = preparedStatement.executeQuery();
//处理结果
while (resultSet.next()){
String pname = resultSet.getString("pname");
double price = resultSet.getDouble("price");
System.out.println("姓名:"+pname+", 工资:"+price);
}
//释放资源
resultSet.close();
preparedStatement.close();
connection.close();//从连接池中获取对象
}
}
druid连接池的使用
使用步骤:
1.导入jar包
2.提供配置文件
常用的配置文件
API介绍:
com.alibaba.druid.pool.DruidDataSourceFactory 类有创建连接池的方法
public static DataSource createDataSource(Properties properties)
创建一个连接池 ,连接池的参数使用properties中的数据
我们可以看到DRUID连接池在创建的时候需要一个Properties对象来设置参数,所以我们使用properties文件来保 存对应的参数。 DRUID连接池的配置文件名称随便,建议放到src目录下面方便加载。 druid.properties 文件内 容:
#数据库连接地址
url=jdbc:mysql:///lu
#数据库驱动类名
driverClassName=com.mysql.jdbc.Driver
#数据库用户名
username=root
#数据库密码
password=root
#连接池初始化容量
initialSize=20
#连接池最大容量
maxActive=50
#连接池最小容量
minIdle=10
#最大等待时间
maxWait=100000
编写代码:
连接池的使用:
所有连接池的使用,编写代码的步骤:
创建一个连接池对象:类名不同
从连接池里获取连接:方法一定是
getConnection()
....使用连接对象操作数据库...
把连接归还到连接池:方法一定是
close()
编写代码,使用连接池
package com.luliang.lianjieci;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
public class Demo04 {
//druid连接池的使用查询语句
public static void main(String[] args) throws Exception {
//读取配置文件得到Properties对象
Properties properties = new Properties();
InputStream inputStream = Demo04.class.getClassLoader().getResourceAsStream("luliang.properties");
//一个方法获取一个流
properties.load(inputStream);
//1.创建连接池的获取连接
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//2.从连接池中获取连接
Connection connection = dataSource.getConnection();
//3.操作数据库
//3.1创建SQL执行平台
//添加代码
String sql = "SELECT*FROM liang";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//执行SQL语句
ResultSet resultSet = preparedStatement.executeQuery();
//4.处理结果
while (resultSet.next()) {
String name = resultSet.getString("name");
String residese = resultSet.getString("residese");
double salary = resultSet.getDouble("salary");
System.out.println("姓名:" + name + ", 住宅:" + residese + ", 工资:" + salary);
}
//关闭资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}