From 34347fc3e12de77acf8beef450c1db1e52466ac2 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Mon, 31 Mar 2014 22:06:10 +1300 Subject: [PATCH] Fix for #77 - NPE when lazy loading on a OneToMany that is not a leaf --- .../DefaultPersistenceContext.java | 14 +++- .../tests/model/inheritmany/IMRelated.java | 42 +++++++++++ .../avaje/tests/model/inheritmany/IMRoot.java | 37 ++++++++++ .../tests/model/inheritmany/IMRootOne.java | 20 +++++ .../tests/model/inheritmany/IMRootTwo.java | 32 ++++++++ .../inheritmany/test/TestInheritWithMany.java | 73 +++++++++++++++++++ 6 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/avaje/tests/model/inheritmany/IMRelated.java create mode 100644 src/test/java/com/avaje/tests/model/inheritmany/IMRoot.java create mode 100644 src/test/java/com/avaje/tests/model/inheritmany/IMRootOne.java create mode 100644 src/test/java/com/avaje/tests/model/inheritmany/IMRootTwo.java create mode 100644 src/test/java/org/avaje/test/model/inheritmany/test/TestInheritWithMany.java diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java index ed6922d38..f6e7bb320 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java @@ -5,6 +5,8 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import javax.persistence.Entity; + import com.avaje.ebean.bean.PersistenceContext; import com.avaje.ebeaninternal.api.Monitor; @@ -124,7 +126,7 @@ public final class DefaultPersistenceContext implements PersistenceContext { private ClassContext getClassContext(Class beanType) { - String clsName = beanType.getName(); + String clsName = getBeanBaseType(beanType).getName(); ClassContext classMap = typeCache.get(clsName); if (classMap == null) { classMap = new ClassContext(); @@ -132,6 +134,16 @@ public final class DefaultPersistenceContext implements PersistenceContext { } return classMap; } + + private Class getBeanBaseType(Class beanType) { + Class parent = beanType.getSuperclass(); + + while (parent != null && parent.isAnnotationPresent(Entity.class)) { + beanType = parent; + parent = parent.getSuperclass(); + } + return beanType; + } private static class ClassContext { diff --git a/src/test/java/com/avaje/tests/model/inheritmany/IMRelated.java b/src/test/java/com/avaje/tests/model/inheritmany/IMRelated.java new file mode 100644 index 000000000..5657ac3d1 --- /dev/null +++ b/src/test/java/com/avaje/tests/model/inheritmany/IMRelated.java @@ -0,0 +1,42 @@ +package com.avaje.tests.model.inheritmany; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +@Entity +public class IMRelated { + + @Id + Long id; + + String name; + + @ManyToOne(optional=false) + IMRoot owner; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public IMRoot getOwner() { + return owner; + } + + public void setOwner(IMRoot owner) { + this.owner = owner; + } + +} diff --git a/src/test/java/com/avaje/tests/model/inheritmany/IMRoot.java b/src/test/java/com/avaje/tests/model/inheritmany/IMRoot.java new file mode 100644 index 000000000..49da888da --- /dev/null +++ b/src/test/java/com/avaje/tests/model/inheritmany/IMRoot.java @@ -0,0 +1,37 @@ +package com.avaje.tests.model.inheritmany; + +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.OneToMany; + +@Entity +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +public class IMRoot { + + @Id + Long id; + + @OneToMany(mappedBy="owner") + List related; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public List getRelated() { + return related; + } + + public void setRelated(List related) { + this.related = related; + } + +} diff --git a/src/test/java/com/avaje/tests/model/inheritmany/IMRootOne.java b/src/test/java/com/avaje/tests/model/inheritmany/IMRootOne.java new file mode 100644 index 000000000..e70919bea --- /dev/null +++ b/src/test/java/com/avaje/tests/model/inheritmany/IMRootOne.java @@ -0,0 +1,20 @@ +package com.avaje.tests.model.inheritmany; + +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; + +@Entity +@DiscriminatorValue("ONE") +public class IMRootOne extends IMRoot { + + String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/test/java/com/avaje/tests/model/inheritmany/IMRootTwo.java b/src/test/java/com/avaje/tests/model/inheritmany/IMRootTwo.java new file mode 100644 index 000000000..dd36be816 --- /dev/null +++ b/src/test/java/com/avaje/tests/model/inheritmany/IMRootTwo.java @@ -0,0 +1,32 @@ +package com.avaje.tests.model.inheritmany; + +import java.util.Date; + +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; + +@Entity +@DiscriminatorValue("TWO") +public class IMRootTwo extends IMRoot { + + String title; + + Date whenTitle; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Date getWhenTitle() { + return whenTitle; + } + + public void setWhenTitle(Date whenTitle) { + this.whenTitle = whenTitle; + } + +} diff --git a/src/test/java/org/avaje/test/model/inheritmany/test/TestInheritWithMany.java b/src/test/java/org/avaje/test/model/inheritmany/test/TestInheritWithMany.java new file mode 100644 index 000000000..cd29de49b --- /dev/null +++ b/src/test/java/org/avaje/test/model/inheritmany/test/TestInheritWithMany.java @@ -0,0 +1,73 @@ +package org.avaje.test.model.inheritmany.test; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.tests.model.inheritmany.IMRelated; +import com.avaje.tests.model.inheritmany.IMRoot; +import com.avaje.tests.model.inheritmany.IMRootOne; +import com.avaje.tests.model.inheritmany.IMRootTwo; + + +public class TestInheritWithMany extends BaseTestCase { + + + @Test + public void test() { + + EbeanServer server = Ebean.getServer(null); + Assert.assertNotNull(server); + + SpiEbeanServer spiServer = (SpiEbeanServer)server; + BeanDescriptor beanDescriptor = spiServer.getBeanDescriptor(IMRoot.class); + InheritInfo inheritInfo = beanDescriptor.getInheritInfo(); + Assert.assertNotNull(inheritInfo); + + + IMRootOne one = new IMRootOne(); + one.setName("One Name"); + server.save(one); + add(one, "aaa"); + add(one, "bbb"); + + + + IMRootTwo two = new IMRootTwo(); + two.setTitle("Two Title"); + server.save(two); + add(two, "ccc"); + add(two, "ddd"); + + + List list = server.find(IMRoot.class).select("id").findList(); + + for (IMRoot imRoot : list) { + // lazy load the related OneToMany which is related to a non-leaf + List related = imRoot.getRelated(); + for (IMRelated imRelated : related) { + imRelated.getName(); + } + } + + } + + private void add(IMRoot owner, String string) { + + IMRelated relate = new IMRelated(); + relate.setName(string); + relate.setOwner(owner); + + EbeanServer server = Ebean.getServer(null); + server.save(relate); + } + + +}