#2156 - Using @OneToOne(targetEntity...) with Interface field results in BeanNotRegisteredException (#2160)

This commit is contained in:
Rob Bygrave authored and GitHub committed 2021-02-12 16:30:28 +13:00
1 parent 536ea57369
commit d3a09b93c0
11 files changed
+142 -73

No files matched your search

@@ -176,4 +176,9 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
public void setFetchPreference(int fetchPreference) {
this.fetchPreference = fetchPreference;
}
@SuppressWarnings("unchecked")
public void setTargetType(Class<?> targetType) {
this.targetType = (Class<T>)targetType;
}
}
@@ -65,19 +65,6 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
this.manyType = manyType;
}
/**
* When generics is not used for manyType you can specify via annotations.
* <p>
* Really only expect this for Scala due to a Scala compiler bug at the moment.
* Otherwise I'd probably not bother support this.
* </p>
*/
@SuppressWarnings("unchecked")
public void setTargetType(Class<?> cls) {
this.targetType = (Class<T>) cls;
}
/**
* Return the many type.
*/
@@ -0,0 +1,39 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.config.BeanNotRegisteredException;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.BeanTable;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
abstract class AnnotationAssoc extends AnnotationParser {
final BeanDescriptorManager factory;
AnnotationAssoc(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
super(info, readConfig);
this.factory = factory;
}
void setTargetType(Class<?> targetType, DeployBeanPropertyAssoc<?> prop) {
if (!targetType.equals(void.class)) {
prop.setTargetType(targetType);
}
}
void setBeanTable(DeployBeanPropertyAssoc<?> prop) {
BeanTable assoc = getBeanTable(prop);
if (assoc == null) {
throw new BeanNotRegisteredException(errorMsgMissingBeanTable(prop.getTargetType(), prop.getFullBeanName()));
}
prop.setBeanTable(assoc);
}
BeanTable getBeanTable(DeployBeanPropertyAssoc<?> prop) {
return factory.getBeanTable(prop.getTargetType());
}
private String errorMsgMissingBeanTable(Class<?> type, String from) {
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered? See https://ebean.io/docs/trouble-shooting#not-registered";
}
}
@@ -44,16 +44,10 @@ import static io.ebean.util.StringHelper.isNull;
/**
* Read the deployment annotation for Assoc Many beans.
*/
class AnnotationAssocManys extends AnnotationParser {
class AnnotationAssocManys extends AnnotationAssoc {
private final BeanDescriptorManager factory;
/**
* Create with the DeployInfo.
*/
AnnotationAssocManys(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
super(info, readConfig);
this.factory = factory;
super(info, readConfig, factory);
}
/**
@@ -435,11 +429,6 @@ class AnnotationAssocManys extends AnnotationParser {
prop.setInverseJoin(inverseDest);
}
private String errorMsgMissingBeanTable(Class<?> type, String from) {
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered? See https://ebean.io/docs/trouble-shooting#not-registered";
}
private void readToMany(ManyToMany propAnn, DeployBeanPropertyAssocMany<?> manyProp) {
manyProp.setMappedBy(propAnn.mappedBy());
@@ -462,20 +451,6 @@ class AnnotationAssocManys extends AnnotationParser {
manyProp.getTableJoin().setType(SqlJoinType.OUTER);
}
private void setTargetType(Class<?> targetType, DeployBeanPropertyAssocMany<?> prop) {
if (!targetType.equals(void.class)) {
prop.setTargetType(targetType);
}
}
private void setBeanTable(DeployBeanPropertyAssocMany<?> manyProp) {
BeanTable assoc = factory.getBeanTable(manyProp.getTargetType());
if (assoc == null) {
throw new BeanNotRegisteredException(errorMsgMissingBeanTable(manyProp.getTargetType(), manyProp.getFullBeanName()));
}
manyProp.setBeanTable(assoc);
}
private String getM2MJoinTableName(BeanTable lhsTable, BeanTable rhsTable) {
TableName lhs = new TableName(lhsTable.getBaseTable());
@@ -33,18 +33,15 @@ import javax.validation.constraints.NotNull;
/**
* Read the deployment annotations for Associated One beans.
*/
public class AnnotationAssocOnes extends AnnotationParser {
public class AnnotationAssocOnes extends AnnotationAssoc {
private static final Logger log = LoggerFactory.getLogger(AnnotationAssocOnes.class);
private final BeanDescriptorManager factory;
/**
* Create with the deploy Info.
*/
AnnotationAssocOnes(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
super(info, readConfig);
this.factory = factory;
super(info, readConfig, factory);
}
/**
@@ -191,25 +188,11 @@ public class AnnotationAssocOnes extends AnnotationParser {
}
}
private String errorMsgMissingBeanTable(Class<?> type, String from) {
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered? Does it have the @Entity annotation? See https://ebean.io/docs/trouble-shooting#not-registered";
}
private BeanTable beanTable(DeployBeanPropertyAssoc<?> prop) {
BeanTable assoc = factory.getBeanTable(prop.getPropertyType());
if (assoc == null) {
throw new BeanNotRegisteredException(errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName()));
}
return assoc;
}
private void readManyToOne(ManyToOne propAnn, DeployBeanProperty prop) {
DeployBeanPropertyAssocOne<?> beanProp = (DeployBeanPropertyAssocOne<?>) prop;
private void readManyToOne(ManyToOne propAnn, DeployBeanPropertyAssocOne<?> beanProp) {
setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo());
beanProp.setBeanTable(beanTable(beanProp));
setTargetType(propAnn.targetEntity(), beanProp);
setBeanTable(beanProp);
beanProp.setDbInsertable(true);
beanProp.setDbUpdateable(true);
beanProp.setNullable(propAnn.optional());
@@ -232,7 +215,8 @@ public class AnnotationAssocOnes extends AnnotationParser {
}
setCascadeTypes(propAnn.cascade(), prop.getCascadeInfo());
prop.setBeanTable(beanTable(prop));
setTargetType(propAnn.targetEntity(), prop);
setBeanTable(prop);
}
private boolean readOrphanRemoval(OneToOne property) {
@@ -261,7 +245,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
BeanTable baseBeanTable = factory.getBeanTable(info.getDescriptor().getBeanType());
String localPrimaryKey = baseBeanTable.getIdColumn();
String foreignColumn = beanTable(prop).getIdColumn();
String foreignColumn = getBeanTable(prop).getIdColumn();
prop.getTableJoin().addJoinColumn(new DeployTableJoinColumn(localPrimaryKey, foreignColumn, false, false));
}
@@ -145,14 +145,6 @@ public class DeployCreateProperties {
private DeployBeanProperty createProp(DeployBeanDescriptor<?> desc, Field field) {
Class<?> propertyType = field.getType();
ManyToOne manyToOne = AnnotationUtil.get(field, ManyToOne.class);
if (manyToOne != null) {
Class<?> tt = manyToOne.targetEntity();
if (!tt.equals(void.class)) {
propertyType = tt;
}
}
if (isSpecialScalarType(field)) {
return new DeployBeanProperty(desc, propertyType, field.getGenericType());
}
@@ -16,7 +16,7 @@ public class TestManyOneInterface extends BaseTestCase {
ResetBasicData.reset();
IAddress a = new Address();
IAddress a = new Address("hello");
IPerson p = new Person();
@@ -15,6 +15,10 @@ public class Address implements IAddress {
private String street;
public Address(String street) {
this.street = street;
}
public long getOid() {
return oid;
}
@@ -0,0 +1,6 @@
package org.tests.model.interfaces;
public interface IPersona {
String persona();
}
@@ -0,0 +1,39 @@
package org.tests.model.interfaces;
import javax.persistence.*;
@Entity
public class Persona implements IPersona {
@Id
private long id;
@Version
private int version;
private final String persona;
@OneToOne(orphanRemoval = true, fetch = FetchType.LAZY, targetEntity = Person.class)
private IPerson person;
public Persona(String persona) {
this.persona = persona;
}
public long getId() {
return id;
}
@Override
public String persona() {
return persona;
}
public void setPerson(IPerson person) {
this.person = person;
}
public IPerson getPerson() {
return person;
}
}
@@ -0,0 +1,38 @@
package org.tests.model.interfaces;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class TestTargetEntity extends BaseTestCase {
@Test
public void test() {
Person person = setup();
Persona persona = new Persona("junk");
persona.setPerson(person);
DB.save(persona);
Persona found = DB.find(Persona.class, persona.getId());
assertThat(found).isNotNull();
assertThat(found.persona()).isEqualTo("junk");
assertThat(found.getPerson().getDefaultAddress().getStreet()).isEqualTo("street");
DB.delete(persona);
DB.delete(person);
DB.delete(person.getDefaultAddress());
}
private Person setup() {
Address address = new Address("street");
DB.save(address);
Person person = new Person();
person.setDefaultAddress(address);
DB.save(person);
return person;
}
}