#115 - Mapping - Add support for @ElementCollection enhancement

Initial support - simple List.
This commit is contained in:
Rob Bygrave
2018-03-20 14:22:29 +13:00
parent c44931ea25
commit 74a9019535
79 changed files with 1891 additions and 805 deletions
@@ -7,18 +7,24 @@ import io.ebean.annotation.Where;
import io.ebean.bean.BeanCollection.ModifyListenMode;
import io.ebean.config.NamingConvention;
import io.ebean.config.TableName;
import io.ebean.util.CamelCaseHelper;
import io.ebean.util.StringHelper;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanTable;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.meta.DeployOrderColumn;
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.type.ScalarType;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
@@ -38,8 +44,8 @@ class AnnotationAssocManys extends AnnotationParser {
/**
* Create with the DeployInfo.
*/
AnnotationAssocManys(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
super(info, javaxValidationAnnotations);
AnnotationAssocManys(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
super(info, readConfig);
this.factory = factory;
}
@@ -82,6 +88,10 @@ class AnnotationAssocManys extends AnnotationParser {
if (manyToMany != null) {
readToMany(manyToMany, prop);
}
ElementCollection elementCollection = get(prop, ElementCollection.class);
if (elementCollection != null) {
readElementCollection(prop, elementCollection);
}
if (get(prop, HistoryExclude.class) != null) {
prop.setExcludedFromHistory();
@@ -160,6 +170,59 @@ class AnnotationAssocManys extends AnnotationParser {
}
}
private void readElementCollection(DeployBeanPropertyAssocMany<?> prop, ElementCollection elementCollection) {
prop.setElementCollection();
if (!elementCollection.targetClass().equals(void.class)) {
prop.setTargetType(elementCollection.targetClass());
}
Column column = get(prop, Column.class);
if (column != null) {
prop.setDbColumn(column.name());
prop.setDbLength(column.length());
prop.setDbScale(column.scale());
}
CollectionTable collectionTable = get(prop, CollectionTable.class);
String fullTableName = getFullTableName(collectionTable);
if (fullTableName == null) {
fullTableName = descriptor.getBaseTable()+"_"+ CamelCaseHelper.toUnderscoreFromCamel(prop.getName());
}
//namingConvention.
BeanTable beanTable = factory.getCollectionBeanTable(fullTableName, prop.getTargetType());
prop.setBeanTable(beanTable);
if (collectionTable != null) {
prop.getTableJoin().addJoinColumn(true, collectionTable.joinColumns(), beanTable);
}
Class<?> elementType = prop.getTargetType();
DeployBeanDescriptor<?> elementDescriptor = factory.createDeployDescriptor(elementType);
elementDescriptor.setBaseTable(new TableName(fullTableName), readConfig.getAsOfViewSuffix(), readConfig.getVersionsBetweenSuffix());
ScalarType<?> scalarType = util.getTypeManager().getScalarType(elementType);
DeployBeanProperty elementProp = new DeployBeanProperty(elementDescriptor, elementType, scalarType, null);
elementProp.setName("value");
elementProp.setDbColumn(prop.getDbColumn());
elementProp.setNullable(false);
elementProp.setDbInsertable(true);
elementProp.setDbUpdateable(true);
elementProp.setDbRead(true);
elementProp.setElementProperty();
elementDescriptor.addBeanProperty(elementProp);
elementDescriptor.setProperties(new String[]{"value"});
elementDescriptor.setName(prop.getFullBeanName());
Class<?> owningType = prop.getOwningType();
factory.createUnidirectional(elementDescriptor, owningType, beanTable, prop.getTableJoin());
prop.setElementDescriptor(factory.createElementDescriptor(elementDescriptor));
}
/**
* Define the joins for a ManyToMany relationship.
* <p>
@@ -204,6 +267,24 @@ class AnnotationAssocManys extends AnnotationParser {
return sb.toString();
}
/**
* Return the full table name
*/
private String getFullTableName(CollectionTable collectionTable) {
if (collectionTable == null) {
return null;
}
StringBuilder sb = new StringBuilder();
if (!StringHelper.isNull(collectionTable.catalog())) {
sb.append(collectionTable.catalog()).append(".");
}
if (!StringHelper.isNull(collectionTable.schema())) {
sb.append(collectionTable.schema()).append(".");
}
sb.append(collectionTable.name());
return sb.toString();
}
/**
* Define intersection table and foreign key columns for ManyToMany.
* <p>
@@ -41,8 +41,8 @@ public class AnnotationAssocOnes extends AnnotationParser {
/**
* Create with the deploy Info.
*/
AnnotationAssocOnes(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
super(info, javaxValidationAnnotations);
AnnotationAssocOnes(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
super(info, readConfig);
this.factory = factory;
}
@@ -152,7 +152,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
}
}
info.setBeanJoinType(prop, prop.isNullable());
prop.setJoinType(prop.isNullable());
if (!prop.getTableJoin().hasJoinColumns() && beanTable != null) {
@@ -42,25 +42,15 @@ public class AnnotationClass extends AnnotationParser {
private final boolean disableL2Cache;
/**
* Create for normal early parse of class level annotations.
*/
public AnnotationClass(DeployBeanInfo<?> info, boolean validationAnnotations, String asOfViewSuffix, String versionsBetweenSuffix, boolean disableL2Cache) {
super(info, validationAnnotations);
this.asOfViewSuffix = asOfViewSuffix;
this.versionsBetweenSuffix = versionsBetweenSuffix;
this.disableL2Cache = disableL2Cache;
}
/**
* Create to parse AttributeOverride annotations which is run last
* after all the properties/fields have been parsed fully.
*/
public AnnotationClass(DeployBeanInfo<?> info) {
super(info, false);
this.asOfViewSuffix = null;
this.versionsBetweenSuffix = null;
this.disableL2Cache = false;
public AnnotationClass(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
super(info, readConfig);
this.asOfViewSuffix = readConfig.getAsOfViewSuffix();
this.versionsBetweenSuffix = readConfig.getVersionsBetweenSuffix();
this.disableL2Cache = readConfig.isDisableL2Cache();
}
/**
@@ -91,14 +91,11 @@ public class AnnotationFields extends AnnotationParser {
*/
private FetchType defaultLobFetchType = FetchType.LAZY;
AnnotationFields(GeneratedPropertyFactory generatedPropFactory, DeployBeanInfo<?> info,
boolean javaxValidationAnnotations, boolean jacksonAnnotationsPresent, boolean eagerFetchLobs) {
super(info, javaxValidationAnnotations);
this.jacksonAnnotationsPresent = jacksonAnnotationsPresent;
this.generatedPropFactory = generatedPropFactory;
if (eagerFetchLobs) {
AnnotationFields(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
super(info, readConfig);
this.jacksonAnnotationsPresent = readConfig.isJacksonAnnotations();
this.generatedPropFactory = readConfig.getGeneratedPropFactory();
if (readConfig.isEagerFetchLobs()) {
defaultLobFetchType = FetchType.EAGER;
}
}
@@ -27,9 +27,12 @@ public abstract class AnnotationParser extends AnnotationBase {
protected final boolean validationAnnotations;
public AnnotationParser(DeployBeanInfo<?> info, boolean validationAnnotations) {
protected final ReadAnnotationConfig readConfig;
public AnnotationParser(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
super(info.getUtil());
this.validationAnnotations = validationAnnotations;
this.readConfig = readConfig;
this.validationAnnotations = readConfig.isJavaxValidationAnnotations();
this.info = info;
this.beanType = info.getDescriptor().getBeanType();
this.descriptor = info.getDescriptor();
@@ -7,10 +7,10 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
/**
* Read the class level deployment annotations.
*/
public class AnnotationSql extends AnnotationParser {
class AnnotationSql extends AnnotationParser {
public AnnotationSql(DeployBeanInfo<?> info, boolean javaxValidationAnnotations) {
super(info, javaxValidationAnnotations);
AnnotationSql(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
super(info, readConfig);
}
@Override
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.deploy.parse;
import io.ebean.RawSql;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.rawsql.SpiRawSql;
@@ -70,15 +69,6 @@ public class DeployBeanInfo<T> {
return tableJoin;
}
/**
* Set a the join alias for a assoc one property.
*/
public void setBeanJoinType(DeployBeanPropertyAssocOne<?> beanProp, boolean outerJoin) {
DeployTableJoin tableJoin = beanProp.getTableJoin();
tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER);
}
/**
* Add named RawSql from ebean.xml.
*/
@@ -0,0 +1,58 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.config.ServerConfig;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
/**
* Configuration used when reading the deployment annotations.
*/
class ReadAnnotationConfig {
private final GeneratedPropertyFactory generatedPropFactory;
private final String asOfViewSuffix;
private final String versionsBetweenSuffix;
private final boolean disableL2Cache;
private final boolean eagerFetchLobs;
private final boolean javaxValidationAnnotations;
private final boolean jacksonAnnotations;
ReadAnnotationConfig(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, ServerConfig serverConfig) {
this.generatedPropFactory = generatedPropFactory;
this.asOfViewSuffix = asOfViewSuffix;
this.versionsBetweenSuffix = versionsBetweenSuffix;
this.disableL2Cache = serverConfig.isDisableL2Cache();
this.eagerFetchLobs = serverConfig.isEagerFetchLobs();
this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent();
this.jacksonAnnotations = generatedPropFactory.getClassLoadConfig().isJacksonAnnotationsPresent();
}
GeneratedPropertyFactory getGeneratedPropFactory() {
return generatedPropFactory;
}
String getAsOfViewSuffix() {
return asOfViewSuffix;
}
String getVersionsBetweenSuffix() {
return versionsBetweenSuffix;
}
boolean isDisableL2Cache() {
return disableL2Cache;
}
boolean isEagerFetchLobs() {
return eagerFetchLobs;
}
boolean isJavaxValidationAnnotations() {
return javaxValidationAnnotations;
}
boolean isJacksonAnnotations() {
return jacksonAnnotations;
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.config.ServerConfig;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
@@ -9,38 +10,10 @@ import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory
*/
public class ReadAnnotations {
/**
* Creates appropriate generated properties - WhenXXX, WhoXXX, Version etc.
*/
private final GeneratedPropertyFactory generatedPropFactory;
private final ReadAnnotationConfig readConfig;
/**
* Typically _with_history and when appended to the base table derives the name of
* the view that unions the base table with the history table to support asOf queries.
*/
private final String asOfViewSuffix;
private final String versionsBetweenSuffix;
/**
* True if the javax validation annotations are present in the classpath.
*/
private final boolean javaxValidationAnnotations;
/**
* True if the jackson annotations are present in the classpath.
*/
private final boolean jacksonAnnotations;
private final boolean disableL2Cache;
public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, boolean disableL2Cache) {
this.generatedPropFactory = generatedPropFactory;
this.asOfViewSuffix = asOfViewSuffix;
this.versionsBetweenSuffix = versionsBetweenSuffix;
this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent();
this.jacksonAnnotations = generatedPropFactory.getClassLoadConfig().isJacksonAnnotationsPresent();
this.disableL2Cache = disableL2Cache;
public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, ServerConfig serverConfig) {
this.readConfig = new ReadAnnotationConfig(generatedPropFactory, asOfViewSuffix, versionsBetweenSuffix, serverConfig);
}
/**
@@ -50,12 +23,10 @@ public class ReadAnnotations {
* to resolve the relationships etc.
* </p>
*/
public void readInitial(DeployBeanInfo<?> info, boolean eagerFetchLobs) {
public void readInitial(DeployBeanInfo<?> info) {
try {
new AnnotationClass(info, javaxValidationAnnotations, asOfViewSuffix, versionsBetweenSuffix, disableL2Cache).parse();
new AnnotationFields(generatedPropFactory, info, javaxValidationAnnotations, jacksonAnnotations, eagerFetchLobs).parse();
new AnnotationClass(info, readConfig).parse();
new AnnotationFields(info, readConfig).parse();
} catch (RuntimeException e) {
throw new RuntimeException("Error reading annotations for " + info, e);
}
@@ -75,14 +46,14 @@ public class ReadAnnotations {
try {
new AnnotationAssocOnes(info, javaxValidationAnnotations, factory).parse();
new AnnotationAssocManys(info, javaxValidationAnnotations, factory).parse();
new AnnotationAssocOnes(info, readConfig, factory).parse();
new AnnotationAssocManys(info, readConfig, factory).parse();
// read the Sql annotations last because they may be
// dependent on field level annotations
new AnnotationSql(info, javaxValidationAnnotations).parse();
new AnnotationSql(info, readConfig).parse();
new AnnotationClass(info).parseAttributeOverride();
new AnnotationClass(info, readConfig).parseAttributeOverride();
info.getDescriptor().postAnnotations();
} catch (RuntimeException e) {
@@ -5,8 +5,6 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import java.util.List;
/**
* Mark transient properties.
*/
@@ -20,29 +18,22 @@ public class TransientProperties {
*/
public void process(DeployBeanDescriptor<?> desc) {
List<DeployBeanProperty> props = desc.propertiesBase();
for (DeployBeanProperty prop : props) {
for (DeployBeanProperty prop : desc.propertiesBase()) {
if (!prop.isDbRead() && !prop.isDbInsertable() && !prop.isDbUpdateable()) {
// non-transient...
prop.setTransient();
}
}
List<DeployBeanPropertyAssocOne<?>> ones = desc.propertiesAssocOne();
for (DeployBeanPropertyAssocOne<?> prop : ones) {
if (prop.getBeanTable() == null) {
if (!prop.isEmbedded()) {
prop.setTransient();
}
}
}
List<DeployBeanPropertyAssocMany<?>> manys = desc.propertiesAssocMany();
for (DeployBeanPropertyAssocMany<?> prop : manys) {
if (prop.getBeanTable() == null) {
for (DeployBeanPropertyAssocOne<?> prop : desc.propertiesAssocOne()) {
if (prop.getBeanTable() == null && !prop.isEmbedded()) {
prop.setTransient();
}
}
for (DeployBeanPropertyAssocMany<?> prop : desc.propertiesAssocMany()) {
if (prop.getBeanTable() == null) {
prop.setTransient();
}
}
}
}