JDBC概述
驱动
为了能让程序员利用java程序操作数据库,数据库厂商提供了一套jar包,通过导入这个jar包,就可以直接调用里边的方法操作数据库,这个jar包称之为驱动
两个数据库驱动互不兼容:
行业中有很多种数据库,要使用这么多数据库需要学习很多的数据库驱动,对于程序员来说学习成本非常高;想让java程序兼容数据库,所有的数据库驱动都实现了jdbc这套接口
JDBC简介
JDBC全称:java Data Base Connectivity(java数据库连接),它主要由接口组成
组成jdbc的两个包:java.sql javax.sql包
开发jdbc应用需要以上两个包的支持外,还需要导入相应的jdbc的数据库实现驱动
不仅需要jdbc接口,还需要驱动这个实现,驱动就是对jdbc的接口的一些实现
ctrl shift o ------导包
public class Demo1 {
public static void main(String[] args) throws SQLException {
//获取数据库驱动
DriverManager.registerDriver(new Driver());
//创建数据库连接
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
//获取传输器
Statement stat=conn.createStatement();
//利用传输器传输sql,并获取返回结果
ResultSet rs=stat.executeQuery("select * from emp");
//遍历结果
while(rs.next()) {
int id=rs.getInt("id");
String name=rs.getString(2);
System.out.println("id:"+id+">>"+"name:"+name);
}
//关闭资源
//后创建的先关闭
rs.close();
stat.close();
conn.close();
}
}
public class Demo2 {
public static void main(String[] args){
Connection conn=null;
Statement stat=null;
ResultSet rs=null;
try {
//获取数据库驱动
//手动注册一次,底层还注册一次,总共两次注册驱动,应该只注册一次即可
//代码与mysql驱动包包名绑死,如果切换数据则需要修改包名代码
//DriverManager.registerDriver(new Driver());
Class.forName("com.mysql.jdbc.Driver");
//创建数据库连接
//Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?user=root&password=root");
//获取传输器
stat=conn.createStatement();
//利用传输器传输sql,并获取返回结果
rs=stat.executeQuery("select * from emp");
//遍历结果
while(rs.next()) {
int id=rs.getInt("id");
String name=rs.getString(2);
System.out.println("id:"+id+">>"+"name:"+name);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
//关闭资源
//后创建的先关闭
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
rs=null;
}
}
if(stat!=null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
stat=null;
}
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
conn=null;
}
}
}
}
}
public class Demo3 {
//添加数据库的sql
@Test
public void add() {
Connection conn=null;
Statement stat=null;
try {
//注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//创建数据库连接
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
//获取传输器
stat=conn.createStatement();
//利用传输器传输sql
int count=stat.executeUpdate("insert into emp values(null,'曹阳',7)");
if(count>0) {
System.out.println("插入成功,受到影响的行数为:"+count);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
if(conn!=null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
conn=null;
}
}
if(stat!=null) {
try {
stat.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
stat=null;
}
}
}
}
//修改信息的sql
@Test
public void update() {
Connection conn=null;
Statement stat=null;
try {
//注册驱动,创建连接
conn=JDBCUtils.getConnection();
//获取传输器
stat=conn.createStatement();
//删除
int count=stat.executeUpdate("update emp set name='李帅' where id=7");
if(count>0) {
System.out.println("更新成功,"+"受影响的行数为:"+count);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
//关闭资源
JDBCUtils.close(conn, stat, null);
}
}
//删除sql
@Test
public void delete() {
Connection conn=null;
Statement stat=null;
try {
//注册驱动,创建连接
conn=JDBCUtils.getConnection();
//获取传输器
stat=conn.createStatement();
//删除操作
int count=stat.executeUpdate("delete from emp where id=8");
if(count>0) {
System.out.println("删除成功,"+"受影响的行数为:"+count);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
//关闭资源
JDBCUtils.close(conn, stat, null);
}
}
}
登录功能的实现:
public class Login {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请输入用户名:");
String username=sc.nextLine();
System.out.println("请输入密码:");
String password=sc.nextLine();
//testLogin(username,password);
preparedTestLogin(username,password);
}
private static void preparedTestLogin(String username, String password) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=JDBCUtils.getConnection();
//发送sql主干
ps=conn.prepareStatement("select * from user where name=? and password=?");
//发送参数
ps.setString(1, username);
ps.setString(2, password);
//通知数据库服务器执行sql
rs=ps.executeQuery();
if(rs.next()) {
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
} catch (Exception e) {
// TODO: handle exception
}finally {
//关闭资源
JDBCUtils.close(conn, ps, rs);
}
}
private static void testLogin(String username, String password) {
Connection conn=null;
Statement stat=null;
ResultSet rs=null;
try {
conn=JDBCUtils.getConnection();
stat=conn.createStatement();
rs=stat.executeQuery("select * from user where name='"+username+"' and password='"+password+"'");
if(rs.next()) {//若果为true证明能查询到用户名和密码,可以登录(name为曹阳'#,password为空也是成功的,sql注入攻击)
System.out.println("登录成功");
}else {
System.out.println("登录失败...");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//关闭资源
JDBCUtils.close(conn, stat, rs);
}
}
}
sql注入攻击
由于sql语句是由前台参数与后台语句拼接而来,在用户传入参数位置可能会输入数据库的关键字。这些关键字可能使得sql语句的语义发生变化,从而达到一些特殊的效果,这种操作方式称之为sql注入攻击
解决方案:
1.利用statement子接口preparedStatement可以有效防止sql注入攻击
PreparedStatement概述:
preparedStatement是statement的一个子接口
具有预编译功能
发送sql的步骤:
1.先将sql语句的主干部分发送到数据库服务器中,sql语句到达服务器中时,会立即变成一段机器码(二进制数据)这个机器码不能被操作
2.再将sql语句中的参数发送到数据库服务器,这些参数到达数据库中时只会作为纯文本内容使用
preparedStatement的优势:
参数可以单独传入,避免sql语句的拼接错误。
拥有预编译功能,可以防止sql注入攻击
javautils工具包
public class JDBCUtils {
private JDBCUtils() {}
//静态对象
public static Properties prop=new Properties();
static{
//获取类加载器JDBCUtils.class.getClassLoader()
//通过类加载器获取src目录,getResource()
//getResource()会得到从盘符到src目录的路径
//直接在括号中书写文件名称即可得到文件路径
//getPath()可将url数据转为String类型的数据
try {
prop.load(new FileInputStream(new File(JDBCUtils.class.getClassLoader().getResource("conf.properties").getPath())));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
//创建连接
public static Connection getConnection() throws Exception {
//注册数据库驱动
Class.forName(prop.getProperty("driver"));
//Class.forName("com.mysql.jdbc.Driver");
//创建数据库连接
return DriverManager.getConnection(prop.getProperty("url"), prop.getProperty("user"), prop.getProperty("password"));
//return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
}
//关闭资源
public static void close(Connection conn,Statement stat,ResultSet rs) {
if(conn!=null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
conn=null;
}
}
if(stat!=null) {
try {
stat.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
stat=null;
}
}
if(rs!=null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
rs=null;
}
}
}
}
conf.properties
批处理:
1.批处理机制
a.在sql语句的执行过程中,每个jdbc六部分只操作一个语句,如果有多个sql要执行,会造成很大的代码冗余,书写不便利。
b.可以将这些sql语句放入一个jdbc的批处理中,一同发送给数据库服务器执行
statement批处理和preparedStatement批处理
a.statement批处理
package cn.tedu.batch;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import cn.tedu.utils.JDBCUtils;
//批处理statement
/*
create table t1(id int,name varchar(20))
insert into t1 values(1,'鸣人')
insert into t1 values(2,'佐助')
insert into t1 values(3,'小音')
insert into t1 values(1,'鞋')
* */
public class StateBatchDemo1 {
public static void main(String[] args) {
Connection conn=null;
Statement stat=null;
ResultSet rs=null;
try {
conn=JDBCUtils.getConnection();
stat=conn.createStatement();
//添加sql到批处理中
stat.addBatch("create table t1(id int,name varchar(20))");
stat.addBatch("insert into t1 values(1,'鸣人')");
stat.addBatch("insert into t1 values(2,'佐助')");
stat.addBatch("insert into t1 values(3,'小音')");
stat.addBatch("insert into t1 values(4,'鞋')");
//通知数据库服务器执行批处理
stat.executeBatch();
System.out.println("Statement批处理执行成功");
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(conn, stat, rs);
}
}
}
preparedstatement批处理:
package cn.tedu.batch;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import cn.tedu.utils.JDBCUtils;
/**
*@author 作者:
*@version 创建时间:2021年1月13日下午1:38:32
*@description 描述:批处理preparestatement
*/
public class PreparedBatchDemo1 {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=JDBCUtils.getConnection();
ps=conn.prepareStatement("insert into t1 values(?,?)");
for (int i = 0; i < 10000; i++) {
//添加sql参数到批处理中
ps.setInt(1, i);
ps.setString(2, "name"+i);
ps.addBatch();
if(i%1000==0) {
//执行批处理
ps.executeBatch();
//清空批处理
ps.clearBatch();
System.out.println("执行完毕,当前批次数为:"+i/1000);
}
}
//循环可能有不满一千的数据,通过本句来执行
ps.executeBatch();
System.out.println("preparestatement执行完毕");
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(conn, ps, rs);
}
}
}
preparestatement的特点:
优点:
1.具有预编译功能
2.将sql主干预留在数据库服务器中,不必重复发送sql语句
3.每次仅发送sql的参数部分,执行效率较高
缺点:
1.只能执行同一语义的sql语句
statement特点:
优点:
1.可以执行不同语义的sql
缺点:
1.没有预编译功能
2.每次都会将完整的sql语句发送到数据库服务器中
3.无法预留sql语句在服务器中,执行效率较低
连接池:
为什么创建连接池:
一般数据库连接缺点:
用户每次请求都要向数据库获取连接,而数据库创建连接一般需要消耗相对较大的资源,创建时间也较长,假设网站一天需要创建10万次连接,极大的浪费数据库的资源,极易造成数据库服务器内存溢出,宕机
连接池的额实现:
连接池概念:
在使用JDBC的过程中,连接池的使用占用资源较少,而创建连接和销毁连接占用资源较多,为了减少在服务器和数据库服务器之间,创建连接和销毁连接的过程,可以使用的连接池代替原来的jdbc有关连接的操作
连接池原理:
连接池在服务器启动的时候,会自动向数据库服务器索要一批链接,这些连接会保留在连接池中,用户需要访问数据库时,可以从连接池中取出连接,使用完成后,可以归还连接,从而省去创建和销毁的过程
package cn.tedu.pool;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;
import javax.sql.DataSource;
/**
*@author 作者:
*@version 创建时间:2021年1月13日下午2:40:28
*@description 描述:连接池底层源码
*/
public class MyPool implements DataSource{
//当前类加载时初始化一些连接
public static LinkedList<Connection> pool=new LinkedList<Connection>();
static {
try {
Class.forName("com.mysql.jdbc.Driver");
for (int i = 0; i <5; i++) {
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
pool.add(conn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Connection getConnection() throws SQLException {
//获取连接
if(pool.size()==0) {
for (int i = 0; i <5; i++) {
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
pool.add(conn);
}
}
Connection conn=pool.remove(0);
System.out.println("从池中取出连接,池中还剩"+pool.size()+"个连接");
return conn;
}
//归还连接
public void returnConnection(Connection conn) throws SQLException {
if(conn!=null && !conn.isClosed()) {
pool.add(conn);
System.out.println("归还连接,池中还剩"+pool.size()+"个连接");
}
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
package cn.tedu.pool;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
*@author 作者:
*@version 创建时间:2021年1月13日下午2:59:31
*@description 描述:测试连接池
*/
public class TestPool {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
MyPool pool=new MyPool();
try {
conn=pool.getConnection();
ps=conn.prepareStatement("select * from exam where id=?");
ps.setInt(1, 1);
rs=ps.executeQuery();
while (rs.next()) {
String name=rs.getString("name");
System.out.println("name:>>"+name);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(conn!=null) {
try {
pool.returnConnection(conn);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
conn=null;
}
}
if(ps!=null) {
try {
ps.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
ps=null;
}
}
if(rs!=null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
rs=null;
}
}
}
}
}
DBCP的连接使用
package cn.tedu.pool;
import java.io.File;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
/**
*@author 作者:
*@version 创建时间:2021年1月13日下午3:13:11
*@description 描述:dbcp连接池测试使用
*/
public class DBCPDemo1 {
public static void main(String[] args) {
Connection conn=null;
Statement stat=null;
ResultSet rs=null;
/*BasicDataSource source=new BasicDataSource();
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql://localhost:3306/mydb");
source.setUsername("root");
source.setPassword("root");*/
//利用工厂生产一个DBCP数据源对象
Properties prop=new Properties();
try {
prop.load(new FileInputStream(new File(DBCPDemo1.class.getClassLoader().getResource("dbcp.properties").getPath())));
BasicDataSourceFactory factory=new BasicDataSourceFactory();
DataSource source=factory.createDataSource(prop);
conn=source.getConnection();
stat=conn.createStatement();
rs=stat.executeQuery("select * from exam");
while (rs.next()) {
int id=rs.getInt(1);
String name=rs.getString(2);
System.out.println("id:"+id+">>name"+name);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(conn!=null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
conn=null;
}
}
if(stat!=null) {
try {
stat.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
stat=null;
}
}
if(rs!=null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
rs=null;
}
}
}
}
}
dbcp.properties配置
c3p0的测试使用
package cn.tedu.pool;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
*@author 作者:
*@version 创建时间:2021年1月13日下午3:50:32
*@description 描述:c3p0连接池测试使用
*/
public class C3P0Demo1 {
public static void main(String[] args) {
Connection conn=null;
Statement stat=null;
ResultSet rs=null;
ComboPooledDataSource source=new ComboPooledDataSource();
try {
/*source.setDriverClass("com.mysql.jdbc.Driver");
source.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
source.setUser("root");
source.setPassword("root");*/
conn=source.getConnection();
stat=conn.createStatement();
rs=stat.executeQuery("select * from exam");
while (rs.next()) {
String name=rs.getString("name");
System.out.println("name:>>"+name);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(conn!=null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
conn=null;
}
}
if(stat!=null) {
try {
stat.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
stat=null;
}
}
if(rs!=null) {
try {
//归还连接
rs.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}finally {
rs=null;
}
}
}
}
}