今天在学习 OpenJPA 时,在 @OneToMany 中指定 fetch 属性为 FetchType.EAGER,实现一对多关系中的多的这方数据及时加载,不进行延迟加载(OpenJPA 默认开启延迟加载)。
但是,在运行实例时,输出如下错误日志信息:
2391 demo1 INFO [main] openjpa.Enhance - Creating subclass and redefining methods for "[class com.huangx.openjpa.lazy.demo1.Teacher, class com.huangx.openjpa.lazy.demo1.Student]". This means that your application will be less efficient than it would if you ran the OpenJPA enhancer. 3328 demo1 TRACE [main] openjpa.jdbc.SQL - <t 1729779847, conn 1431467659> executing prepstmnt 142247393 SELECT t0.name, t1.TEACHER_ID, t2.id, t2.name, t2.salary, t2.sex FROM Teacher t0 LEFT OUTER JOIN Teacher_Student t1 ON t0.id = t1.TEACHER_ID LEFT OUTER JOIN Student t2 ON t1.STUDENTLIST_ID = t2.id WHERE t0.id = ? ORDER BY t1.TEACHER_ID ASC [params=?] 3344 demo1 TRACE [main] openjpa.jdbc.SQL - <t 1729779847, conn 1431467659> [16 ms] spent Exception in thread "main" <openjpa-2.4.2-r422266:1777108 nonfatal general error> org.apache.openjpa.persistence.PersistenceException: null FailedObject: 351 [org.apache.openjpa.util.IntId] [java.lang.String] at org.apache.openjpa.kernel.BrokerImpl.find(BrokerImpl.java:1029) at org.apache.openjpa.kernel.BrokerImpl.find(BrokerImpl.java:923) at org.apache.openjpa.kernel.DelegatingBroker.find(DelegatingBroker.java:230) at org.apache.openjpa.persistence.EntityManagerImpl.find(EntityManagerImpl.java:488) at com.huangx.openjpa.lazy.demo1.Demo.main(Demo.java:18) Caused by: java.lang.NullPointerException at org.apache.openjpa.jdbc.kernel.JDBCStoreManager.setInverseRelation(JDBCStoreManager.java:452) at org.apache.openjpa.jdbc.kernel.JDBCStoreManager.initializeState(JDBCStoreManager.java:412) at org.apache.openjpa.jdbc.kernel.JDBCStoreManager.initialize(JDBCStoreManager.java:305) at org.apache.openjpa.kernel.DelegatingStoreManager.initialize(DelegatingStoreManager.java:112) at org.apache.openjpa.kernel.ROPStoreManager.initialize(ROPStoreManager.java:57) at org.apache.openjpa.kernel.BrokerImpl.initialize(BrokerImpl.java:1048) at org.apache.openjpa.kernel.BrokerImpl.find(BrokerImpl.java:1006) ... 4 more
(1)Student.java 代码
@Data
@Entity
@Table
public class Student {
@Id
@GeneratedValue
private int id;
@Column
private String name;
@Column
private String sex;
@Column
private float salary;
}(2)Teacher.java 代码
@Data
@Entity
@Table
public class Teacher {
@Id
@GeneratedValue
private int id;
@Column
private String name;
// FetchType.EAGER 表示立即加载 Student 数据
@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
private List<Student> studentList;
}(3)客户端代码
EntityManagerFactory emf = Persistence.createEntityManagerFactory(NAME);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// 初始化数据
Teacher teacher = em.find(Teacher.class, 351);
System.out.println("id = " + teacher.getId());
System.out.println("name = " + teacher.getName());
List<Student> studentList = teacher.getStudentList();
for(Student student : studentList) {
System.out.println(student);
}
em.getTransaction().commit();
em.close();
emf.close();
System.out.println("finished.");运行上面客户端代码,将执行如下 SQL 语句:
SELECT t0.name, t1.TEACHER_ID, t2.id, t2.name, t2.salary, t2.sex FROM Teacher t0 LEFT OUTER JOIN Teacher_Student t1 ON t0.id = t1.TEACHER_ID LEFT OUTER JOIN Student t2 ON t1.STUDENTLIST_ID = t2.id WHERE t0.id = 351 ORDER BY t1.TEACHER_ID ASC;
上面 SQL 语句中没有匹配 Teacher 实体的 id 字段,并且通过打断点分析源码。如下图:
![OpenJPA FailedObject: * [org.apache.openjpa.util.IntId] [java.lang.String] 错误](https://www.hxstrive.com/hxstrivedocs/2021/09/02/11e366ef41474e729b9e7796db4239ac.png)
上图中,红框中的语句抛出了 NullPointerException 异常。这是因为没有获取到 ClassMapping(即 cm),为什么 cm 变量为空呢?接下来看下图:
![OpenJPA FailedObject: * [org.apache.openjpa.util.IntId] [java.lang.String] 错误](https://www.hxstrive.com/hxstrivedocs/2021/09/02/58e7327fc492413d9c72d7a288d810d6.jpg)
上图中,_conf.getMetaDataRepositoryInstance() 方法返回了 MetaDataRepository 对象,该对象中的 _metas 属性中存放了实体信息。继续看下图:
![OpenJPA FailedObject: * [org.apache.openjpa.util.IntId] [java.lang.String] 错误](https://www.hxstrive.com/hxstrivedocs/2021/09/02/110441782bad4f0f9c6e89228e3f03c9.jpg)
上图中,pc.getClass() 返回目标实体的 Class 属性,然后使用 _conf.getMetaDataRepositoryInstance().getCachedMetaData() 方法获取对应的 ClassMapping。然而,上面两张图片的实体类型并不一致,其中:
_conf.getMetaDataRepositoryInstance() 的 _metas 类型为 com.huangx.openjpa.lazy.demo1.Student
pc.getClass() 类型为 org.apache.openjpa.enhance.com$huangx$openjpa$lazy$demo1$Student$pcsubclass