diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index f6f927a645..23f05b2e99 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -237,7 +237,7 @@ public BeanDescriptor(BeanDescriptorMap owner, DeployBeanDescriptor deploy) { this.owner = owner; this.multiValueSupported = owner.isMultiValueSupported(); this.entityType = deploy.getEntityType(); - this.properties = deploy.getProperties(); + this.properties = deploy.propertyNames(); this.name = InternString.intern(deploy.getName()); this.baseTableAlias = "t0"; this.fullName = InternString.intern(deploy.getFullName()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index c6f50d9dde..c362ed5220 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -443,11 +443,10 @@ private void readTableToDescriptor() { // build map of tables to view entities dependent on those tables // for the purpose of invalidating appropriate query caches String[] dependentTables = desc.dependentTables(); - if (dependentTables != null && dependentTables.length > 0) { + if (dependentTables != null) { for (String depTable : dependentTables) { depTable = depTable.toLowerCase(); - List> list = tableToViewDescMap.computeIfAbsent(depTable, k -> new ArrayList<>(1)); - list.add(desc); + tableToViewDescMap.computeIfAbsent(depTable, k -> new ArrayList<>(1)).add(desc); } } } @@ -578,7 +577,7 @@ private void errorBeanNotRegistered(Class entityType) { } } - private String errNothingRegistered() { + private String errNothingRegistered() { return "There are no registered entities. If using query beans, that generates EbeanEntityRegister.java into " + "generated sources and is service loaded. If using module-info.java, then probably missing 'provides io.ebean.config.EntityClassRegister with EbeanEntityRegister' clause."; } @@ -1186,7 +1185,7 @@ private void readDeployAssociations(DeployBeanInfo info) { setConcurrencyMode(desc); } // generate the byte code - createByteCode(desc); + setAccessors(desc); } /** @@ -1255,13 +1254,13 @@ private PlatformIdGenerator createSequenceIdGenerator(String seqName, int stepSi return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, stepSize, seqName); } - private void createByteCode(DeployBeanDescriptor deploy) { + private void setAccessors(DeployBeanDescriptor deploy) { // check to see if the bean supports EntityBean interface // generate a subclass if required - setEntityBeanClass(deploy); + confirmEnhanced(deploy); // use Code generation or Standard reflection to support // getter and setter methods - setBeanReflect(deploy); + setPropertyAccessors(deploy); } /** @@ -1288,15 +1287,14 @@ private void setScalarType(DeployBeanDescriptor deployDesc) { * and getting of properties. It is generally faster to use code generation * rather than reflection to do this. */ - private void setBeanReflect(DeployBeanDescriptor desc) { + private void setPropertyAccessors(DeployBeanDescriptor desc) { // Set the BeanReflectGetter and BeanReflectSetter that typically // use generated code. NB: Due to Bug 166 so now doing this for // abstract classes as well. - BeanPropertiesReader reflectProps = new BeanPropertiesReader(desc.getBeanType()); - desc.setProperties(reflectProps.getProperties()); + BeanPropertiesReader reflectProps = new BeanPropertiesReader(desc.propertyNames()); for (DeployBeanProperty prop : desc.propertiesAll()) { String propName = prop.getName(); - Integer pos = reflectProps.getPropertyIndex(propName); + Integer pos = reflectProps.propertyIndex(propName); if (pos == null) { if (isPersistentField(prop)) { throw new IllegalStateException( @@ -1368,7 +1366,7 @@ private boolean hasEntityBeanInterface(Class beanClass) { /** * Test the bean type to see if it implements EntityBean interface already. */ - private void setEntityBeanClass(DeployBeanDescriptor desc) { + private void confirmEnhanced(DeployBeanDescriptor desc) { Class beanClass = desc.getBeanType(); if (!hasEntityBeanInterface(beanClass)) { String msg = "Bean " + beanClass + " is not enhanced? Check packages specified in ebean.mf. If you are running in IDEA or " + diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index 6aa5186b7b..f9715f2aab 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -24,6 +24,8 @@ import jakarta.persistence.Entity; import jakarta.persistence.MappedSuperclass; + +import java.lang.reflect.Field; import java.util.*; /** @@ -45,8 +47,6 @@ public int compare(DeployBeanProperty o1, DeployBeanProperty o2) { private static final PropOrder PROP_ORDER = new PropOrder(); - private static final String I_SCALAOBJECT = "scala.ScalaObject"; - private final DatabaseConfig config; private final BeanDescriptorManager manager; /** @@ -139,10 +139,30 @@ public DeployBeanDescriptor(BeanDescriptorManager manager, Class beanType, Da this.beanType = beanType; } + private String[] readPropertyNames() { + try { + Field field = beanType.getField("_ebean_props"); + return (String[]) field.get(null); + } catch (Exception e) { + throw new IllegalStateException("Error getting _ebean_props field on type " + beanType, e); + } + } + + public void setPropertyNames(String[] properties) { + this.properties = properties; + } + + public String[] propertyNames() { + if (properties == null) { + properties = readPropertyNames(); + } + return properties; + } + /** * Set the IdClass to use. */ - public void setIdClass(Class idClass) { + public void setIdClass(Class idClass) { this.idClass = idClass; } @@ -260,7 +280,6 @@ public boolean isDraftableElement() { * Read the top level doc store deployment information. */ public void readDocStore(DocStore docStore) { - this.docStore = docStore; docStoreMapped = true; docStoreQueueId = docStore.queueId(); @@ -276,23 +295,10 @@ public void readDocStore(DocStore docStore) { } } - public boolean isScalaObject() { - Class[] interfaces = beanType.getInterfaces(); - for (Class anInterface : interfaces) { - String iname = anInterface.getName(); - if (I_SCALAOBJECT.equals(iname)) { - return true; - } - } - return false; - } - public DeployBeanTable createDeployBeanTable() { - DeployBeanTable beanTable = new DeployBeanTable(getBeanType()); beanTable.setBaseTable(baseTable); beanTable.setIdProperty(idProperty()); - return beanTable; } @@ -360,14 +366,6 @@ public void setIdentityType(IdType type) { this.identityMode.setIdType(type); } - public String[] getProperties() { - return properties; - } - - public void setProperties(String[] props) { - this.properties = props; - } - /** * Return the class type this BeanDescriptor describes. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java index e1a065a2ec..fc15c0f503 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java @@ -213,9 +213,9 @@ private void readElementCollection(DeployBeanPropertyAssocMany prop, ElementC int sortOrder = 0; if (!prop.getManyType().isMap()) { - elementDescriptor.setProperties(new String[]{"value"}); + elementDescriptor.setPropertyNames(new String[]{"value"}); } else { - elementDescriptor.setProperties(new String[]{"key", "value"}); + elementDescriptor.setPropertyNames(new String[]{"key", "value"}); String dbKeyColumn = "mkey"; MapKeyColumn mapKeyColumn = get(prop, MapKeyColumn.class); if (mapKeyColumn != null) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/properties/BeanPropertiesReader.java b/ebean-core/src/main/java/io/ebeaninternal/server/properties/BeanPropertiesReader.java index ec56da19de..d72d2a1028 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/properties/BeanPropertiesReader.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/properties/BeanPropertiesReader.java @@ -1,7 +1,5 @@ package io.ebeaninternal.server.properties; -import java.lang.reflect.Field; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -11,34 +9,14 @@ public final class BeanPropertiesReader { private final Map propertyIndexMap = new HashMap<>(); - private final String[] props; - public BeanPropertiesReader(Class clazz) { - this.props = getProperties(clazz); + public BeanPropertiesReader(String[] props) { for (int i = 0; i < props.length; i++) { propertyIndexMap.put(props[i], i); } } - public String[] getProperties() { - return props; - } - - @Override - public String toString() { - return Arrays.toString(props); - } - - public Integer getPropertyIndex(String property) { + public Integer propertyIndex(String property) { return propertyIndexMap.get(property); } - - private String[] getProperties(Class clazz) { - try { - Field field = clazz.getField("_ebean_props"); - return (String[]) field.get(null); - } catch (Exception e) { - throw new IllegalStateException("Error getting _ebean_props field on type " + clazz, e); - } - } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/deploy/parse/AnnotationClassTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/deploy/parse/AnnotationClassTest.java index 72e88a0135..516081d3f3 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/deploy/parse/AnnotationClassTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/deploy/parse/AnnotationClassTest.java @@ -7,6 +7,7 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; import io.ebeaninternal.server.type.DefaultTypeManager; import org.junit.jupiter.api.Test; +import org.tests.model.basic.Customer; import java.util.Collections; @@ -47,7 +48,7 @@ public void convertColumnNames_when_AllQuotedIdentifiersIsFalse() { private AnnotationClass createAnnotationClass(DatabaseConfig config) { DeployUtil deployUtil = new DeployUtil(new DefaultTypeManager(config, new BootupClasses()), config); - DeployBeanInfo deployBeanInfo = new DeployBeanInfo(deployUtil, new DeployBeanDescriptor<>(null, null, null)); + DeployBeanInfo deployBeanInfo = new DeployBeanInfo(deployUtil, new DeployBeanDescriptor<>(null, Customer.class, null)); ReadAnnotationConfig readAnnotationConfig = new ReadAnnotationConfig(new GeneratedPropertyFactory(true, new DatabaseConfig(), Collections.emptyList()), "","", new DatabaseConfig()); return new AnnotationClass(deployBeanInfo, readAnnotationConfig); } diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index c626c8f4d8..1c0dca3422 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -144,7 +144,7 @@ 11 11 - -proc:none + -proc:none diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index 4154d4d68d..325413e51a 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -31,7 +31,7 @@ 11 11 - -proc:none + -proc:none diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java index 149fe62ecc..250323f5e3 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java @@ -475,6 +475,11 @@ FileObject createManifestWriter() throws IOException { return createMetaInfWriter(METAINF_MANIFEST); } + FileObject createNativeImageWriter(String name) throws IOException { + String nm = "META-INF/native-image/" + name + "/reflect-config.json"; + return createMetaInfWriter(nm); + } + FileObject createMetaInfWriter(String target) throws IOException { return filer.createResource(StandardLocation.CLASS_OUTPUT, "", target); } diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleModuleInfoWriter.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleModuleInfoWriter.java index d609939b57..c238541785 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleModuleInfoWriter.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleModuleInfoWriter.java @@ -4,6 +4,7 @@ import javax.tools.JavaFileObject; import java.io.IOException; import java.io.Writer; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.StringJoiner; @@ -38,6 +39,7 @@ void write() throws IOException { writer.close(); writeServicesFile(); writeManifestFile(); + writeNativeImageFile(); } private void writeServicesFile() { @@ -48,9 +50,7 @@ private void writeServicesFile() { writer.write(factoryFullName); writer.close(); } - } catch (IOException e) { - e.printStackTrace(); processingContext.logError(null, "Failed to write services file " + e.getMessage()); } } @@ -68,9 +68,7 @@ private void writeManifestFile() { writer.close(); } } - } catch (IOException e) { - e.printStackTrace(); processingContext.logError(null, "Failed to write services file " + e.getMessage()); } } @@ -84,10 +82,41 @@ private String manifestEntityPackages(Set allEntityPackages) { return builder.delete(builder.lastIndexOf("\n"), builder.length()).append('\n').toString(); } - private void writePackage() { + private void writeNativeImageFile() { + try { + Set allEntities = new LinkedHashSet<>(processingContext.getDbEntities()); + for (Set value : processingContext.getOtherDbEntities().values()) { + allEntities.addAll(value); + } - writer.append("package %s;", factoryPackage).eol().eol(); + if (!allEntities.isEmpty()) { + FileObject jfo = processingContext.createNativeImageWriter(factoryPackage + ".ebean-entity"); + if (jfo != null) { + boolean first = true; + Writer writer = jfo.openWriter(); + writer.write("["); + for (String entity : allEntities) { + if (first) { + first = false; + } else { + writer.write(","); + } + writer.write("\n {\"name\": \""); + writer.write(entity); + writer.write("\", \"allDeclaredConstructors\": true, \"allDeclaredFields\": true}"); + } + writer.write("\n]\n"); + writer.write("\n"); + writer.close(); + } + } + } catch (IOException e) { + processingContext.logError(null, "Failed to write services file " + e.getMessage()); + } + } + private void writePackage() { + writer.append("package %s;", factoryPackage).eol().eol(); writer.append("import java.util.ArrayList;").eol(); writer.append("import java.util.Collections;").eol(); writer.append("import java.util.List;").eol(); @@ -125,7 +154,6 @@ private String quoteTypes(Set otherClasses) { } private void writeStartClass() { - buildAtContextModule(writer); writer.append("public class %s implements EntityClassRegister {", factoryShortName).eol().eol();