今天在学习 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 字段,并且通过打断点分析源码。如下图:
上图中,红框中的语句抛出了 NullPointerException 异常。这是因为没有获取到 ClassMapping(即 cm),为什么 cm 变量为空呢?接下来看下图:
上图中,_conf.getMetaDataRepositoryInstance() 方法返回了 MetaDataRepository 对象,该对象中的 _metas 属性中存放了实体信息。继续看下图:
上图中,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