一.JDBC概述
JDBC全称为:Java DataBase Connectivity(Java数据库连接)
二.JDBC入门
1.搭建开发环境
#创建数据库
create database jdbctest;
use jdbctest;
create table user(
uid int primary key auto_increment,
username varchar(20),
password varchar(20),
name varchar(20)
);
insert into user values (null,'aaa','111','张三');
insert into user values (null,'bbb','222','李四');
insert into user values (null,'ccc','333','王五');
2.编写程序,在程序中加载数据库驱动
//DriverManager.registerDriver(new Driver());
Class.forName("com.mysql.jdbc.Driver");
3.建立连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest","root","123456");
4.创建用于向数据库发送SQL的statement对象
String sql = "select * from user";
Statement stmt = conn.createStatement();
5.从代表结果集的ResultSet中取出数据
ResultSet result = stmt.executeQuery(sql);
while(result.next()){
int uid = resultSet.getInt("uid");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
String name = resultSet.getString("name");
System.out.println(uid+" "+username+" "+password+" "+name);
}
6.断开与数据库的连接,并释放相关资源
result.close();
stmt.close();
conn.close();
三.JDBC的API
1.DriverManager驱动管理类
-
主要作用
-
注册驱动
-
实际开发中注册驱动会使用如下的方式:
Class.forName("com.mysql.jdbc.Driver");
-
因为之前的方式会导致驱动注册两次
-
-
获得连接
Connection getConnection(String url,String username,String password);
- url写法:jdbc:mysql://localhost:3306/jdbc
- Jdbc:协议
- mysql:子协议
- localhost:主机名
- 3306:端口号
- url简写(主机为本机):
jdbc:mysql:///jdbc
- url写法:jdbc:mysql://localhost:3306/jdbc
-
2.Connection连接对象
- 主要作用
- 创建执行SQL语句的对象
Statement createStatement()
执行SQL语句,有SQL注入的漏洞存在PreparedStatement prepareStatement(String sql)
预编译SQL语句,解决SQL注入的漏洞CallableStatement prepareCall(String sql)
执行SQL中存储过程
- 进行事务的管理
setAutoCommit(boolean autoCommit)
设置事务是否自动提交commit()
事务提交rollback()
事务回滚
- 创建执行SQL语句的对象
3.Statement执行SQL语句
- 主要作用
- 执行SQL语句
Boolean execute(String sql)
执行SQL,执行select语句返回true,否则返回falseResultSet executeQuery(String sql)
执行SQL中的select语句int executeUpdate(String sql)
执行SQL中的insert/update/delete语句
- 执行批处理操作
addBatch(String sql)
添加批处理executeBatch()
执行批处理clearBatch()
清理批处理
- 执行SQL语句
4.ResultSet结果集
- 结果集:其实就是查询语句(select)语句查询的结果的封装
- 主要作用:
- 结果集获取查询到的结果
next()
判断是否有下一行记录,如果有则将光标移到下一行- 针对不同的类型的数据可以使用getXXX()获取数据,通用的获取数据的方法
getObject()
- 结果集获取查询到的结果
四.JDBC的资源释放
-
JDBC程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet,Statement和Connection对象
-
特别是Connectiion对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
-
在finally中释放资源
if(rs!=null){ try{ rs.close(); }catch(SQLException e){ e.printStackTrace(); } rs = null;//close后,资源还没有被回收。如果手动将其置为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; }
五.JDBC的CRUD操作
1.向数据库中保存记录
public void demo1 () {
Connection conn = null;
Statement stmt = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest","root","123456");
stmt = conn.createStatement();
String sql = "insert into user values(null,'eee','123','孙柳')";
int i = stmt.executeUpdate(sql);
if(i>0) {
System.out.println("保存成功!");
}
}catch(Exception e) {
e.printStackTrace();
}finally {
//释放资源
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;
}
}
}
2.修改数据库中的记录
public void demo2() {
Connection conn = null;
Statement stmt = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获得连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest","root","123456");
//创建执行SQL语句的对象
stmt = conn.createStatement();
//编写SQL语句
String sql = "update user set username = 'qqq',password='456',name='赵六' where uid = 4";
//执行SQL语句
int i = stmt.executeUpdate(sql);
if(i>0) {
System.out.println("修改成功!");
}
}catch(Exception e) {
e.printStackTrace();
}finally {
//释放资源
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;
}
}
}
3.删除数据库中的记录
public void demo3() {
Connection conn = null;
Statement stmt = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获得连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest","root","123456");
//创建执行SQL语句的对象
stmt = conn.createStatement();
//编写SQL语句
String sql = "DELETE FROM user WHERE uid = 5";
//执行SQL语句
int i = stmt.executeUpdate(sql);
if(i>0) {
System.out.println("删除成功!");
}
}catch(Exception e) {
e.printStackTrace();
}finally {
//释放资源
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;
}
}
}
4.查询数据库中的记录
//查询多条记录
public void demo4() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///jdbctest","root","123456");
stmt = conn.createStatement();
String sql = "SELECT * FROM user";
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:"+uid+" username:"+username+" password:"+password+" name:"+name);
}
}catch(Exception e) {
e.printStackTrace();
}finally {
if(rs!=null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
rs = null;//close后,资源还没有被回收。如果手动将其置为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;
}
}
}
//查询一条记录
public void demo5() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///jdbctest","root","123456");
stmt = conn.createStatement();
String sql = "SELECT * FROM user where uid = 2";
rs = stmt.executeQuery(sql);
if(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:"+uid+" username:"+username+" password:"+password+" name:"+name);
}
}catch(Exception e) {
e.printStackTrace();
}finally {
if(rs!=null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
rs = null;//close后,资源还没有被回收。如果手动将其置为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;
}
}
}
六.JDBC的工具类的抽取
-
为了简化JDBC的开发,可以将一些重复的代码进行提取
-
定义JDBCUtils.java工具类
import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class JDBCUtils { 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(); //获得属性文件的输入流 //通常采用类加载器方式的进行获取 InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"); try { props.load(is); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } 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); } /** * 获得连接的方法 * @return * @throws SQLException * @throws ClassNotFoundException */ public static Connection getConnection() throws SQLException, ClassNotFoundException { loadDriver(); Connection conn = DriverManager.getConnection(url,username,password); return conn; } /** * 资源释放 * @param stmt * @param conn * @throws SQLException */ public static void release(Statement stmt, Connection conn) throws SQLException { 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; } } /** * 资源释放 * @param rs * @param stmt * @param conn */ public static void release(ResultSet rs,Statement stmt,Connection conn) { if(rs != null) { try { rs.close(); }catch(SQLException e) { e.printStackTrace(); } rs = null; } } }
-
定义Properties文件
driverClass=com.mysql.jdbc.Driver url=jdbc:mysql:///jdbctest username=root password=123456
-
定义测试文件
import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import org.junit.Test; public class JDBCDemo3 { @Test //保存记录 public void demo1() throws SQLException { Connection conn = null; Statement stmt = null; try { //获得连接 conn = JDBCUtils.getConnection(); //创建执行 stmt = conn.createStatement(); //编写sql语句 String sql = "insert into user values (null,'ppp','123','小六')"; int num = stmt.executeUpdate(sql); if(num > 0) { System.out.println("保存成功!"); } }catch(Exception e) { e.printStackTrace(); }finally { JDBCUtils.release(stmt, conn); } } }
七.SQL注入漏洞
- SQL漏洞产生
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Test;
public class JDBCDemo4 {
public static boolean login(String username, String password) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
boolean flag=false;
//获得连接
try {
conn = JDBCUtils.getConnection();
//创建执行
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 (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.release(rs,stmt, conn);
}
return flag;
}
@Test
public void demo1() {
if(JDBCDemo4.login("aaa ' or '1=1", "suibianxie")) {//SQL注入方式
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
}
}
-
SQL注入漏洞的解决
-
使用预处理对象preparedStatement
-
preparedStatement是Statement的子接口,它的实例对象可以通过调用
Connection.preparedStatement(sql)
方法获得,相对于Statement对象而言- PreparedStatement可以避免SQL注入的问题。
- Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。PreparedStatement可对SQL进行预编译,从而提高数据库的执行效率。
- 并且PreparedStatement对于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 rs = pstmt.executeQuery(); //判断结果集 if(rs.next()) { flag = true; }else { flag = false; } }catch(Exception e) { e.printStackTrace(); }finally { JDBCUtils.release(rs, pstmt, conn); } return flag; }
-
-
PreparedStatement的使用
-
保存数据
public void demo1() throws SQLException { 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, "qqq"); pstmt.setString(2, "123"); pstmt.setString(3, "张武"); //执行sql语句 int num = pstmt.executeUpdate(); if(num>0) { System.out.println("保存成功!"); } }catch(Exception e) { e.printStackTrace(); }finally { JDBCUtils.release(pstmt, conn); } }
-
修改数据
public void demo2() throws SQLException { Connection conn = null; PreparedStatement pstmt = null; try { //获得连接 conn = JDBCUtils.getConnection(); //编写SQL String sql = "update user set username = ?, password = ?, name = ? where uid =?"; //预处理SQL pstmt = conn.prepareStatement(sql); //设置参数 pstmt.setString(1, "www"); pstmt.setString(2, "123456"); pstmt.setString(3, "张柳"); pstmt.setInt(4, 9); //执行SQL int num = pstmt.executeUpdate(); if(num>0) { System.out.println("修改成功"); } }catch(Exception e) { e.printStackTrace(); }finally { JDBCUtils.release(pstmt, conn); } }
-
删除数据
public void demo3() throws SQLException { Connection conn = null; PreparedStatement pstmt = null; try { //获得连接 conn = JDBCUtils.getConnection(); //编写SQL String sql = "delete from user where uid = ?"; //预处理SQL pstmt = conn.prepareStatement(sql); //设置参数 pstmt.setInt(1, 9); //执行SQL int num = pstmt.executeUpdate(); if(num>0) { System.out.println("删除成功"); } }catch(Exception e) { e.printStackTrace(); }finally { JDBCUtils.release(pstmt, conn); } }
-
查询所有数据
public void demo4() throws SQLException { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { //获得连接 conn = JDBCUtils.getConnection(); //编写SQL String sql = "select * from user"; //预处理SQL pstmt = conn.prepareStatement(sql); //执行SQL rs = pstmt.executeQuery(); while(rs.next()) { System.out.println(rs.getInt("uid")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("name")); } }catch(Exception e) { e.printStackTrace(); }finally { JDBCUtils.release(rs,pstmt, conn); } }
-
八.数据库连接池
-
连接池
- 连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用
- 传统直接获取连接的缺点:用户每次请求都需要向数据库获得连接通常需要消耗相对大的资源,创建时间也比较长。访问量增大极易造成数据库服务器内存溢出。
-
C3P0连接池的使用
-
手动设置连接池操作
package com.imooc.jdbc; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import org.junit.Test; import com.mchange.v2.c3p0.ComboPooledDataSource; /** * 连接池的测试类 * @author wangzhe * */ public class DataSourceDemo1 { @Test /** * 手动设置连接池 */ public void demo1() throws PropertyVetoException { //创建连接池 ComboPooledDataSource dataSource = new ComboPooledDataSource(); //设置连接池参数 dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql:///jdbctest"); dataSource.setUser("root"); dataSource.setPassword("123456"); dataSource.setMaxPoolSize(20);//设置最大连接池个数 dataSource.setInitialPoolSize(3);//设置初始化连接池个数 //获得连接 Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { //获得连接 conn = dataSource.getConnection();//从连接池中获得 //编写SQL String sql = "select * from user"; //预编译SQL pstmt = conn.prepareStatement(sql); //设置参数:无参数 //执行SQL rs = pstmt.executeQuery(); while(rs.next()) { System.out.println(rs.getInt("uid")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("name")); } }catch(Exception e) { e.printStackTrace(); }finally { JDBCUtils.release(rs, pstmt, conn); } } }
-
使用配置文件的方式
//c3p0的配置文件:c3p0-config.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:///jdbctest</property> <property name="user">root</property> <property name="password">123456</property> <property name="initialPoolSize">5</property> <property name="maxPoolSize">20</property> </default-config> </c3p0-config> //使用配置文件的方式 @Test public void demo2() throws PropertyVetoException { //创建连接池 ComboPooledDataSource dataSource = new ComboPooledDataSource();//自动找classpath下的配置文件 //获得连接 Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { //获得连接 conn = dataSource.getConnection();//从连接池中获得 //编写SQL String sql = "select * from user"; //预编译SQL pstmt = conn.prepareStatement(sql); //设置参数:无参数 //执行SQL rs = pstmt.executeQuery(); while(rs.next()) { System.out.println(rs.getInt("uid")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("name")); } }catch(Exception e) { e.printStackTrace(); }finally { JDBCUtils.release(rs, pstmt, conn); } }
-