大家好,我是一名入门的菜鸟,如果你不经意间翻开了我的文章,谢谢您,您的支持是我前进的动力,让我们一起加油!!
由于不是名牌大学,只是一个普普通通的专科生,所以,我想通过自己的努力来获得我想要的,我不会放弃我的梦想,我也曾幻想着我成功的时候在朋友边吹嘘,也曾想象到我失败时候潦倒的样子,幻想始终是幻想,我会努力的,加油,你一定能行
文章目录
1 CLOB 和 BLOB
如果我们想在数据库中存储图片以及大量的文字或某个字段,我们就需要用到CLOB和BLOB
首先讲解一下他们的作用:
- CLOB:Character Large Object 大的字符串数据类型,存储大量的文本数据,如:文字,书,文章。
- BLOB:Binary Large Object 大的字节数据类型,存储二进制的数据,如:音乐,图片,视频。
在MySql的处理类型
- CLOB在Mysql中对应的是TEXT类型
- BLOB在Mysql中对应的就是BLOB
TEXT在数据库中又被细分为四种,它们之间只有存储时的字符数据长度不一样,使用方式是一样的。下面是细分的具体长度
类型 | 最长 |
---|---|
TINYTEXT | 256 字节 |
TEXT | 64KB |
MEDIUMTEXT | 16MB |
LONGTEXT | 4GB |
BLOB类型也同样会成4种,它们的使用方式也是一样的,唯一的区别就是存储的字节数据长度不同,具体长度见下表。
类型 | 最长 |
---|---|
TINYBLOB | 256字节 |
BLOB | 64K |
MEDIUMBLOB | 16M |
LONGBLOB | 4G |
CLOB的读写操作
这是数据库的架构总览
项目需求:在本地文件中有txt文件,要把这个文件通过流写到mysql浏览器中
/**
* CLOB实现,存大量的文本数据,如:文字,书,文章
* CLOB在mysql中对应的是TEXT
*/
public class Demo01 {
/**
* 读取本级文件写到数据库中
* setClob()
*/
@Test
public void insert(){
FileReader reader = null;
Connection conn = null;
PreparedStatement ps = null;
try {
//读取本机文件
reader = new FileReader("D:\\test\\李白.txt");
//获取连接
conn = JDBCUtils.getConn();
//创建语句对象:预编译对象
ps = conn.prepareStatement("insert into employee(name,resume) values (?,?)");
//给?赋值(设置占位符)
ps.setString(1,"奥神");
//传入clob类型
ps.setClob(2,reader);
int i = ps.executeUpdate();
System.out.println(i);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(conn,ps);
}
}
运行结果:
小结
设置CLOB类型使用哪个方法?
setClob(占位符,Reader 字符流)
项目需求:从数 据库中读取文件写到本地文件中,文件名字为name列名中的内容
/**
* 从数据库中读取
* 从数据库中读取之后再写入一个新的文件中
*/
@Test
public void read(){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//获取连接
conn = JDBCUtils.getConn();
//创建语句对象:预编译对象
ps = conn.prepareStatement("select * from employee where name = ?");
//给?赋值(设置占位符)
ps.setString(1,"奥神");
//获得结果集
rs = ps.executeQuery();
//因为只有一列内容,不用while循环
if(rs.next()){
//直接读取字符串打印
String name = rs.getString("name");
String resume = rs.getString("resume");
System.out.println("名字:" + name);
System.out.println("简历:" + resume);
//获取Clob类型中存储的数据
Clob clob = rs.getClob("resume");
//得到字符输入流:这个reader就是数据库中的文件数据
Reader reader = clob.getCharacterStream();
//再利用字符输出流写到文件中
FileWriter fileWriter = new FileWriter("D:\\test\\" + name + ".txt");
//讲从数据库中读取到的数据复制到输出流中
IOUtils.copy(reader,fileWriter);
//关流
fileWriter.close();
reader.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.close(conn,ps,rs);
}
}
小结
读取CLOB使用哪个方法?
1. 直接做为字符串读取:getString("列名")
2. 先读取到Clob类型 -> Reader -> Writer 写成一个文件
getClob("列名") -> getCharacterStream()
BLOB的读写
案例需求:
将本地的一张图片d:/p1.jpg更新到上面的员工信息的photo列中,照片类型使用MEDIUMBLOB类型,因为照片的有可能大于64K。
案例代码:
/**
* 向数据库中加载图片
*/
@Test
public void insertPhoto() throws Exception {
//获取文件字节输入流对象
FileInputStream fis = new FileInputStream("D:\\test\\p2.jpg");
//连接数据库
Connection conn = JDBCUtils.getConn();
PreparedStatement ps = conn.prepareStatement("update employee set photo = ? where name = ?");
//替换占位符
ps.setBlob(1,fis);
ps.setString(2,"奥神");
int i = ps.executeUpdate();
//关闭连接
JDBCUtils.close(conn,ps);
fis.close();
System.out.println(i);
}
总结
设置BLOB的占位符使用哪个方法?
setBlob(占位符, InputStream 输入流)
案例需求:
通过名字,读取某员工的姓名和照片,使用字节输出流写出照片到硬盘,文件名是name.jpg,文件写完以后,关闭输入输出流。
案例代码:
/**
* 从数据酷中读取并且写到文件中
*/
@Test
public void readPhoto() throws Exception{
//获取数据库
Connection conn = JDBCUtils.getConn();
//获取预编译对象
PreparedStatement ps = conn.prepareStatement("select name,photo from employee where name = ?");
ps.setString(1,"奥神");
ResultSet rs = ps.executeQuery();
if(rs.next()){
String name = rs.getString("name");
//获取数据库中的图片:字节输入流
InputStream phote = rs.getBinaryStream("photo");
System.out.println(name);
System.out.println(phote);
//创建文件字节输出流
FileOutputStream fileOutputStream = new FileOutputStream("D:\\test\\" + name + ".jpg");
//把输入流写到输出流中
IOUtils.copy(phote,fileOutputStream);
//关流
fileOutputStream.close();
phote.close();
}
JDBCUtils.close(conn,ps,rs);
}
2 元数据
- 元数据:用来描述数据的数据成为元数据,比如建表的代码,用来描述表的结构,描述这一列的信息
元数据分类:
三种元数据 | 说明 | 如何得到元数据 |
---|---|---|
数据库元数据 | 描述数据库一些信息,如:数据库驱动版本号,数据库版本号等 | 通过连接对象Connection |
参数元数据 | 描述占位符参数的一些信息,如:一共有几个参数 | 通过PreparedStatement |
结果集元数据 | 描述结果集中信息,如:一共有多少列,每列的数据类型是什么 | 通过ResultSet |
获取元数据的一系列方法:
- ParameterMetaData(参数元数据)
PreparedStatement接口中的方法 | 说明 |
---|---|
ParameterMetaData getParameterMetaData() | 通过预编译的语句获取参数元数据 |
因为是接口,我么要使用参数元数据,就要使用它的方法
ParameterMetaData接口中的方法 | 说明 |
---|---|
int getParameterCount() | 获取参数的个数 |
String getParameterTypeName(int param) | 获取某一列参数的数据类型,参数就是列号,从1开始 只能获取varchar类型的数据 |
案例:得到SQL语句参数的元数据
/**
* 参数元数据
*/
public class Demo3 {
public static void main(String[] args) throws SQLException {
//1. 得到连接对象
Connection connection = JdbcUtils.getConnection();
//2. 得到语句对象,SQL语句是向学生表中插入一条记录
PreparedStatement preparedStatement = connection.prepareStatement("insert into student (name, sex, birthday) values (?,?,?)");
preparedStatement.setString(1,"猪八戒");
preparedStatement.setInt(2, 1); //使用整数
preparedStatement.setDate(3, Date.valueOf("2000-11-11"));
//3. 得到参数元数据
ParameterMetaData metaData = preparedStatement.getParameterMetaData();
//4. 输出参数的个数 getParameterCount()
System.out.println("一共有多少个参数:" + metaData.getParameterCount());
//5. 得到第1个参数的类型名字 getParameterTypeName()
System.out.println("第1个参数的类型:" + metaData.getParameterTypeName(1));
//6. 执行SQL语句,插入1条记录
int row = preparedStatement.executeUpdate();
System.out.println("插入了一条记录:" + row);
//关闭连接
JdbcUtils.close(connection, preparedStatement);
}
}
注意事项
默认情况下:
MySQL驱动对参数元数据的数据类型支持不理想,需要如下配置才能得到参数的MySQL数据类型,而且只能得到VARCHAR类型,如果不配置这个参数则会出现异常。 |
---|
jdbc:mysql://localhost:3306/数据库名?generateSimpleParameterMetadata=true |
修改工具类和修改URL参数
结果集元数据:ResultSetMetaData
- 通过结果集元数据获取结果集中的列名和列的类型总共有多少列信息
获取ResultSetMataData:
ResultSet接口中的方法 | 说明 |
---|---|
ResultSetMetaData getMetaData() | 通过结果集对象获取结果集元数据 |
接口中的方法:
ResultSetMetaData接口中的方法 | 说明 |
---|---|
int getColumnCount() | 获取结果集中有多少列 |
String getColumnName(int column) | 获取指定列的列名 |
String getColumnTypeName(int column) | 获取指定列的数据类型,返回的是字符串的类型名 |
案例:结果集元数据的使用
/**
* 结果集元数据的使用
*/
public class Demo4 {
public static void main(String[] args) throws SQLException {
//1. 创建连接对象
Connection connection = JdbcUtils.getConnection();
//2. 获取预编译的语句对象
PreparedStatement preparedStatement = connection.prepareStatement("select * from student where id=?");
preparedStatement.setInt(1, 1);
//3. 查询学生信息,得到结果集对象
ResultSet resultSet = preparedStatement.executeQuery();
//4. 通过结果集对象得到结果集元数据对象
ResultSetMetaData metaData = resultSet.getMetaData();
//5. 得到一共有多少列
int columnCount = metaData.getColumnCount();
System.out.println("一共有" + columnCount + "列");
//6. 循环输出每一列的名字和列的类型
for (int i = 0; i < columnCount; i++) {
//列号从1开始
String columnName = metaData.getColumnName(i + 1);
//每一列的数据类型
String columnTypeName = metaData.getColumnTypeName(i + 1);
System.out.println("第" + (i+1) + "列的名字是:" + columnName + ",数据类型是:" + columnTypeName);
}
//7. 释放资源关闭连接
JdbcUtils.close(connection, preparedStatement, resultSet);
}
}
3 工具类增强:(增删改查)通用
package com.aoshen.util;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 获取数据库连接
*/
public class JDBCUtils {
public static Connection getConn() throws SQLException {
return DriverManager.getConnection("jdbc:mysql:///db4","root","root");
}
/**
* 三个参数:含有查询操作
* @param rs
* @param stmt
* @param conn
*/
public static void close(Connection conn, PreparedStatement stmt,ResultSet rs){
if(rs !=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt !=null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn !=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 两个参数:只执行增删改
* @param stmt
* @param conn
*/
public static void close(Connection conn,PreparedStatement stmt){
close(conn, stmt,null);
}
/**
* 增删改
* @param sql
* @param args
* @return
*/
public static int update(String sql,Object...args){
Connection conn = null;
PreparedStatement ps = null;
int row = 0; //影响的行数
try {
//创建连接数据库
conn = getConn();
//获取预编译对象
ps = conn.prepareStatement(sql);
//通过获取元数据,获取有多少个参数
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount();
//遍历
for (int i = 0; i < count; i++) {
ps.setObject(i+1,args[i]);
}
row = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(conn,ps);
}
return row;
}
/**
* 查询
* @param sql
* @param c
* @param args
* @param <T>
* @return
*/
public static <T> List<T> query(String sql,Class<T> c,Object...args){
Connection connection = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
//创建集合用来存储得到的值
List<T> list = new ArrayList<>();
try{
//创建连接
connection = getConn();
ps = connection.prepareStatement(sql);
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount();
for (int i = 0; i < count; i++) {
ps.setObject(i+1,args[i]);
}
resultSet = ps.executeQuery();
while (resultSet.next()){
//每条数据都创建一个对象存起来
T t = c.getConstructor().newInstance();
//得到对象中有哪些属性
Field[] fields = c.getDeclaredFields();
//遍历
for (Field field : fields) {
//因为是私有的,要暴力反射
field.setAccessible(true);
//获取列名
String name = field.getName();
//天啊及到对象中
field.set(t,resultSet.getObject(name));
}
//添加到集合中
list.add(t);
}
}catch (Exception e){
e.printStackTrace();
}finally {
close(connection,ps,resultSet);
}
return list;
}
}
注意:这个JDBCUtils还不是最终进化版,后面还会进行优化,我们先一步一步来!!
最后感谢大家的支持!!