当前位置:首页 > 技术 > 正文内容

JPA中LOB数据类型的存储与延迟加载策略

访客 技术 2026年6月10日 1

在JPA(Java Persistence API)中,大对象(Large Object, LOB)数据类型用于存储大量二进制数据(BLOB)或字符数据(CLOB)。这些数据通常不适合存储在标准的VARCHAR或VARBINARY列中,因为它们的大小可能非常庞大。JPA通过@Lob注解提供了对LOB数据类型的支持,并允许开发者配置其加载策略以优化性能。

JPA LOB类型映射

LOB在数据库中主要分为两种:字符大对象(CLOB)和二进制大对象(BLOB)。

  • CLOB列:用于存储大量的字符序列,例如长文本、XML文档或JSON字符串。在Java中,通常映射为Stringchar[]Character[]类型。
  • BLOB列:用于存储大量的二进制序列,例如图片、视频、音频文件或任何其他二进制文件。在Java中,通常映射为byte[]Byte[]Serializable类型。

JPA通过@Lob注解将实体字段标记为LOB类型。如果字段类型是byte[],JPA会将其映射为BLOB;如果字段类型是String,则映射为CLOB。

示例:基本的LOB数据存储

以下示例展示了一个实体类DocumentAsset,其中包含一个byte[]类型的字段,用于存储文档的二进制内容,并使用@Lob注解进行标记。

实体定义:DocumentAsset.java

package com.example.data.model;

import javax.persistence.*;

@Entity
@Table(name = "DOCUMENT_ASSETS")
public class DocumentAsset {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ASSET_ID")
    private Long id;

    @Column(name = "DOC_TITLE", nullable = false)
    private String title;

    @Column(name = "DOC_TYPE")
    private String documentType; // e.g., "image/jpeg", "application/pdf"

    @Lob // 标记为LOB类型,对应数据库中的BLOB
    @Column(name = "FILE_CONTENT")
    private byte[] fileContent;

    // 构造函数
    public DocumentAsset() {}

    public DocumentAsset(String title, String documentType, byte[] fileContent) {
        this.title = title;
        this.documentType = documentType;
        this.fileContent = fileContent;
    }

    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getDocumentType() { return documentType; }
    public void setDocumentType(String documentType) { this.documentType = documentType; }
    public byte[] getFileContent() { return fileContent; }
    public void setFileContent(byte[] fileContent) { this.fileContent = fileContent; }

    @Override
    public String toString() {
        return "DocumentAsset [id=" + id + ", title=" + title + ", documentType=" + documentType + "]";
    }
}

数据访问层:DocumentRepository.java

此Repository负责DocumentAsset实体的持久化操作。

package com.example.data.repository;

import com.example.data.model.DocumentAsset;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;

@Repository
@Transactional
public class DocumentRepository {

    @PersistenceContext
    private EntityManager entityManager;

    /**
     * 持久化或更新一个文档资产。
     * @param asset 待保存的文档资产
     * @return 已保存的文档资产(可能包含生成ID)
     */
    public DocumentAsset save(DocumentAsset asset) {
        if (asset.getId() == null) {
            entityManager.persist(asset);
        } else {
            asset = entityManager.merge(asset);
        }
        return asset;
    }

    /**
     * 根据ID查找文档资产。
     * @param id 文档资产ID
     * @return 匹配的文档资产或null
     */
    public DocumentAsset findById(Long id) {
        return entityManager.find(DocumentAsset.class, id);
    }

    /**
     * 获取所有文档资产的列表。
     * @return 文档资产列表
     */
    public List<DocumentAsset> findAll() {
        return entityManager.createQuery("SELECT d FROM DocumentAsset d", DocumentAsset.class).getResultList();
    }
}

执行上述代码的持久化操作后,数据库中的相应表结构和数据可能如下所示:

Table Name: DOCUMENT_ASSETS
 Row:
    Column Name: ASSET_ID,
    Column Type: BIGINT:
    Column Value: 1

    Column Name: DOC_TITLE,
    Column Type: VARCHAR:
    Column Value: Report_Q1_2023

    Column Name: DOC_TYPE,
    Column Type: VARCHAR:
    Column Value: application/pdf

    Column Name: FILE_CONTENT,
    Column Type: BLOB:
    Column Value: [二进制数据内容]

 Row:
    Column Name: ASSET_ID,
    Column Type: BIGINT:
    Column Value: 2

    Column Name: DOC_TITLE,
    Column Type: VARCHAR:
    Column Value: User_Avatar

    Column Name: DOC_TYPE,
    Column Type: VARCHAR:
    Column Value: image/png

    Column Name: FILE_CONTENT,
    Column Type: BLOB:
    Column Value: [二进制数据内容]

JPA LOB数据的延迟加载(Lazy Loading)

LOB字段通常存储大量数据,如果每次查询实体时都立即加载这些大对象,可能会导致严重的性能问题和内存消耗。为了优化性能,JPA允许对LOB字段进行延迟加载。

延迟加载(Lazy Loading)意味着LOB数据只有在实际需要访问时(即调用其getter方法时)才从数据库中加载。默认情况下,@Lob字段通常是立即加载(EAGER)的,但可以通过@Basic注解的fetch属性显式地设置为延迟加载。

  • FetchType.EAGER:立即加载。实体被查询时,相关的LOB数据会立即从数据库中加载到内存。
  • FetchType.LAZY:延迟加载。实体被查询时,LOB字段只会加载一个代理或占位符。直到代码第一次尝试访问该字段(例如调用getFileContent()),实际的LOB数据才会被加载。

示例:LOB字段的延迟加载配置

我们修改上一个示例中的DocumentAsset实体,为其fileContent字段配置延迟加载。

实体定义:DocumentAssetLazy.java

package com.example.data.model;

import javax.persistence.*;
import static javax.persistence.FetchType.LAZY; // 静态导入FetchType.LAZY

@Entity
@Table(name = "DOCUMENT_ASSETS_LAZY") // 使用不同的表名以区分
public class DocumentAssetLazy {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ASSET_ID")
    private Long id;

    @Column(name = "DOC_TITLE", nullable = false)
    private String title;

    @Column(name = "DOC_TYPE")
    private String documentType;

    @Lob
    @Basic(fetch = LAZY) // 显式配置为延迟加载
    @Column(name = "FILE_CONTENT")
    private byte[] fileContent;

    // 构造函数
    public DocumentAssetLazy() {}

    public DocumentAssetLazy(String title, String documentType, byte[] fileContent) {
        this.title = title;
        this.documentType = documentType;
        this.fileContent = fileContent;
    }

    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getDocumentType() { return documentType; }
    public void setDocumentType(String documentType) { this.documentType = documentType; }
    public byte[] getFileContent() { return fileContent; }
    public void setFileContent(byte[] fileContent) { this.fileContent = fileContent; }

    @Override
    public String toString() {
        return "DocumentAssetLazy [id=" + id + ", title=" + title + ", documentType=" + documentType + "]";
    }
}

数据访问层:DocumentRepositoryLazy.java

对于延迟加载,Repository的实现通常保持不变。延迟加载行为由JPA提供者在运行时管理。

package com.example.data.repository;

import com.example.data.model.DocumentAssetLazy;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional
public class DocumentRepositoryLazy {

    @PersistenceContext
    private EntityManager entityManager;

    public DocumentAssetLazy save(DocumentAssetLazy asset) {
        if (asset.getId() == null) {
            entityManager.persist(asset);
        } else {
            asset = entityManager.merge(asset);
        }
        return asset;
    }

    public DocumentAssetLazy findById(Long id) {
        // 当调用此方法时,如果fetch=LAZY,fileContent字段不会被加载
        // 只有当后续代码调用 asset.getFileContent() 时,数据才会被加载
        return entityManager.find(DocumentAssetLazy.class, id);
    }
}

使用延迟加载后,当我们从数据库中检索DocumentAssetLazy实体时,fileContent字段并不会立即加载。只有当应用程序代码真正访问getFileContent()方法时,JPA才会执行额外的查询来获取LOB数据。这对于需要频繁查询实体但只需偶尔访问大对象内容的场景非常有用。

标签: JPALOBBlobCLOB

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。