文章目录
一、JDBC介绍
JDBC(Java Data Base Connection)是Java中提供的一套标准的应用编程接口,用来连接Java编程语言和数据库。
常用组件:
-
DriverManger:数据库驱动程序的管理类,匹配Java程序到数据库驱动的协议(MySQL,SQL server),识别出某个子协议与数据库服务器进行通信
-
Driver(驱动):处理与数据库服务器的通信的,主要是- DriverManger管理对象,指具体的连接驱动(MySQL驱动)
-
Connection:接口形式,接触数据库的所有方法,以及数据库对象的上下文
-
Statement:创建用于接口的对象将所有的SQL提交的数据库(SQL操作都是通过该对象)
-
ResultSet:使用SQL语句执行查询后,返回的结果都是保存在该对象中
-
SqlExeption:这个类是数据库应用程序中的任何错误
-
注意:JDBC是第三方提供的库,要使用JDBC必须先引入JDBC相关jar包(mysql-connection-java),IDEA上直接编写(Array List)等可以直接使用,这是JDK提供的工具类。
-
maven:
使用maven来构建程序,主要作用是用来统一管理第三方依赖jar,包括jar下载,更新jar…等。操作使用maven可以更加方便。mybatis,spring,springMVC,springboot框架都需要用到maven。
二、JDBC操作流程
编码步骤:
- 引入驱动包路径Class.fromName
- 连接数据库 DriveManager.getConnection(url,name,password) 获取Connection对象
- 获取Statement对象,通过Connection对象来获取
- 通过Statement执行SQL
- 查询操作获取ResultSet结果集对象
- 关闭资源
public class JDBCDemo {
public static void main(String[] args) {
try{
//1.加载MySQL驱动 com.mysql.jdbc.Driver
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//连接MySQL数据库
/**
* 连接数据库需要的信息:
* url:"jdbc:mysql://localhost:3306/库名"
* username:"用户名"
* password:"密码"
*/
try {
//2.获取connection对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/school?characterEncoding=utf8","root","123456");
//?characterEncoding=utf8用来设置数据库的编码(如果在插入时,表无法识别给定的字符,在这里设置编码格式可以解决)
System.out.println("数据库登陆成功...");
//3.获取Statement对象
Statement statement = connection.createStatement();
//Statement还有两种形式:
//connection.prepareStatement(); connection.prepareCall();
String sql = "select * from student where SID=5";
//4.(操作)查询操作使用executeQuery,修改操作使用execute
statement.executeQuery(sql);
ResultSet resultSet = statement.executeQuery(sql);//结果集
//处理result结果集
/**
* resultSet返回的是一个结果集,可以包含多个对象。
* 多个对象中,每调用 next()方法,处理结果集中一行数据。
*/
while(resultSet.next()){
String sid = resultSet.getString("SID");
//支持返回各种类型,如:int,double...
String name = resultSet.getString("Sname");
String age = resultSet.getString("Sage");
String sex = resultSet.getString("Ssex");
System.out.println("ID:"+sid+",姓名:"+name+",年龄:"+age+",性别:"+sex);
}
//关闭资源
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
实现增删改查
public class JDBCDemo {
private static Connection connection;
//连接数据库
public static void getConnection() {
try {
//1.加载MySQL驱动 com.mysql.jdbc.Driver
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
//2.获取connection对象
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/school?characterEncoding=utf8", "root", "123456");
System.out.println("数据库登陆成功...");
} catch (SQLException e) {
e.printStackTrace();
}
}
//关闭资源
public static void close(){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//查询操作
public static void selectUserById(String id){
getConnection();
try {
Statement statement = connection.createStatement();
String sql = "select * from student where SID= '"+id+"'";
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
String sid = resultSet.getString("SID");
String name = resultSet.getString("Sname");
String age = resultSet.getString("Sage");
String sex = resultSet.getString("Ssex");
System.out.println("ID:"+sid+",姓名:"+name+",年龄:"+age+",性别:"+sex);
}
//关闭资源
close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//变更操作
public static void updateNameById(String id,String name){
getConnection();
try {
Statement statement = connection.createStatement();
String sql = "update student set Sname= '"+name+"' where SID= '"+id+"'";
//通过executeUpdate修改数据
int i = statement.executeUpdate(sql);
System.out.print("变更成功!");
close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//插入数据
public static void addUser(String Sname,int Sage){
getConnection();
try {
Statement statement = connection.createStatement();
String sql = "insert into Student (Sname,Sage) values('"+Sname+"','"+Sage+"')";
int i = statement.executeUpdate(sql);
System.out.print("插入成功!");
} catch (SQLException e) {
e.printStackTrace();
}
}
//删除数据
public static void remove(int left,int right){
getConnection();
try {
Statement statement = connection.createStatement();
String sql = "delete from Student where SID>='"+left+"'and SID<='"+right+"'";
int i = statement.executeUpdate(sql);
System.out.print("已删除!");
} catch (SQLException e) {
e.printStackTrace();
}
}
public static String login(String Sname,String SID){
getConnection();
try {
Statement statement = connection.createStatement();
String sql = "select * from Student where Sname = '"+Sname+"' and SID = '"+SID+"'";
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
return "success";
} else {
return "fail";
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
close();
}
return "fail";
}
public static void main(String[] args) {
selectUserById("5");//查询信息
updateNameById("3","妲己");//更改名称
addUser("伽罗",20);//添加数据
//正常登录
String Sname = "伽罗";
String SID = "12";
String login = login(Sname, SID);
System.out.println(login);//判断是否可以登录
remove(14,14);//删除数据
}
}
三、SQL注入问题
通过输入的参数和SQL进行拼接的查询数据库,为了绕开正常SQL逻辑而添加一些额外的SQL信息来达到访问数据库的目的。
以登录为例:
public static String login(String Sname,String SID){
getConnection();
try {
Statement statement = connection.createStatement();
String sql = "select * from Student where Sname = '"+Sname+"' and SID = '"+SID+"'";
//select * from student where Sname = XXX or 1=1 and SID =XXX
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
return "success";
} else {
return "fail";
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
close();
}
return "fail";
}
- 通过 Sname(“伽罗”) 和 passwd(12)能够正常登陆。
但是现在 Sname= “伽罗 or 1=1” 和SID(错误信息) 是可以完成登陆的,当前拼接的SQL信息为select * from student where Sname = XXX or 1=1 and SID =XXX
,改变了SQL意义。
//测试
String Sname = "伽罗 or 1=1";
String SID = "12";
String login = login(Sname,SID);
如何解决SQL注入问题?
使用PreparedStatement来解决SQL注入的问题,
将sql和参数分别传给数据库服务端,先进行预编译,会先进行SQL语法的检查,在语法检测成功后才进行执行,如果语法检查不通过则无法执行。
//使用preparedStatement来实现,其中SQL中参数用占位符
String sql1 = "select * from student where Sname = ? and SID = ?";
PreparedStatement statement = connection.prepareStatement(sql1);
//往SQL中占位符给定数据 setString第一个参数指的是占位符的位置,第一个位置为1
statement.setString(1,Sname);
statement.setString(2,SID);
ResultSet resultSet1 = statement.executeQuery(sql1);
PreparedStatement和Statement区别?:
- SQL注入问题(安全性问题)
Statement是将拼接的SQL在服务端直接执行,无法避免的SQL注入问题;PreparedStatement将SQL和参数分别传递给服务单,先进行预编译,可以检查SQL语法,可以避免SQL注入问题。 - 效率不同
PreparedStatement先编译,再将编译结果拿来执行;Statement是在服务端需要进行编译执行。PreparedStatement效率高于Statement。
四、JDBC处理事务
在JDBC下和事务相关的操作都封装在Connection提供的相应操作
connection.commit();//提交事务
connection.rollback();//事务回滚
connection.setSavepoint();//保存点
Savepoint point1 = connection.setSavepoint("point1");
connection.rollback(point1);//回滚到指定的保存点
五、连接池
(一) 介绍
- 在JDBC的操作过程中,每次进行SQL操作都需要先进行连接操作,SQL执行完毕后需要关闭连接,在数据库的并发量比较高的情况下,频繁的进行数据库的连接和关闭是比较消耗系统性能。
- 为了解决对资源的频繁的连接关闭问题,可以通过连接复用来解决,就引入了连接池的概念。
(二)连接池的原理
- 在创建连接池时,就先初始化指定数量的连接(initnum),将建立的连接放入到池中。当有新的SQL操作请求时,可以在池中获取一个空闲的连接来执行SQL操作,当SQL执行完成后将连接放入池中重复使用。
- 最大连接数(maxNum),
当池中的连接都在使用时,而且连接数没有到达最大的时候,就可以新创建一个连接来执行新的SQL请求操作;
如果已经达到了最大的连接数且所有的连接都在使用时可以将新连接进行拒绝或者是通过缓存先存储等有空闲连接在此执行,有空闲时间阈值,当一个连接空闲的时间超过阈值,就自动进行销毁。
(三)连接池的优势
- 复用资源
- 对资源的进行统一的管理
- 提高了系统资源的利用率
- 常见连接池:c3p0,Druid,dbcp…
使用连接池步骤
引入依赖 -> 配置连接池 -> 使用连接池
- 引入依赖
<!--c3p0连接池-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
- 创建c3p0-config.xml配置文件
在resources资源路径下增加在意c3p0的配置
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!--配置连接池mysql-->
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/school</property>
<property name="user">root</property>
<property name="password">123456</property>
<!-- 初始化连接数 -->
<property name="initialPoolSize">10</property>
<!--最大空闲时间,多少秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime">30</property>
<!--连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize">100</property>
<!-- 最小连接数 -->
<property name="minPoolSize">10</property>
</named-config>
</c3p0-config>
连接池的使用
public class c3p0Demo {
public static void main(String[] args) {
//设置数据源DataSource
ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
try {
//获取connection
Connection connection = dataSource.getConnection();
//获取Statement对象
Statement statement = connection.createStatement();
String sql = "select * from student where SID = 1";
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
String sid = resultSet.getString("SID");
String name = resultSet.getString("Sname");
String age = resultSet.getString("Sage");
String sex = resultSet.getString("Ssex");
System.out.println("ID:"+sid+",姓名:"+name+",年龄:"+age+",性别:"+sex);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}