JDBC:用于执行SQL语句的java API,为多种数据库提供统一的访问,不需要具体了解每种数据库的驱动的写法,只需要了解JDBC的接口规范,每种数据库的具体驱动都需要实现JDBC的规范
jdbc程序运行完后,必须释放程序在运行过程中,创建的那些与数据库交互的对象。ResultSet、Statement、Connection对象:Connection是数据库的连接对象,往往会有一个最大的访问个数,如果没有把对象及时释放掉,可能导致别人无法访问数据库
import com.mysql.jdbc.Driver;
import org.junit.Test;
import java.sql.*;
public class JDBCDemo1 {
@Test
public void demo1(){
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
//1.加载驱动
try {
// DriverManager.registerDriver(new Driver());会导致驱动注册两次,源代码中静态代码块包含注册驱动的语句,当Driver类被加载时会注册驱动
//加上以上语句手动注册一次,会注册驱动两次
Class.forName("com.mysql.jdbc.Driver");//加载Driver类,类一旦被加载,就会执行静态代码块,注册驱动
//2.获得连接
conn=DriverManager.getConnection("jdbc:mysql:///jdbctest","root","1984");
//3.创建执行SQL语句的对象,并且执行SQL
//3,1创建执行SQL的对象
String sql="select * from user";
stmt=conn.createStatement();
//3.2执行SQL语句
rs=stmt.executeQuery(sql);
while(rs.next()){
int uid=rs.getInt("uid");
String username=rs.getString("username");
String password=rs.getString("password");
String name=rs.getString("name");
System.out.println(uid+" "+username+" "+password+" "+name);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//为了保证程序异常时,仍然可以正确释放资源,将释放资源放到finally代码块内(不管是否发生异常都会执行)
//4.释放资源
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs=null;
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt=null;
}
if(conn!=null){
try {
conn.close();//遵循晚创建,早释放,虽然释放了,但还没有被垃圾回收机制回收掉
} catch (SQLException e) {
e.printStackTrace();
}
conn=null;//手动置为null,垃圾回收机制会更早地回收对象
}
}
}
}
JDBC的增删改查:增删改操作基本一致
import org.junit.Test;
import java.sql.*;
public class JDBCDemo2 {
//增删改操作基本类似
@Test
/**
* 保存操作
*/
public void demo1() {
Connection conn = null;
Statement stmt = null;
//1.加载驱动
try {
// DriverManager.registerDriver(new Driver());会导致驱动注册两次,源代码中静态代码块包含注册驱动的语句,当Driver类被加载时会注册驱动
//加上以上语句手动注册一次,会注册驱动两次
Class.forName("com.mysql.jdbc.Driver");//加载Driver类,类一旦被加载,就会执行静态代码块,注册驱动
//2.获得连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&characterEncoding=utf8", "root", "1984");
//3.创建执行SQL语句的对象,并且执行SQL
//3,1创建执行SQL的对象
String sql = "insert into user values (null,'eee','123','张三')";
stmt = conn.createStatement();
//3.2执行SQL语句
int i = stmt.executeUpdate(sql);//返回所影响的行数,i>0说明操作成功
if (i > 0) {
System.out.println("保存成功!");
}
} catch(Exception e){
e.printStackTrace();
}finally{
//为了保证程序异常时,仍然可以正确释放资源,将释放资源放到finally代码块内(不管是否发生异常都会执行)
//4.释放资源
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();//遵循晚创建,早释放,虽然释放了,但还没有被垃圾回收机制回收掉
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;//手动置为null,垃圾回收机制会更早地回收对象
}
}
}
@Test
/**
* 修改操作
*/
public void demo2() {
Connection conn = null;
Statement stmt = null;
//1.加载驱动
try {
// DriverManager.registerDriver(new Driver());会导致驱动注册两次,源代码中静态代码块包含注册驱动的语句,当Driver类被加载时会注册驱动
//加上以上语句手动注册一次,会注册驱动两次
Class.forName("com.mysql.jdbc.Driver");//加载Driver类,类一旦被加载,就会执行静态代码块,注册驱动
//2.获得连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&characterEncoding=utf8", "root", "1984");
//3.创建执行SQL语句的对象,并且执行SQL
//3,1创建执行SQL的对象
String sql = "update user set username='qqq',password='234',name='刘三' where uid=4";
stmt = conn.createStatement();
//3.2执行SQL语句
int i = stmt.executeUpdate(sql);//返回所影响的行数,i>0说明操作成功
if (i > 0) {
System.out.println("修改成功!");
}
} catch(Exception e){
e.printStackTrace();
}finally{
//为了保证程序异常时,仍然可以正确释放资源,将释放资源放到finally代码块内(不管是否发生异常都会执行)
//4.释放资源
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();//遵循晚创建,早释放,虽然释放了,但还没有被垃圾回收机制回收掉
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;//手动置为null,垃圾回收机制会更早地回收对象
}
}
}
@Test
/**
* 删除操作
*/
public void demo3() {
Connection conn = null;
Statement stmt = null;
//1.加载驱动
try {
// DriverManager.registerDriver(new Driver());会导致驱动注册两次,源代码中静态代码块包含注册驱动的语句,当Driver类被加载时会注册驱动
//加上以上语句手动注册一次,会注册驱动两次
Class.forName("com.mysql.jdbc.Driver");//加载Driver类,类一旦被加载,就会执行静态代码块,注册驱动
//2.获得连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&characterEncoding=utf8", "root", "1984");
//3.创建执行SQL语句的对象,并且执行SQL
//3,1创建执行SQL的对象
String sql = "delete from user where uid=7";
stmt = conn.createStatement();
//3.2执行SQL语句
int i = stmt.executeUpdate(sql);//返回所影响的行数,i>0说明操作成功
if (i > 0) {
System.out.println("删除成功!");
}
} catch(Exception e){
e.printStackTrace();
}finally{
//为了保证程序异常时,仍然可以正确释放资源,将释放资源放到finally代码块内(不管是否发生异常都会执行)
//4.释放资源
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();//遵循晚创建,早释放,虽然释放了,但还没有被垃圾回收机制回收掉
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;//手动置为null,垃圾回收机制会更早地回收对象
}
}
}
@Test
/**
* 查询所有记录/一条记录
*/
public void demo4() {
Connection conn = null;
Statement stmt = null;
ResultSet rs=null;
//1.加载驱动
try {
// DriverManager.registerDriver(new Driver());会导致驱动注册两次,源代码中静态代码块包含注册驱动的语句,当Driver类被加载时会注册驱动
//加上以上语句手动注册一次,会注册驱动两次
Class.forName("com.mysql.jdbc.Driver");//加载Driver类,类一旦被加载,就会执行静态代码块,注册驱动
//2.获得连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&characterEncoding=utf8", "root", "1984");
//3.创建执行SQL语句的对象,并且执行SQL
//3,1创建执行SQL的对象
String sql = "select * from user";
//String sql="select * from user where uid=2";//一条记录
stmt = conn.createStatement();
//3.2执行SQL语句
rs=stmt.executeQuery(sql);
//if(rs.next()){//一条记录
while(rs.next()){
int uid=rs.getInt("uid");
String username=rs.getString("username");
String password=rs.getString("password");
String name=rs.getString("name");
System.out.println(uid+" "+username+" "+password+" "+name);
}
} catch(Exception e){
e.printStackTrace();
}finally{
//为了保证程序异常时,仍然可以正确释放资源,将释放资源放到finally代码块内(不管是否发生异常都会执行)
//4.释放资源
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();//遵循晚创建,早释放,虽然释放了,但还没有被垃圾回收机制回收掉
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;//手动置为null,垃圾回收机制会更早地回收对象
}
if (rs != null) {
try {
rs.close();//遵循晚创建,早释放,虽然释放了,但还没有被垃圾回收机制回收掉
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;//手动置为null,垃圾回收机制会更早地回收对象
}
}
}
}
抽取重复代码封装成工具类(使用静态成员)
import jdbc.utils.JDBCUtils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.Statement;
public class JDBCDemo3 {
@Test
/**
* 保存记录
*/
public void demo(){
Connection conn=null;
Statement stmt=null;
try {
//获得连接
conn= JDBCUtils.getConnection();
//创建执行SQL语句的对象
stmt=conn.createStatement();
String sql="insert into user values(null,'fff','123','王四')";
int i=stmt.executeUpdate(sql);
if(i>0){
System.out.println("保存成功");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.release(conn,stmt);
}
}
}
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* JDBC的工具类,想修改参数时,需要修改工具类的源代码,所以将参数提取到配置文件中,在工具类中只需要解析配置文件,在配置文件中修改参数
*/
public class JDBCUtils {
//static final,全局变量,随着类加载而产生,在程序运行过程中不允许被修改,修饰配置信息
private static final String driverClass;
private static final String url;
private static final String username;
private static final String password;
static{//静态代码块,无论类实例多少次,只执行一次,只能调用静态成员
//加载属性文件并解析
Properties props=new Properties();
//如何获得属性文件的输入流
//通常使用类的加载器的方式进行获取:FileInputStream("src/jdbc.properties"):在java项目中可行,Web项目不可行,没有src路径
InputStream inputStream=JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
try {
props.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
// driverClass="com.mysql.jdbc.Driver";
// url="jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&characterEncoding=utf8";
// username="root";
// password="1984";
driverClass=props.getProperty("driverClass");
url=props.getProperty("url");
username=props.getProperty("username");
password=props.getProperty("password");
}
/**
* 注册驱动的方法
*/
public static void loadDriver() throws ClassNotFoundException {
Class.forName(driverClass);
}
/**
* 获得连接的方法
* 静态方法,静态方法不能直接访问同一个类中的非静态成员,只能直接调用同一个类中的静态成员
* 静态方法中不能使用this,this代表当前类实例,static代表类共享冲突,静态变量与静态方法属于一类,可直接访问
* 通过对象实例化后,对象.成员方法的方式访问非静态成员
*/
public static Connection getConnection() throws Exception {
loadDriver();
Connection conn= DriverManager.getConnection(url, username, password);
return conn;
}
/**
* 资源释放
*/
public static void release(Connection conn, Statement stmt){
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();//遵循晚创建,早释放,虽然释放了,但还没有被垃圾回收机制回收掉
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;//手动置为null,垃圾回收机制会更早地回收对象
}
}
public static void release(Connection conn, Statement stmt, ResultSet rs){
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();//遵循晚创建,早释放,虽然释放了,但还没有被垃圾回收机制回收掉
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;//手动置为null,垃圾回收机制会更早地回收对象
}
if (rs != null) {
try {
rs.close();//遵循晚创建,早释放,虽然释放了,但还没有被垃圾回收机制回收掉
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;//手动置为null,垃圾回收机制会更早地回收对象
}
}
}
配置文件
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&characterEncoding=utf8
username=root
password=1984
SQL注入的漏洞:一个网站需要登陆的功能,需要输入用户名和密码进行登陆,SQL注入漏洞是指已知用户名,不知道密码的情况下,也可以登陆到系统中,对账号进行破坏,原因:在输入用户名时输入了SQL语句的关键字
import jdbc.utils.JDBCUtils;
import org.junit.Test;
import java.sql.*;
/**
* 演示JDBC注入的漏洞
*/
public class JDBCDemo4 {
@Test
/**
* 测试SQL注入漏洞的方法:注入漏洞,在输入用户名时输入了SQL语句的关键字
*/
public void demo(){
//已知用户名,对账号进行破坏,显示登陆成功
//boolean flag=JDBCDemo4.login("aaa' or '1=1","111hdjgjio");//输入了SQL的关键字or
//String sql="select * from user where username = 'aaa' or '1=1'and password ='111hdjgjio'";//true or false为true
//boolean flag=JDBCDemo4.login("aaa' -- ","111hdjgjio");
//String sql="select * from user where username = 'aaa' -- 'and password ='111hdjgjio'";//--是注释,后面被注释掉了
boolean flag=JDBCDemo4.login("aaa","111");
if(flag)
System.out.println("登陆成功");
else
System.out.println("登陆失败");
}
/**
* 判断是否登陆成功
* 根据输入的用户名和密码到数据库中查询,查询到了就返回,没查询到则登陆失败(JDBC的查询操作)
*/
public static boolean login(String username,String password){
Connection conn = null;
Statement stmt = null;
ResultSet rs=null;
boolean flag=false;
try {
conn = JDBCUtils.getConnection();
//创建执行SQL的语句
stmt = conn.createStatement();
//编写SQL语句
String sql="select * from user where username = '" +username +"'and password ='"+password+"'";
//执行SQL语句
rs=stmt.executeQuery(sql);
//判断结果集中是否有数据(查询到了)
if(rs.next()){
flag=true;
}
else
flag=false;
} catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(conn,stmt,rs);
}
return flag;
}
}
SQL注入漏洞的解决:根本在SQL语句的拼接上,Statement对象执行SQL语句时,SQL语句是字符串拼接的,因此不能使用Statement对象,而使用PreparedStatement(预处理对象):使用占位符将SQL语句中变量的地方占住,SQL语句格式固定,再传来的SQL的关键字也不识别了,将关键字当作普通的字符串处理
/**
* 避免SQL注入漏洞的方法
*/
public static boolean login2(String username, String password) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
boolean flag=false;
try {
conn = JDBCUtils.getConnection();
//编写SQL语句
String sql = "select * from user where username = ? and password =?";
//预处理SQL
pstmt=conn.prepareStatement(sql);
//设置参数
pstmt.setString(1,username);
pstmt.setString(2,password);
//执行SQL语句,SQL已经预处理了,执行时不需要传入SQL
rs = pstmt.executeQuery();
//判断结果集中是否有数据(查询到了)
if (rs.next()) {
flag = true;
} else
flag = false;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.release(conn, pstmt, rs);
}
return flag;
}
@Test//注解
/**
* 保存数据
*/
public void demo1() {
Connection conn=null;
PreparedStatement pstmt=null;
try {
//获得连接
conn= JDBCUtils.getConnection();
//编写SQL语句
String sql="insert into user values (null,?,?,?)";
//预处理SQL
pstmt= conn.prepareStatement(sql);
//设置参数的值
pstmt.setString(1,"111");//或直接setObject()
pstmt.setString(2,"ddd");
pstmt.setString(3,"李四");
//执行SQL
int i= pstmt.executeUpdate();
if(i>0){
System.out.println("保存成功!");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.release(conn,pstmt);
}
}
数据库连接池(一块内存):很多用户通过服务器端的程序,调用数据访问对象,创建连接,跟数据库进行交互,用户每次请求都需要向数据库获得连接(用户访问请求->JDBC(从数据库连接池获得连接)->数据库)
访问时不需要再创建连接,而是从连接池中拿一个出来,用完以后再归还
C3P0连接池
package jdbcDemo3;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import utils.JDBCUtils;
import org.junit.Test;
import utils.JDBCUtils2;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* 连接池的测试类
*/
public class DataSourceDemo1 {
@Test
/**
* 使用配置文件的方式,属性文件或xml文件(放在src路径下,默认就会查找)
* 将创建连接池抽取成工具类
*
*/
public void demo2(){
//获得连接
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
try {
conn=JDBCUtils2.getConnection();
//编写SQL
String sql="select * from user";
//预编译SQL
pstmt=conn.prepareStatement(sql);
//设置参数
//执行SQL
rs= pstmt.executeQuery();
while(rs.next()){
int uid=rs.getInt("uid");
String username=rs.getString("username");
String password=rs.getString("password");
String name=rs.getString("name");
System.out.println(uid+" "+username+" "+password+" "+name);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//连接池内部已经增强了Connection的close(),原有的销毁变成归还了,不用管归还的操作
JDBCUtils2.release(conn,pstmt,rs);
}
}
@Test
/**
* 手动设置连接池
*/
public void demo1(){
//获得连接
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
try {
//创建连接池
ComboPooledDataSource dataSource=new ComboPooledDataSource();
//设置连接池的相关参数
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&characterEncoding=utf8");
dataSource.setUser("root");
dataSource.setPassword("1984");
dataSource.setMaxPoolSize(20);
dataSource.setInitialPoolSize(3);
//从连接池中获得连接
conn= dataSource.getConnection();
//编写SQL
String sql="select * from user";
//预编译SQL
pstmt=conn.prepareStatement(sql);
//设置参数
//执行SQL
rs= pstmt.executeQuery();
while(rs.next()){
int uid=rs.getInt("uid");
String username=rs.getString("username");
String password=rs.getString("password");
String name=rs.getString("name");
System.out.println(uid+" "+username+" "+password+" "+name);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//连接池内部已经增强了Connection的close(),原有的销毁变成归还了,不用管归还的操作
JDBCUtils2.release(conn,pstmt,rs);
}
}
}
抽取工具类
/**
* 抽取工具类
*/
public class JDBCUtils2 {
private static final ComboPooledDataSource dataSource = new ComboPooledDataSource();//,保证一个程序内,连接池只被初始化一次
/**
* 获得连接的方法
*/
public static Connection getConnection() throws Exception {
Connection conn = dataSource.getConnection();
return conn;
}
}
xml文件
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbctest</property>
<property name="user">root</property>
<property name="password">1984</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
</c3p0-config>