From 9d336cb20b33e551b5a0dd6e816a672030429cb0 Mon Sep 17 00:00:00 2001 From: yuanxuegui Date: Sat, 17 Mar 2018 13:13:39 +0800 Subject: [PATCH] ENH: Add mappingsLocation config to search xml mapping files. ENH: Add mappingsLocation config to search xml mapping files. ENH: Add mappingsLocation config to search xml mapping files. ENH: Add mappingsLocation config to search xml mapping files. ENH: Add mappingsLocation config to search xml mapping files. ENH: Add mappingsLocation config to search xml mapping files. remove unused import. ENH: Add mappingsLocation config and search xml mapping files feature. --- .../java/io/ebean/config/ServerConfig.java | 65 +++++++++++++------ .../server/deploy/BeanDescriptorManager.java | 39 ++++++++++- .../java/io/ebean/EbeanServer_eqlTest.java | 15 +++++ .../org/tests/rawsql/TestRawSqlOrmQuery.java | 26 ++++++++ .../ebean-xml-mappings/CustomerMapping.xml | 14 ++++ .../ebean-xml-mappings/OrderMapping.xml | 19 ++++++ src/test/resources/ebean.properties | 2 + 7 files changed, 159 insertions(+), 21 deletions(-) create mode 100644 src/test/resources/ebean-xml-mappings/CustomerMapping.xml create mode 100644 src/test/resources/ebean-xml-mappings/OrderMapping.xml diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java index 51221e96f..8d0d6f797 100644 --- a/src/main/java/io/ebean/config/ServerConfig.java +++ b/src/main/java/io/ebean/config/ServerConfig.java @@ -14,14 +14,7 @@ import io.ebean.config.dbplatform.DbEncrypt; import io.ebean.config.dbplatform.DbType; import io.ebean.config.dbplatform.IdType; import io.ebean.config.properties.PropertiesLoader; -import io.ebean.event.BeanFindController; -import io.ebean.event.BeanPersistController; -import io.ebean.event.BeanPersistListener; -import io.ebean.event.BeanPostConstructListener; -import io.ebean.event.BeanPostLoad; -import io.ebean.event.BeanQueryAdapter; -import io.ebean.event.BulkTableEventListener; -import io.ebean.event.ServerConfigStartup; +import io.ebean.event.*; import io.ebean.event.changelog.ChangeLogListener; import io.ebean.event.changelog.ChangeLogPrepare; import io.ebean.event.changelog.ChangeLogRegister; @@ -33,14 +26,7 @@ import io.ebean.util.StringHelper; import org.avaje.datasource.DataSourceConfig; import javax.sql.DataSource; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.ServiceLoader; +import java.util.*; /** * The configuration used for creating a EbeanServer. @@ -488,6 +474,12 @@ public class ServerConfig { */ private boolean defaultOrderById = false; + /** + * The mappingLocations for searching xml mapping. Only used when + * mappingLocations is empty/not explicitly specified. + */ + private List mappingLocations = new ArrayList<>(); + /** * Construct a Server Configuration for programmatically creating an EbeanServer. */ @@ -2782,7 +2774,7 @@ public class ServerConfig { if (packages != null) { String packagesProp = p.get("search.packages", p.get("packages", null)); - packages = getSearchJarsPackages(packagesProp); + packages = getSearchList(packagesProp); } collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", collectQueryStatsByNode); @@ -2862,6 +2854,12 @@ public class ServerConfig { tenantSchemaProvider = p.createInstance(TenantSchemaProvider.class, "tenant.schemaProvider", tenantSchemaProvider); tenantPartitionColumn = p.get("tenant.partitionColumn", tenantPartitionColumn); classes = getClasses(p); + + + if (mappingLocations != null) { + String mappingsProp = p.get("search.mappingsLocations", p.get("mappingsLocations", null)); + mappingLocations = getSearchList(mappingsProp); + } } private NamingConvention createNamingConvention(PropertiesWrapper properties, NamingConvention namingConvention) { @@ -2898,10 +2896,10 @@ public class ServerConfig { return classes; } - private List getSearchJarsPackages(String searchPackages) { + private List getSearchList(String searchNames) { - if (searchPackages != null) { - String[] entries = StringHelper.splitNames(searchPackages); + if (searchNames != null) { + String[] entries = StringHelper.splitNames(searchNames); List hitList = new ArrayList<>(entries.length); Collections.addAll(hitList, entries); @@ -3076,6 +3074,33 @@ public class ServerConfig { return config; } + /** + * Add a mapping location to search for xml mapping via class path search. + */ + public void addMappingLocation(String mappingLocation) { + if (mappingLocations == null) { + mappingLocations = new ArrayList<>(); + } + mappingLocations.add(mappingLocation); + } + + /** + * Return mapping locations to search for xml mapping via class path search. + */ + public List getMappingLocations() { + return mappingLocations; + } + + /** + * Set mapping locations to search for xml mapping via class path search. + *

+ * This is only used if classes have not been explicitly specified. + *

+ */ + public void setMappingLocations(List mappingLocations) { + this.mappingLocations = mappingLocations; + } + public enum UuidVersion { VERSION4, VERSION1, diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index 6bd12fda2..1f37d27b3 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -26,6 +26,7 @@ import io.ebeaninternal.api.ConcurrencyMode; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.TransactionEventTable; import io.ebeaninternal.server.cache.SpiCacheManager; +import io.ebeaninternal.server.core.ClassPathScanners; import io.ebeaninternal.server.core.InternString; import io.ebeaninternal.server.core.InternalConfiguration; import io.ebeaninternal.server.core.Message; @@ -65,6 +66,9 @@ import io.ebeaninternal.xmlmapping.model.XmNamedQuery; import io.ebeaninternal.xmlmapping.model.XmRawSql; import io.ebeanservice.docstore.api.DocStoreBeanAdapter; import io.ebeanservice.docstore.api.DocStoreFactory; +import org.avaje.classpath.scanner.ClassPathScanner; +import org.avaje.classpath.scanner.Resource; +import org.avaje.classpath.scanner.ResourceFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,6 +76,7 @@ import javax.persistence.MappedSuperclass; import javax.persistence.PersistenceException; import javax.persistence.Transient; import javax.sql.DataSource; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; @@ -416,6 +421,13 @@ public class BeanDescriptorManager implements BeanDescriptorMap { } } + List xmlMappingResources = searchXmlMapping(); + for (Resource xmlMappingRes : xmlMappingResources) { + try (InputStream is = new FileInputStream(xmlMappingRes.getLocationOnDisk())) { + mappings.add(XmlMappingReader.read(is)); + } + } + for (XmEbean mapping : mappings) { List entityDeploy = mapping.getEntity(); for (XmEntity deploy : entityDeploy) { @@ -424,10 +436,35 @@ public class BeanDescriptorManager implements BeanDescriptorMap { } } catch (IOException e) { - throw new RuntimeException("Error reading ebean.xml", e); + throw new RuntimeException("Error reading ebean xml mapping", e); } } + private List searchXmlMapping() { + List scanners = ClassPathScanners.find(serverConfig); + List mappingLocations = serverConfig.getMappingLocations(); + List resourceList = new ArrayList<>(); + + long st = System.currentTimeMillis(); + if (mappingLocations != null && !mappingLocations.isEmpty()) { + for (ClassPathScanner finder : scanners) { + for (String mappingLocation : mappingLocations) { + resourceList.addAll(finder.scanForResources(mappingLocation, new ResourceFilter() { + + @Override + public boolean isMatch(String resourceName) { + return resourceName.endsWith(".xml"); + } + })); + } + } + } + + long searchTime = System.currentTimeMillis() - st; + logger.debug("Classpath search mappings[{}] searchTime[{}]", resourceList.size(), searchTime); + return resourceList; + } + private void readEntityMapping(ClassLoader classLoader, XmEntity entityDeploy) { String entityClassName = entityDeploy.getClazz(); diff --git a/src/test/java/io/ebean/EbeanServer_eqlTest.java b/src/test/java/io/ebean/EbeanServer_eqlTest.java index a29e00bad..75c0c1f4f 100644 --- a/src/test/java/io/ebean/EbeanServer_eqlTest.java +++ b/src/test/java/io/ebean/EbeanServer_eqlTest.java @@ -250,4 +250,19 @@ public class EbeanServer_eqlTest extends BaseTestCase { assertThat(query.getGeneratedSql()).contains("from o_customer t0 left join contact t1 on t1.customer_id = t0.id "); } + + @Test + public void namedQuery_fromCustomXmlLocations() { + + ResetBasicData.reset(); + + Query query = server() + .createNamedQuery(Customer.class, "withContactsById2") + .setParameter("id", 1); + + query.setUseCache(false); + query.findOne(); + + assertThat(query.getGeneratedSql()).contains("from o_customer t0 left join contact t1 on t1.customer_id = t0.id "); + } } diff --git a/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java b/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java index 51e3d53c5..cabde9917 100644 --- a/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java +++ b/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java @@ -51,6 +51,32 @@ public class TestRawSqlOrmQuery extends BaseTestCase { assertThat(sql).contains("where o.status = ? order by c.name, c.id"); } + @IgnorePlatform(Platform.ORACLE) + @Test + public void testNamed_fromCustomXmlLocations() { + + ResetBasicData.reset(); + + Query query = Ebean.createNamedQuery(Order.class, "myRawTest2"); + query.setParameter("orderStatus", Order.Status.NEW); + query.setMaxRows(10); + List list = query.findList(); + for (Order order : list) { + order.getCretime(); + } + + String sql = query.getGeneratedSql(); + if (isSqlServer()) { + assertThat(sql).contains("select top 10 o.id,"); + } else { + assertThat(sql).contains("select o.id,"); + assertThat(sql).contains("limit 10"); + } + assertThat(sql).contains("o.id, o.status, o.ship_date, c.id, c.name, a.id, a.line_1, a.line_2, a.city from o_order o"); + assertThat(sql).contains("join o_customer c on o.kcustomer_id = c.id "); + assertThat(sql).contains("where o.status = ? order by c.name, c.id"); + } + @Test public void test() { diff --git a/src/test/resources/ebean-xml-mappings/CustomerMapping.xml b/src/test/resources/ebean-xml-mappings/CustomerMapping.xml new file mode 100644 index 000000000..b55626fbf --- /dev/null +++ b/src/test/resources/ebean-xml-mappings/CustomerMapping.xml @@ -0,0 +1,14 @@ + + + + + + + select (name, version) + fetch contacts (firstName, lastName, email) + where id = :id + + + + + diff --git a/src/test/resources/ebean-xml-mappings/OrderMapping.xml b/src/test/resources/ebean-xml-mappings/OrderMapping.xml new file mode 100644 index 000000000..cd35e81a9 --- /dev/null +++ b/src/test/resources/ebean-xml-mappings/OrderMapping.xml @@ -0,0 +1,19 @@ + + + + + + + + + select o.id, o.status, o.ship_date, c.id, c.name, a.id, a.line_1, a.line_2, a.city + from o_order o + join o_customer c on o.kcustomer_id = c.id + join o_address a on c.billing_address_id = a.id + where o.status = :orderStatus + order by c.name, c.id + + + + + diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 3b048c4c2..3616698d1 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -168,3 +168,5 @@ ebean.migrationtest.ddl.header=-- Migrationscripts for ebean unittest ebean.migrationtest.migration.appName=migrationtest ebean.migrationtest.migration.migrationPath=dbmigration/migrationtest ebean.migrationtest.migration.strict=true + +ebean.mappingsLocations=classpath:/ebean-xml-mappings