1、概述
JDBC:Java Data Base Connectivity Java 连接数据库。
JDBC:其实就是Java定义的一套和数据库建立连接的规范(接口),那么各家数据库厂商,想要Java去操作各家的数据库,必须实现这套接口,我们把数据库厂商写的这套实现类,称之为数据库驱动。
2、使用JDBC
(1)入门
/*1.导入数据库的驱动jar包,mysql版本不同jar包不同
2.加载驱动jar包
3.获取连接对象
4. 获取操作对象
5.开始操作
6.释放资源*/
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//MySQL 8.0版本
Class.forName("com.mysql.cj.jdbc.Driver");
String url="jdbc:mysql://localhost:3306/mytest1?serverTimezone=UTC";
//MySQL 5.0版本
Class.forName("com.mysql.jdbc.Driver");//可以省略不写
String url="jdbc:mysql://localhost:3306/mytest1";
String username="root";
String password="123456";
Connection conn = DriverManager.getConnection(url, username, password);
Statement statement = conn.createStatement();
String sql="insert into users values('wangwu','123456')";
int i = statement.executeUpdate(sql); //返回值是影响的行数
if(i>0){
System.out.println("插入成功");
}else{
System.out.println("插入失败");
}
//6.释放资源
conn.close();
statement.close();
}
}
(2)类的介绍
- class.forName((“com.mysql.cj.jdbc.Driver”);加载驱动,也可以省略不写。
package com.mysql.cj.jdbc;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
//查看源码可知 注册驱动这个操作是静态代码块来实现
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
- 类 DriverManager Java提供的 管理一组 JDBC 驱动程序的基本服务。
- 接口 Connection 试图建立到给定数据库 URL 的连接。
- 接口 Statement 用于执行静态 SQL 语句并返回它所生成结果的对象。
statement.execute(sql);//用来执行所有的SQL语句,如果第一个结果为 ResultSet 对象,则返回 true;如果
//其为更新计数或者不存在任何结果,则返回 false
statement.executeUpdate(sql);//用来执行DML语句 用来对表中数据进行增删改 返回值是影响的行数
statement.executeQuery(sql);//用来执行DQl语句 用来对表中数据进行查询
(3)结果集对象 ResultSet
- 概念:结果集对象,是我们执行了查询语句之后返回的一个查询结果对象。
- 用法:ResultSet对象具有指向其当前数据行的光标,最初光标被置于第一行之前,next方法将光标移动到下一行,在没有下一行时返回false,所以可以使用它来迭代结果集。
package com.westo.jdbc2;
import java.sql.*;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mytest1?serverTimezone=UTC", "root", "123456");
String sql=" select *from user";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
String username = resultSet.getString("username");//参数可以是表的字段名 也可以是序号(从1开始)
String password = resultSet.getString("password");
System.out.println(username + "==" + password);
}
connection.close();
statement.close();
resultSet.close();
}
}
(4)SQL注入问题
SQL注入:一些对数据库语法特别精通的人,可以利用一些数据库的语法规则,写出一些特殊sql的语句,来绕过数据库的验证。
package com.westo.jdbc2;
import java.sql.*;
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mytest1?serverTimezone=UTC", "root", "123456");
Statement statement = connection.createStatement();
//我们发现这样也可以登录成功
String username="1'or '1'='1";
String password="1'or '1'='1";
String sql="select *from user where username='"+username+"' and password='"+password+"' ";
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
connection.close();
statement.close();
resultSet.close();
}
}
PreparedStatement 预编译操作对象可以在安全方面防止SQL注入。
使用步骤
- conn.prepareStatement(sql);
- sql语句中的字段的值用?问号占位
- 给sql语句中的问号赋值
package com.westo.jdbc2;
import java.sql.*;
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mytest1?serverTimezone=UTC", "root", "123456");
String username="1'or '1'='1";
String password="1'or '1'='1";
//值用?号占位
String sql="select *from user where username=? and password=?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
System.out.println("登陆成功");
}else{
System.out.println("登录失败");
}
connection.close();
preparedStatement.close();
resultSet.close();
}
}
3、JDBC操作
(1)JDBC工具类
package com.westo.jdbc2;
import java.io.FileReader;
import java.sql.*;
import java.util.Properties;
public class JDBCutils {
private static String url;
private static String username;
private static String password;
static{
try {
//将基本信息放在配置文件中 从文件中读取
Properties properties = new Properties();
properties.load(new FileReader("JDBC.properties"));
Class.forName(properties.getProperty("classname")) ;
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接对象
public static Connection getConnection() throws SQLException {
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
//释放资源
public static void close(Connection connection, Statement statement, ResultSet resultSet) throws SQLException {
if (connection!=null) {
connection.close();
}
if (statement!=null) {
statement.close();
}
if (resultSet!=null) {
resultSet.close();
}
public static void close(Connection connection,Statement statement) throws SQLException {
if (connection!=null) {
connection.close();
}
if (statement!=null) {
statement.close();
}
}
}
(2)批处理
插入大量数据时,建议使用批处理来做。
package com.westo.jdbc2;
import com.westo.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
public class Demo3 {
public static void main(String[] args) throws SQLException {
//模拟插入大量数据
ArrayList<User> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
User user1 = new User(i, "大量数据");
list.add(user1);
}
Connection connection = JDBCUtils.getConnection();
String sql="insert into user values(?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (User user : list) {
//这种方式 没添加一条数据 executeUpdate执行一次
preparedStatement.setInt(1,user.getId());
preparedStatement.setString(2,user.getName());
//preparedStatement.executeUpdate();
//我们可以先把数据缓存起来
preparedStatement.addBatch();
}
//统一一次执行
preparedStatement.executeBatch();
//清空批处理
preparedStatement.clearBatch();
JDBCUtils.close(connection,preparedStatement);
}
}
class User{
private int id;
private String name;
public User() { }
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
(3)调用存储过程
// 存储过程
DELIMITER $$
USE `mytest1`$$
DROP PROCEDURE IF EXISTS `procedure2`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `procedure2`(IN num INT,OUT result INT)
BEGIN
-- 定义一个局部变量
DECLARE i INT DEFAULT 1;
DECLARE sum1 INT DEFAULT 0;
WHILE i<=num DO
SET sum1=sum1+i;
SET i=i+1;
END WHILE;-- 结束循环
SET result=sum1;
END$$
DELIMITER ;
//java代码
package com.westo.jdbc2;
import com.westo.utils.JDBCUtils;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Types;
public class Demo4 {
public static void main(String[] args) throws SQLException {
Connection connection = JDBCUtils.getConnection();
String sql="{call procedure2(?,?)}";//输入输出参数用?占位
CallableStatement prepareCall = connection.prepareCall(sql);
//给输入参数赋值
prepareCall.setInt(1,100);
//需要注册输出参数
prepareCall.registerOutParameter(2, Types.INTEGER);
prepareCall.execute();
//获取输出结果
int anInt = prepareCall.getInt(2);
System.out.println(anInt);
JDBCUtils.close(connection,prepareCall);
}
}
(4)调用自定义函数
//函数
SET GLOBAL log_bin_trust_function_creators=TRUE;
DELIMITER $$
CREATE
FUNCTION `mytest1`.`funct`(num INT)
RETURNS INT
BEGIN
DECLARE i INT DEFAULT 100;
SET i=i+num;
RETURN i;
END$$
DELIMITER;
//java代码
package com.westo.jdbc2;
import com.westo.utils.JDBCUtils;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Types;
public class Demo5 {
public static void main(String[] args) throws SQLException {
//调用自定义函数
Connection connection = JDBCUtils.getConnection();
String sql="{?=call funct(?)}";
CallableStatement callableStatement = connection.prepareCall(sql);
//设置输入参数
callableStatement.setInt(2,100);
callableStatement.registerOutParameter(1, Types.INTEGER);
callableStatement.execute();
int anInt = callableStatement.getInt(1);
System.out.println(anInt);
JDBCUtils.close(connection,callableStatement);
}
}
(5)获取自增长键的值
要获取自增长键的值,需要在获取操作对象时声明一个参数 Statement.RETURN_GENERATED_KEYS
package com.westo.jdbc2;
import com.westo.utils.JDBCUtils;
import java.sql.*;
public class Demo6 {
public static void main(String[] args) throws SQLException {
Connection connection = JDBCUtils.getConnection();
String sql="insert into demo(username,password) values('张三','123456')";
PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
int i = preparedStatement.executeUpdate();
if (i>0) {
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
//获取自增长键的值 结果集
ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
int anInt=0;
while (generatedKeys.next()) {
anInt = generatedKeys.getInt(1);
System.out.println(anInt);
}
}
}