You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@Basic(fetch=FetchType.LAZY)未生效,为何无法实现懒加载?

问题:Hibernate中@Basic(fetch = FetchType.LAZY)对String/Enum字段不生效

问题描述

为UserInfo类的String类型name字段和Enum类型status字段添加了@Basic(fetch = FetchType.LAZY)注解,期望实现懒加载,但查询SQL显示这两个字段仍被立即加载。使用Hibernate 6.1.7.Final,尝试过添加@Lob注解、将注解移到getter/setter上均无效。

相关代码与配置如下:

Main类代码

package org.example;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;

public class Main
{
    public static void main(String[] args)
    {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-persistence-unit");
        EntityManager entityManager = emf.createEntityManager();

        entityManager.getTransaction().begin();

        UserInfo userInfo = new UserInfo();
        userInfo.setUserName("User 1");
        userInfo.setHappy(true);
        userInfo.setStatus(Status.SINGLE);

        entityManager.persist(userInfo);

        entityManager.getTransaction().commit();

        UserInfo info = entityManager.find(UserInfo.class,1);
        System.out.println(info);

        entityManager.close();
    }
}

Status枚举

public enum Status {
    SINGLE, COMMITTED, MARRIED
}

persistence.xml

<persistence xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
    <persistence-unit name="my-persistence-unit">
        <description>JPA In Action</description>
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name = "jakarta.persistence.jdbc.url"
                      value = "jdbc:mysql://localhost/JPA-In-Action"/>
            <property name = "jakarta.persistence.jdbc.user" value = "root"/>
            <property name="jakarta.persistence.jdbc.password" value="1234"/>
            <property name="jakarta.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.show_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>JPA-In-Action</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>JPA-In-Action</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>19</maven.compiler.source>
    <maven.compiler.target>19</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>6.1.7.Final</version>
    </dependency>

    <dependency>
      <groupId>jakarta.xml.bind</groupId>
      <artifactId>jakarta.xml.bind-api</artifactId>
      <version>4.0.0</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.32</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

输出结果

Hibernate: insert into user (happy, status, name) values (?, ?, ?)
Hibernate: select u1_0.id,u1_0.happy,u1_0.status,u1_0.name from user u1_0 where u1_0.id=?
org.example.UserInfo@22ad1bae

解决方案

1. 开启Hibernate字节码增强

Hibernate对String、Enum这类非LOB基础类型的懒加载支持,依赖字节码增强生成可代理的字段访问逻辑。默认不开启该功能,需手动配置Maven插件:

在pom.xml的<build><plugins>中添加以下插件:

<plugin>
    <groupId>org.hibernate.orm.tooling</groupId>
    <artifactId>hibernate-enhance-maven-plugin</artifactId>
    <version>6.1.7.Final</version>
    <executions>
        <execution>
            <phase>process-classes</phase>
            <goals>
                <goal>enhance</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <enableLazyInitialization>true</enableLazyInitialization>
    </configuration>
</plugin>

2. 调整实体类注解

确保@Basic(fetch = FetchType.LAZY)注解统一应用在字段或getter上(不要混合使用),无需额外添加@Lob(除非是大文本类型)。示例UserInfo类:

import jakarta.persistence.*;

@Entity
@Table(name = "user")
public class UserInfo {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private boolean happy;
    
    @Basic(fetch = FetchType.LAZY)
    private String userName; // 对应数据库name字段
    
    @Basic(fetch = FetchType.LAZY)
    @Enumerated(EnumType.STRING)
    private Status status;

    // getter、setter
    // 注意:toString不要包含懒加载字段,否则会触发立即加载
    @Override
    public String toString() {
        return "UserInfo{" +
                "id=" + id +
                ", happy=" + happy +
                '}';
    }

    // 省略getter和setter
}

3. 可选:配置懒加载无事务支持

若需在EntityManager关闭后访问懒加载字段,可在persistence.xml的properties中添加:

<property name="hibernate.enable_lazy_load_no_trans" value="true"/>

注:该配置仅用于测试或特殊场景,生产环境建议在事务内访问懒加载字段。

4. 测试验证

修改Main类测试逻辑,避免立即触发懒加载字段访问:

UserInfo info = entityManager.find(UserInfo.class,1);
// 此时仅查询id和happy字段,不会加载name和status
System.out.println(info); 
// 手动访问懒加载字段,触发二次查询
System.out.println(info.getUserName());
System.out.println(info.getStatus());

此时会看到两次SQL:第一次查询id和happy,第二次查询name和status。

原因说明

Hibernate的懒加载依赖代理机制,但String、Enum这类基础类型无法直接生成代理。字节码增强会修改实体类字节码,为这些字段生成延迟加载的访问器,第一次调用getter时才触发数据库查询。

内容的提问来源于stack exchange,提问作者Aaryamaan Pol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 06:07:02