@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
相关产品推荐
相关产品推荐

