3.2 问题演示
👉问题描述:
当我们尝试给上述刚建好的数据表t_user中的blob类型的字段photo存储图片时,可能发生以下问题:
A.blob类型的字段装不下很大的图片(比如6MB的图片)
B.数据表的字段的数据类型可以容纳JDBC传送的图片,但是传送图片的数据产生了异常,例如(com.mysql.cj.jdbc.exceptions.PacketTooBigException)
💡小tips:
在Mysql里,
BLOB
类型可以存储0-64K(小图片)的二进制字符串数据
,
MEDIUMBLOB
类型可以存0-16MB的二进制形式的长文本数据(大图片)
,LONGBLOB
类型可以存储0-4GB的二进制形式的极大文本数据(大图片)
案例:演示A问题
代码演示如下:
👉①测试64kb以下的图片:
@Test public void test01() throws SQLException, FileNotFoundException, UnsupportedEncodingException { Scanner input=new Scanner(System.in); System.out.print("请输入用户名:"); String username=input.next(); System.out.print("请输入密码:"); String password=input.next(); System.out.print("请输入图片路径:"); String path=input.next(); //连接数据库 String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC"; Connection root = DriverManager.getConnection(url, "root", "123456"); String sql="INSERT INTO t_user VALUES(?,md5(?),?)"; //测试图片路径:C:\Users\king\Desktop\桌面清理\starsky.png PreparedStatement pst = root.prepareStatement(sql); pst.setObject(1,username); pst.setObject(2,password); pst.setObject(3,new FileInputStream(path));//文件字节IO流包含文件,它自动把文件写进去了 int len = pst.executeUpdate(); System.out.println(len>0?"更新成功":"更新失败"); pst.close(); root.close(); }
❓这里存在一个问题?
在sqlyog中对应的blob类型的字段接收来自JDBC传送的图片字节数据后,显示RIFF�,我的MySQL数据库的默认字符集已经是utf8mb4,我的sql yog上的字段确实是blob类型的,就是无法显示图片,只有图片的字节数据
💡tips:
为了时间效率,这个问题我先搁置一旁,如果有知道的大佬,欢迎评论区指正
👉② 测试6MB的图片:
代码演示:还是在刚才的代码上运行
👉 解决措施:将数据表photo字段的数据类型修改为"MEDIUMBLOB"类型,它可以容纳16MB及以下的图片字节数据。
- ⭐sql yog改变字段数据类型的步骤如下:
- ⭐ sql语句修改:
ALTER TABLE t_user MODIFY photo MEDIUMBLOB;
代码演示:再次运行代码成功
案例:演示B问题
👉测试图片:
代码演示如下:
还是刚才上面的案例代码
/* com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (6,671,689 > 4,194,304). You can change this value on the server by setting the 'max_allowed_packet' variable. */
👉原因分析:
MySQL 8.0的默认max_allowed_packet参数值为4MB。
这个参数控制着MySQL服务器能够接收和发送的最大数据包大小。
👉解决措施:
- 以
管理员身份运行cmd命令
,执行net stop 你自己的mysql的服务名称
命令,以停止MySQL服务,不然改了参数也没用。
net stop mysql80 #(mysql80是我自己起的服务名称)
2. 找到你的MySQL服务端的配置文件my.ini,修改max_allowed_packet的参数为16MB,保存更改,然后执行"net start 你自己的MySQL服务名称"命令重启mysql的服务,
再执行代码演示如下:
四、获取自增长键值
4.1 准备测试数据
👉创建数据表t_employee
CREATE TABLE `t_employee` ( `eid` int NOT NULL AUTO_INCREMENT COMMENT '员工编号', `ename` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '员工姓名', `salary` double NOT NULL COMMENT '薪资', `commission_pct` decimal(3,2) DEFAULT NULL COMMENT '奖金比例', `birthday` date NOT NULL COMMENT '出生日期', `gender` enum('男','女') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '男' COMMENT '性别', `tel` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '手机号码', `email` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '邮箱', `address` varchar(150) DEFAULT NULL COMMENT '地址', `work_place` set('北京','深圳','上海','武汉') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '北京' COMMENT '工作地点', `hiredate` date NOT NULL COMMENT '入职日期', `job_id` int DEFAULT NULL COMMENT '职位编号', `mid` int DEFAULT NULL COMMENT '领导编号', `did` int DEFAULT NULL COMMENT '部门编号', PRIMARY KEY (`eid`), KEY `job_id` (`job_id`), KEY `did` (`did`), KEY `mid` (`mid`), CONSTRAINT `t_employee_ibfk_1` FOREIGN KEY (`job_id`) REFERENCES `t_job` (`jid`) ON DELETE SET NULL ON UPDATE CASCADE, CONSTRAINT `t_employee_ibfk_2` FOREIGN KEY (`did`) REFERENCES `t_department` (`did`) ON DELETE SET NULL ON UPDATE CASCADE, CONSTRAINT `t_employee_ibfk_3` FOREIGN KEY (`mid`) REFERENCES `t_employee` (`eid`) ON DELETE SET NULL ON UPDATE CASCADE, CONSTRAINT `t_employee_chk_1` CHECK ((`salary` > 0)), CONSTRAINT `t_employee_chk_2` CHECK ((`hiredate` > `birthday`)) ) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
4.2 问题演示
👉问题描述:
希望自增长的字段在添加完成后,可以及时的反馈给Java程序端。
👉解决方案:
(1)
Preparedstatement pst = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS):
ps:这里的statement是一个接口,它是Preparedstatement的父接口。
为何不用statement?因为它不支持占位符 ”?“的写法
Statement接口中有一个常量值
RETURN GENERATED KEYS,表示执行sql的同时,返回自增长的键值
(2)
ResultSet rs = pst.getGeneratedKeys();//返回的是一个结果集
👉注意:要想拿到ResultSet rs的内容,必须要先遍历判断一下,因为它的游标在刚开始时指向的是表头,而不是指向第一个自增列
案例:尝试给t_employee添加一条记录,并返回它的员工编号(自增长列
)
代码演示如下:
import org.junit.Test; import java.sql.*; import java.util.Date; import java.util.Scanner; public class TestSqlAuto { @Test public void test01() throws SQLException { Scanner input=new Scanner(System.in); System.out.print("请输入姓名:"); String ename=input.next(); System.out.print("请输入薪资:"); String salary=input.next(); System.out.print("请输入出生日期:"); String birthday=input.next(); System.out.print("请输入性别:"); String gender=input.next(); System.out.print("请输入电话:"); String tel=input.next(); System.out.print("请输入邮箱:"); String email=input.next(); //连接数据库,类似于网络编程中的socket String url="jdbc:mysql://localhost:3306/atguigu?serverTimezone=UTC"; Connection root = DriverManager.getConnection(url, "root", "123456"); //多加一步,把?用具体的变量、表达式等值代替 String sql="insert into t_employee(ename,salary,birthday,gender,tel,email,hiredate) values(?,?,?,?,?,?,?)"; //此时对sql进行预编译,里面是带?的 PreparedStatement pst = root.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);//Statement.RETURN_GENERATED_KEYS表示,执行sql后,返回自增长键值 pst.setObject(1,ename);//这里的1代替第一个?,用ename变量的值代替第一个?的位置,下面的依次类推 pst.setObject(2,salary); pst.setObject(3,birthday); pst.setObject(4,gender); pst.setObject(5,tel); pst.setObject(6,email); pst.setObject(7,new Date()); int len = pst.executeUpdate();//执行sql语句 System.out.println(len>0?"添加成功":"添加失败"); //执行完毕后,通过 PreparedStatement对象pst获取它的自增长键值对 ResultSet generatedKeys = pst.getGeneratedKeys();//返回的是一个结果集 while (generatedKeys.next()){//它必须要先遍历一下,让游标指向首行记录 System.out.println("你的员工编号:"+generatedKeys.getObject(1));//此处只有一个自增长的键值 } //释放资源 input.close(); pst.close(); root.close(); } }