走好选择的路,别选好走的路
文章目录
JDBC批处理
批处理是指将关联的 SQL 语句组合成一个批处理,并将他们当成一个调用提交给数据库。
当你一次发送多个 SQL 语句到数据库时,可以减少通信的资源消耗,从而提高了性能。
下面我们来演示怎么批处理:
- 先在mysql数据库中新建一个0317test库,在库下新建一张空表users:
CREATE DATABASE 0317test;
USE 0317test;
CREATE TABLE users(
username VARCHAR(20),
password VARCHAR(20)
);
- 现在我们在java创建一个Users类:
public class Users {
private String username;
private String password;
public Users() {
}
public Users(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Users{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
- 我们现在通过java来往数据库中插入10000条数据
import java.sql.*;
import java.util.ArrayList;
public class JDBCDemo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//我们先来模拟10000条数据-创建10000个user对象暂且存到list表中
ArrayList<Users> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
Users user = new Users("张三-" + i, "123-" + i);
list.add(user);
}
//现在来连接数据库操作
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql:///0317test", "root", "123456");
String sql = "insert into users values(?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
//遍历list表,取出每一个user对象的名字和密码赋给?并且执行sql
for (Users user : list) {
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
//直接执行
ps.executeUpdate();
}
//释放资源
conn.close();
ps.close();
}
}
这么一条一条的插入数据非常耗费资源而且效率不高,我们可以使用批处理来提升效率,来看升级后的代码,其实只是改了两三行代码,但是可以大大节省资源:
import java.sql.*;
import java.util.ArrayList;
public class JDBCDemoPlus {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
ArrayList<Users> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
Users user = new Users("张三-" + i, "123-" + i);
list.add(user);
}
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql:///0317test", "root", "123456");
String sql = "insert into users values(?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
//遍历list表,取出每一个user对象的名字和密码赋给?并且执行sql
for (Users user : list) {
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
//ps.executeUpdate(); 删除这行代码,我们不一条一条来执行
//先把预定义参数添加到缓存中,待会一起执行
ps.addBatch(); //<----关键
}
//执行
ps.executeBatch();
//清除缓存
ps.clearBatch();
//释放资源
conn.close();
ps.close();
}
}
JDBC调用存储过程和自定义函数
mysql中存储过程和函数的区别
对于存储过程来说可以返回参数,如记录集,而函数只能返回值或者表对象。函数只能返回一个变量;而存储过程可以返回多个。存储过程的参数可以有IN,OUT,INOUT三种类型,而函数只能有IN类~~存储过程声明时不需要返回类型,而函数声明时需要描述返回类型,且函数体中必须包含一个有效的RETURN语句。
Statement、PreparedStatement和CallableStatement的具体区别
1).三者为继承关系:
Statement(接口)
|
PreparedStatement(接口)
|
CallableStatement(接口)
2).区别:
a).功能:
Statement 接口提供了执行语句和获取结果的基本方法;
PreparedStatement 接口添加了处理 IN 参数的方法;
CallableStatement 接口添加了处理 OUT 参数的方法。
b).用法
Statement :
普通的不带参的查询SQL;
PreparedStatement :
可变参数的SQL,编译一次,执行多次,效率高;
安全性好,有效防止Sql注入等问题;
支持批量更新,批量删除;
CallableStatement :
继承自PreparedStatement,支持带参SQL操作;
支持调用存储过程,提供了对输出和输入/输出参数的支持;
调用存储过程,或者自定义函数,得换一个操作对象用于执行 SQL 存储过程的接口,即CallableStatement:
JDBC API 提供了一个存储过程 SQL 转义语法,该语法允许对所有 RDBMS 使用标准方式调用存储过程。此转义语法有一个包含结果参数的形式和一个不包含结果参数的形式。如果使用结果参数,则必须将其注册为 OUT 参数。其他参数可用于输入、输出或同时用于二者。参数是根据编号按顺序引用的,第一个参数的编号是 1。
- 调用自定义函数的语法
{?=call<procedure -name >[( < arg1 >,<arg2 >, ...)]}
- 调用存储过程的语法
{call<procedure -name >[( < arg1 >,<arg2 >, ...)]}
下面我们来分别演示调用存储过程和自定义函数:
调用存储过程
- 先在数据库中删除user表中存储的数据
TRUNCATE TABLE users;
- 新建一个存储过程,这个存储过程用来给users表中插入数据,然后返回表中现有的数据总条数
DELIMITER $$
CREATE
PROCEDURE `0317test`.`mypro`(IN uname VARCHAR(32),IN pwd VARCHAR(32),OUT num INT)
BEGIN
INSERT INTO 0317test.`users` VALUES(uname,pwd);
SELECT COUNT(*) INTO num FROM 0317test.`users`;
END$$
DELIMITER ;
- 我们现在来用java调用这个存储过程
import java.sql.*;
public class CallableStaDemo {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn =
DriverManager.getConnection("jdbc:mysql:///0317test", "root", "123456");
//调用存储过程的语法:
//{call<procedure -name >[( < arg1 >,<arg2 >, ...)]}
//输入或者输出参数用?占位,注意是英文?
String sql = "{call mypro(?,?,?)}";
CallableStatement callableStatement = conn.prepareCall(sql);
//输出参数要注册一下,这里第三个参数是out
callableStatement.registerOutParameter(3, Types.INTEGER);
//给输出参数赋值
callableStatement.setString(1, "张小凡");
callableStatement.setString(2, "123456");
//执行
callableStatement.execute();
//获取out值
int anInt = callableStatement.getInt(3);
//打印一下看看
System.out.println(anInt);
//释放资源
conn.close();
callableStatement.close();
}
}
执行后返回,这是数据库中现有的行数:
1
我们回到数据库中查看users表:
username password
--------- ----------
张小凡 123456
这样,我们就成功了调用了存储过程并且得到了它的返回值。
调用自定义函数
- 新建一个自定义函数,它可以根据传入的username来返回password
DELIMITER $$
CREATE
FUNCTION `0317test`.`myfun`(uname VARCHAR(32))
RETURNS VARCHAR(32)
BEGIN
DECLARE pwd VARCHAR(32);
SELECT PASSWORD INTO pwd FROM users WHERE username=uname;
RETURN pwd;
END$$
DELIMITER ;
- 现在来调用一下这个函数来查找一下用户名为张小凡的密码
import java.sql.*;
public class CallableStaDemo2 {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql:///0317test", "root", "123456");
//调用自定义函数的语法
//{?=call<procedure -name >[( < arg1 >,<arg2 >, ...)]}
//第一个?代表返回值
String sql = "{?=call myfun(?)}";
CallableStatement callableStatement = conn.prepareCall(sql);
//注册函数返回值
callableStatement.registerOutParameter(1, Types.VARCHAR);
//给参数赋值,注意,第一个?代表返回值,索引是1,传入参数从2开始
callableStatement.setString(2, "张小凡");
//执行
callableStatement.execute();
//获取返回结果
String pwd = callableStatement.getString(1);
System.out.println(pwd);
//释放资源
conn.close();
callableStatement.close();
}
}
获取自增长键的值
关于自增长、主键、外键等知识,请移步:
https://blog.csdn.net/qq_44238142/article/details/88411770#_710
- 我们删除之前的users表:
DROP TABLE users;
- 我们现在来新建两张表,一张是用户表,一张是订单表:
-- 主表
CREATE TABLE users(
id INT PRIMARY KEY AUTO_INCREMENT, -- 主表主键
username VARCHAR(20)
);
-- 从表
CREATE TABLE orders(
id INT PRIMARY KEY AUTO_INCREMENT, -- 从表主键
totalprice DOUBLE,
user_id INT
);
-- 给从表添加外键约束
ALTER TABLE orders ADD FOREIGN KEY(user_id) REFERENCES users(id);
- 现在用java来操作这两张表,比如,我们有了一位新用户马某,他现在下了一张价值¥2000的订单,由于订单表中的user_id值和users表中的id是关联的,我们在下单时就必须获取到这个自增长的id值,这里就需要用到JDBC中获取自增长键的知识:
import java.sql.*;
public class JDBCDemo2 {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql:///0317test", "root", "123456");
/*先在用户表里添加一条数据*/
String sql = "insert into users values(?,?)";
//要获取自增长的值,就要加上Statement.RETURN_GENERATED_KEYS这个常量声明一下
PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
//第一个是id列,自增长约束,赋null值即可
ps.setNull(1, Types.NULL);
ps.setString(2, "马某");
//执行,给用户表里加上马某这个人
int i = ps.executeUpdate();
//声明一下主键,在if判断里用
int keyValue = 0;
if (i > 0) {
System.out.println("插入成功");
//获取自增长键的结果集
ResultSet generatedKeys = ps.getGeneratedKeys();
while (generatedKeys.next()) {
keyValue = generatedKeys.getInt(1);
//输出一下
System.out.println(keyValue);//1
}
} else {
System.out.println("插入失败");
}
/*在订单表里给马某下张订单*/
String sql2 = "insert into orders values(?,?,?)";
PreparedStatement ps2 = conn.prepareStatement(sql2);
ps2.setNull(1, Types.NULL);
ps2.setDouble(2, 2000);
//orders第三列user_id是外键,关联users表中的id
ps2.setInt(3, keyValue);
//执行
ps2.executeUpdate();
}
}