Compare commits

..
Author SHA1 Message Date
Robin Bygrave 8115c5e230 [maven-release-plugin] prepare release avaje-ebeanorm-7.18.1 2016-07-11 21:31:25 +12:00
Robin Bygrave ad180c5531 No effective change - reduce test logging 2016-07-11 21:27:34 +12:00
Robin Bygrave b454f46dd4 Bump pom to 7.18.1-SNAPSHOT 2016-07-11 21:27:06 +12:00
Robin Bygrave 8a853b28f3 #772 - DB Migration DDL - alter table add column ... needs type conversion when using multiple platforms. e.g. varchar(20) ... convert to varchar2(20) for Oracle 2016-07-11 21:25:58 +12:00
Robin Bygrave a1be7ba5dd #771 - DB Migration DDL when using multiple platforms ... destination into sub directories rather than file suffix 2016-07-11 21:17:12 +12:00
Robin Bygrave 3914516793 #770 - DB Migration DDL generation run offline when using multiple platforms 2016-07-11 20:20:26 +12:00
Robin Bygrave 8c5854f77c #770 - DB Migration DDL generation run offline when using multiple platforms 2016-07-11 20:14:29 +12:00
Robin Bygrave 0bc5b94eae #770 - DB Migration DDL generation run offline when using multiple platforms 2016-07-11 20:08:26 +12:00
Robin Bygrave 12fa287f35 #769 - DB Migration DDL generation for UUID type when generating multiple platforms 2016-07-11 19:54:50 +12:00
Robin Bygrave 2991cb9ad1 #768 - Query.setDisableLazyLoading(true) ... not disabling lazy loading on @ManyToOne reference bean 2016-07-11 15:09:59 +12:00
Robin Bygrave 3b7f5496a8 #767 - Query.setDisableLazyLoading(true) ... not disabling lazy loading on @OneToMany 2016-07-11 12:28:31 +12:00
Robin Bygrave 4be703ff6e #767 - Query.setDisableLazyLoading(true) ... not disabling lazy loading on @OneToMany 2016-07-11 11:58:02 +12:00
Robin Bygrave d2c5765ac1 No effective change - tidy comments 2016-07-11 11:24:57 +12:00
Robin Bygrave aaf256b9be #766 - RawSql should automatically map column alias with underscore to camelCase ... so item_total should automatically map to bean property itemTotal 2016-07-08 23:44:11 +12:00
Robin Bygrave c7c6410cd9 #765 - ENH: Add ebean.xml with support for named RawSql, also adds EbeanServer.createNamedQuery() 2016-07-08 22:35:04 +12:00
Robin Bygrave 2c3c23725a No effective change - reformat code 2016-07-08 15:37:26 +12:00
Robin Bygrave 92069c0319 #764 - NPE on BeanDescriptorManager.processViewInvalidation(BeanDescriptorManager.java:385) 2016-07-08 15:34:43 +12:00
Robin Bygrave 630ad66ff1 #763 - Internal Code cleanup - remove redundant null checking etc 2016-07-08 15:01:39 +12:00
Robin Bygrave b732b390c7 [maven-release-plugin] prepare for next development iteration 2016-07-07 21:51:15 +12:00
Robin Bygrave 8c62fcdcb2 [maven-release-plugin] prepare release avaje-ebeanorm-7.17.1 2016-07-07 21:50:53 +12:00
Robin Bygrave e458f577fe No effective change - reduce logging for BeanDescriptorManager entity count message from INFO to DEBUG 2016-07-07 21:49:43 +12:00
Robin Bygrave 23434bab8c Bump pom to 7.17.1-SNAPSHOT 2016-07-07 21:45:40 +12:00
Robin Bygrave c47b5a1595 #762 - Change H2 Platform to IDENTITY (from SEQUENCE) with version 1.4.192 2016-07-07 21:43:15 +12:00
Robin Bygrave 411846347b #762 - Change H2 Platform to IDENTITY (from SEQUENCE) with version 1.4.192 2016-07-07 21:33:19 +12:00
Robin Bygrave 3722d19424 #761 - Change default for persistBatchOnCascade to be platform specific rather than NONE 2016-07-07 21:19:08 +12:00
Robin Bygrave 04a7310e5d [maven-release-plugin] prepare for next development iteration 2016-07-06 12:27:45 +12:00
105 changed files with 2262 additions and 890 deletions
+3 -3
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>7.16.3</version>
<version>7.18.1</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -37,7 +37,7 @@
<scm>
<developerConnection>scm:git:https://github.com/ebean-orm/avaje-ebeanorm.git</developerConnection>
<tag>avaje-ebeanorm-7.16.3</tag>
<tag>avaje-ebeanorm-7.18.1</tag>
</scm>
<dependencies>
@@ -177,7 +177,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.189</version>
<version>1.4.192</version>
<scope>provided</scope>
</dependency>
@@ -38,6 +38,11 @@ public interface BeanState {
*/
void setDisableLazyLoad(boolean disableLazyLoading);
/**
* Return true if the bean has lazy loading disabled.
*/
boolean isDisableLazyLoad();
/**
* Set the loaded state of the property given it's name.
*
+15
View File
@@ -945,6 +945,21 @@ public final class Ebean {
return serverMgr.getDefaultServer().createCsvReader(beanType);
}
/**
* Create a named query.
* <p>
* For RawSql the named query is expected to be in ebean.xml.
* </p>
*
* @param beanType The type of entity bean
* @param namedQuery The name of the query
* @param <T> The type of entity bean
* @return The query
*/
public static <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
return serverMgr.getDefaultServer().createNamedQuery(beanType, namedQuery);
}
/**
* Create a query for a type of entity bean.
* <p>
@@ -199,6 +199,19 @@ public interface EbeanServer {
*/
<T> UpdateQuery<T> update(Class<T> beanType);
/**
* Create a named query.
* <p>
* For RawSql the named query is expected to be in ebean.xml.
* </p>
*
* @param beanType The type of entity bean
* @param namedQuery The name of the query
* @param <T> The type of entity bean
* @return The query
*/
<T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery);
/**
* Create a query for an entity bean and synonym for {@link #find(Class)}.
*
+2 -2
View File
@@ -606,9 +606,9 @@ public final class RawSql implements Serializable {
}
}
private static String derivePropertyName(String dbAlias, String dbColumn) {
protected static String derivePropertyName(String dbAlias, String dbColumn) {
if (dbAlias != null) {
return dbAlias;
return CamelCaseHelper.toCamelFromUnderscore(dbAlias);
}
int dotPos = dbColumn.indexOf('.');
if (dotPos > -1) {
@@ -31,6 +31,11 @@ public interface BeanCollection<E> extends Serializable {
ALL
}
/**
* Set the disableLazyLoad state.
*/
void setDisableLazyLoad(boolean disableLazyLoad);
/**
* Load bean from another collection.
*/
@@ -20,6 +20,8 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
protected boolean readOnly;
protected boolean disableLazyLoad;
/**
* The EbeanServer this is associated with. (used for lazy fetch).
*/
@@ -84,6 +86,11 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
this.filterMany = filterMany;
}
@Override
public void setDisableLazyLoad(boolean disableLazyLoad) {
this.disableLazyLoad = disableLazyLoad;
}
protected void lazyLoadCollection(boolean onlyIds) {
if (loader == null) {
loader = (BeanCollectionLoader) Ebean.getServer(ebeanServerName);
@@ -102,7 +102,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
private void initClear() {
synchronized (this) {
if (list == null) {
if (modifyListening) {
if (!disableLazyLoad && modifyListening) {
lazyLoadCollection(true);
} else {
list = new ArrayList<E>();
@@ -114,7 +114,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
private void init() {
synchronized (this) {
if (list == null) {
lazyLoadCollection(false);
if (disableLazyLoad) {
list = new ArrayList<E>();
} else {
lazyLoadCollection(false);
}
}
}
}
@@ -198,9 +198,11 @@ public class ServerConfig {
private PersistBatch persistBatch = PersistBatch.NONE;
/**
* Use for per request batch mode.
* Use for cascade persist JDBC batch mode. INHERIT means use the platform default
* which is ALL except for SQL Server where it is NONE (as getGeneratedKeys isn't
* supported on SQL Server with JDBC batch).
*/
private PersistBatch persistBatchOnCascade = PersistBatch.NONE;
private PersistBatch persistBatchOnCascade = PersistBatch.INHERIT;
private int persistBatchSize = 20;
@@ -318,7 +320,7 @@ public class ServerConfig {
/**
* Setting to indicate if UUID should be stored as binary(16) or varchar(40) or native DB type (for H2 and Postgres).
*/
private DbUuid dbUuid = DbUuid.AUTO;
private DbUuid dbUuid = DbUuid.AUTO_VARCHAR;
private List<IdGenerator> idGenerators = new ArrayList<IdGenerator>();
@@ -2530,13 +2532,14 @@ public class ServerConfig {
/**
* Return the PersistBatch mode to use for 'batchOnCascade' taking into account if the database
* platform supports getGeneratedKeys in batch mode.
* <p>
* Used to effectively turn off batchOnCascade for SQL Server - still allows explicit batch mode.
* </p>
*/
public PersistBatch appliedPersistBatchOnCascade() {
return databasePlatform.isDisallowBatchOnCascade() ? PersistBatch.NONE : persistBatchOnCascade;
if (persistBatchOnCascade == PersistBatch.INHERIT) {
// use the platform default (ALL except SQL Server which has NONE)
return databasePlatform.getPersistBatchOnCascade();
}
return persistBatchOnCascade;
}
/**
@@ -2612,18 +2615,45 @@ public class ServerConfig {
public enum DbUuid {
/**
* Store using native UUID in H2 and Postgres.
* Store using native UUID in H2 and Postgres and otherwise fallback to VARCHAR(40).
*/
AUTO,
AUTO_VARCHAR(true, false),
/**
* Store using DB VARCHAR.
* Store using native UUID in H2 and Postgres and otherwise fallback to BINARY(16).
*/
VARCHAR,
AUTO_BINARY(true, true),
/**
* Store using DB BINARY.
* Store using DB VARCHAR(40).
*/
BINARY
VARCHAR(false, false),
/**
* Store using DB BINARY(16).
*/
BINARY(false, true);
boolean nativeType;
boolean binary;
DbUuid(boolean nativeType, boolean binary) {
this.nativeType = nativeType;
this.binary = binary;
}
/**
* Return true if native UUID type is preferred.
*/
public boolean useNativeType() {
return nativeType;
}
/**
* Return true if BINARY(16) storage is preferred over VARCHAR(40).
*/
public boolean useBinary() {
return binary;
}
}
}
@@ -2,6 +2,7 @@ package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Query;
import com.avaje.ebean.config.PersistBatch;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
@@ -155,11 +156,9 @@ public class DatabasePlatform {
protected boolean forwardOnlyHintOnFindIterate;
/**
* Flag set for SQL Server due to lack of support of getGeneratedKeys in
* batch mode (meaning for batch inserts you should explicitly turn off
* getGeneratedKeys - joy).
* By default we use JDBC batch when cascading (except for SQL Server).
*/
protected boolean disallowBatchOnCascade;
protected PersistBatch persistBatchOnCascade = PersistBatch.ALL;
protected PlatformDdl platformDdl;
@@ -183,8 +182,11 @@ public class DatabasePlatform {
public DatabasePlatform() {
}
public void configure(Properties properties) {
// by default do nothing
/**
* Configure UUID Storage etc based on ServerConfig settings.
*/
public void configure(ServerConfig serverConfig) {
dbTypeMap.config(nativeUuidType, serverConfig.getDbUuid());
}
/**
@@ -542,14 +544,10 @@ public class DatabasePlatform {
}
/**
* Return true if the persistBatchOnCascade setting should be ignored.
* <p>
* This is primarily for SQL Server which does not support getGeneratedKeys with jdbc batch mode
* so can't really be transparently used.
* </p>
* Return the platform default JDBC batch mode for persist cascade.
*/
public boolean isDisallowBatchOnCascade() {
return disallowBatchOnCascade;
public PersistBatch getPersistBatchOnCascade() {
return persistBatchOnCascade;
}
/**
@@ -152,4 +152,10 @@ public class DbType {
return sb.toString();
}
/**
* Create a copy of the type with a new default length.
*/
public DbType withLength(int defaultLength) {
return new DbType(name, defaultLength);
}
}
@@ -1,5 +1,7 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.ServerConfig;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
@@ -9,6 +11,8 @@ import java.util.Map;
*/
public class DbTypeMap {
private static final DbType UUID_NATIVE = new DbType("uuid", false);
private static final DbType UUID_PLACEHOLDER = new DbType("uuidPlaceholder");
private static final DbType JSON_CLOB_PLACEHOLDER = new DbType("jsonClobPlaceholder");
private static final DbType JSON_BLOB_PLACEHOLDER = new DbType("jsonBlobPlaceholder");
private static final DbType JSON_VARCHAR_PLACEHOLDER = new DbType("jsonVarcharPlaceholder");
@@ -66,7 +70,7 @@ public class DbTypeMap {
/**
* Return the DbTypeMap with standard (not platform specific) types.
*
* <p>
* This has some extended JSON types (JSON, JSONB, JSONVarchar, JSONClob, JSONBlob).
* These types get translated to specific database platform types during DDL generation.
*/
@@ -104,8 +108,6 @@ public class DbTypeMap {
put(Types.BLOB, new DbType("blob"));
put(Types.CLOB, new DbType("clob"));
// DB native UUID support (H2 and Postgres)
put(DbType.UUID, new DbType("uuid"));
put(Types.ARRAY, new DbType("array"));
if (logicalTypes) {
@@ -116,6 +118,7 @@ public class DbTypeMap {
put(DbType.JSONClob, new DbType("jsonclob"));
put(DbType.JSONBlob, new DbType("jsonblob"));
put(DbType.JSONVarchar, new DbType("jsonvarchar", 1000));
put(DbType.UUID, UUID_NATIVE);
} else {
put(DbType.JSON, JSON_CLOB_PLACEHOLDER); // Postgres maps this to JSON
@@ -123,6 +126,7 @@ public class DbTypeMap {
put(DbType.JSONClob, JSON_CLOB_PLACEHOLDER);
put(DbType.JSONBlob, JSON_BLOB_PLACEHOLDER);
put(DbType.JSONVarchar, JSON_VARCHAR_PLACEHOLDER);
put(DbType.UUID, UUID_PLACEHOLDER);
}
put(Types.LONGVARBINARY, new DbType("longvarbinary"));
@@ -133,7 +137,6 @@ public class DbTypeMap {
put(Types.DATE, new DbType("date"));
put(Types.TIME, new DbType("time"));
put(Types.TIMESTAMP, new DbType("timestamp"));
}
/**
@@ -191,4 +194,17 @@ public class DbTypeMap {
public DbType get(int jdbcType) {
return typeMap.get(jdbcType);
}
/**
* Map the UUID appropriately based on native DB support and ServerConfig.DbUuid.
*/
public void config(boolean nativeUuidType, ServerConfig.DbUuid dbUuid) {
if (nativeUuidType && dbUuid.useNativeType()) {
put(DbType.UUID, UUID_NATIVE);
} else if (dbUuid.useBinary()) {
put(DbType.UUID, get(Types.BINARY).withLength(16));
} else {
put(DbType.UUID, get(Types.VARCHAR).withLength(40));
}
}
}
@@ -9,9 +9,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* H2 database trigger used to populate history tables to support the @History feature.
@@ -54,7 +52,6 @@ public class H2HistoryTrigger implements Trigger {
insertSql.append("insert into ").append(tableName).append(HISTORY_SUFFIX).append(" (");
int count = 0;
List<String> columns = new ArrayList<String>();
while (rs.next()) {
if (++count > 1) {
insertSql.append(",");
@@ -66,7 +63,6 @@ public class H2HistoryTrigger implements Trigger {
this.effectEndPosition = count - 1;
}
insertSql.append(columnName);
columns.add(columnName);
}
insertSql.append(") values (");
for (int i = 0; i < count; i++) {
@@ -1,9 +1,11 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl;
import javax.sql.DataSource;
import java.util.Properties;
/**
* H2 specific platform.
@@ -19,9 +21,7 @@ public class H2Platform extends DatabasePlatform {
this.nativeUuidType = true;
this.dbDefaultValue.setNow("now()");
// only support getGeneratedKeys with non-batch JDBC
// so generally use SEQUENCE instead of IDENTITY for H2
this.dbIdentity.setIdType(IdType.SEQUENCE);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
@@ -34,6 +34,18 @@ public class H2Platform extends DatabasePlatform {
// so no changes to dbTypeMap required
}
@Override
public void configure(ServerConfig serverConfig) {
super.configure(serverConfig);
Properties properties = serverConfig.getProperties();
if (properties != null) {
String idType = properties.getProperty("ebean.h2.idtype");
if (idType != null) {
this.dbIdentity.setIdType(IdType.valueOf(idType));
}
}
}
/**
* Return a H2 specific sequence IdGenerator that supports batch fetching
* sequence values.
@@ -1,5 +1,7 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.PersistBatch;
import java.sql.Types;
/**
@@ -18,6 +20,7 @@ public class MsSqlServer2000Platform extends DatabasePlatform {
public MsSqlServer2000Platform() {
super();
this.name = "mssqlserver2000";
this.persistBatchOnCascade = PersistBatch.NONE;
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setSelectLastInsertedIdTemplate("select @@IDENTITY as X");
@@ -1,5 +1,6 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.PersistBatch;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.MsSqlServerDdl;
import java.sql.Types;
@@ -21,7 +22,7 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
this.name = "mssqlserver2005";
// effectively disable persistBatchOnCascade mode for SQL Server
// due to lack of support for getGeneratedKeys in batch mode
this.disallowBatchOnCascade = true;
this.persistBatchOnCascade = PersistBatch.NONE;
this.idInExpandedForm = true;
this.selectCountWithAlias = true;
this.sqlLimiter = new MsSqlServer2005SqlLimiter();
@@ -64,12 +64,15 @@ public class PostgresPlatform extends DatabasePlatform {
}
@Override
public void configure(Properties properties) {
super.configure(properties);
String tsType = properties.getProperty("ebean.postgres.timestamp");
if (tsType != null) {
// set timestamp type to "timestamp" without time zone
dbTypeMap.put(Types.TIMESTAMP, new DbType(tsType));
public void configure(ServerConfig serverConfig) {
super.configure(serverConfig);
Properties properties = serverConfig.getProperties();
if (properties != null) {
String tsType = properties.getProperty("ebean.postgres.timestamp");
if (tsType != null) {
// set timestamp type to "timestamp" without time zone
dbTypeMap.put(Types.TIMESTAMP, new DbType(tsType));
}
}
}
@@ -164,9 +164,6 @@ public class DbMigration {
* </p>
*/
public void addPlatform(DbPlatformName platform, String prefix) {
if (!prefix.endsWith("-")) {
prefix += "-";
}
platforms.add(new Pair(getPlatform(platform), prefix));
}
@@ -204,13 +201,20 @@ public class DbMigration {
// use this flag to stop other plugins like full DDL generation
if (!online) {
DbOffline.setRunningMigration();
DbOffline.setGenerateMigration();
if (databasePlatform == null || !platforms.isEmpty()) {
// for multiple platform generation set the general platform
// to H2 so that it runs offline without DB connection
setPlatform(DbPlatformName.H2);
}
}
setDefaults();
try {
Request request = createRequest();
generateExtraDdl(request);
if (platforms.isEmpty()) {
generateExtraDdl(request.migrationDir, databasePlatform);
}
String pendingVersion = generatePendingDrop();
if (pendingVersion != null) {
@@ -234,15 +238,15 @@ public class DbMigration {
* migration runner.
* </p>
*/
private void generateExtraDdl(Request request) throws IOException {
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform) throws IOException {
if (databasePlatform != null) {
if (dbPlatform != null) {
ExtraDdl extraDdl = ExtraDdlXmlReader.read("/extra-ddl.xml");
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (ExtraDdlXmlReader.matchPlatform(databasePlatform.getName(), script.getPlatforms())) {
writeExtraDdl(request, script);
if (ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) {
writeExtraDdl(migrationDir, script);
}
}
}
@@ -252,13 +256,13 @@ public class DbMigration {
/**
* Write (or override) the "repeatable" migration script.
*/
private void writeExtraDdl(Request request, DdlScript script) throws IOException {
private void writeExtraDdl(File migrationDir, DdlScript script) throws IOException {
String fullName = repeatableMigrationName(script.getName());
logger.info("writing repeatable script {}", fullName);
File file = new File(request.migrationDir, fullName);
File file = new File(migrationDir, fullName);
FileWriter writer = new FileWriter(file);
writer.write(script.getValue());
writer.flush();
@@ -363,14 +367,16 @@ public class DbMigration {
logger.warn("migration already exists, not generating DDL");
} else {
if (databasePlatform != null) {
if (!platforms.isEmpty()) {
writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
} else if (databasePlatform != null) {
// writer needs the current model to provide table/column details for
// history ddl generation (triggers, history tables etc)
DdlWrite write = new DdlWrite(new MConfiguration(), request.current);
PlatformDdlWriter writer = createDdlWriter(databasePlatform, "");
writer.processMigration(dbMigration, write, request.migrationDir, fullVersion);
}
writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
}
}
@@ -426,7 +432,10 @@ public class DbMigration {
for (Pair pair : platforms) {
DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read());
PlatformDdlWriter platformWriter = createDdlWriter(pair);
platformWriter.processMigration(dbMigration, platformBuffer, writePath, fullVersion);
File subPath = platformWriter.subPath(writePath, pair.prefix);
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
generateExtraDdl(subPath, pair.platform);
}
}
@@ -14,7 +14,7 @@ public class DbOffline {
private static final String KEY = "ebean.dboffline";
private static boolean runningMigration;
private static boolean generateMigration;
/**
* Set the platform to use when creating the next EbeanServer instance.
@@ -55,23 +55,23 @@ public class DbOffline {
* Return true if the migration is running. This typically means don't run the
* plugins like full DDL generation.
*/
public static boolean isRunningMigration() {
return runningMigration;
public static boolean isGenerateMigration() {
return generateMigration;
}
/**
* Called when the migration is running is order to stop other plugins
* like the full DDL generation from executing.
*/
public static void setRunningMigration() {
runningMigration = true;
public static void setGenerateMigration() {
generateMigration = true;
}
/**
* Reset the offline platform and runningMigration flag.
*/
public static void reset() {
runningMigration = false;
generateMigration = false;
System.clearProperty(KEY);
logger.debug("reset");
}
@@ -831,9 +831,10 @@ public class BaseTableDdl implements TableDdl {
protected void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable) throws IOException {
String convertedType = platformDdl.convert(column.getType(), false);
buffer.append("alter table ").append(tableName)
.append(" add column ").append(column.getName())
.append(" ").append(column.getType());
.append(" ").append(convertedType);
if (!onHistoryTable) {
if (isTrue(column.isNotnull())) {
@@ -26,6 +26,8 @@ import java.util.List;
*/
public class PlatformDdl {
protected final DatabasePlatform platform;
protected PlatformHistoryDdl historyDdl = new NoHistorySupportDdl();
/**
@@ -98,6 +100,7 @@ public class PlatformDdl {
protected final DbDefaultValue dbDefaultValue;
public PlatformDdl(DatabasePlatform platform) {
this.platform = platform;
this.dbIdentity = platform.getDbIdentity();
this.dbDefaultValue = platform.getDbDefaultValue();
this.typeConverter = new PlatformTypeConverter(platform.getDbTypeMap());
@@ -107,6 +110,7 @@ public class PlatformDdl {
* Set configuration options.
*/
public void configure(ServerConfig serverConfig) {
platform.configure(serverConfig);
historyDdl.configure(serverConfig, this);
naming = serverConfig.getConstraintNaming();
}
@@ -11,7 +11,6 @@ import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import java.io.IOException;
import java.util.List;
/**
* Reads EbeanServer bean descriptors to build the current model.
@@ -24,6 +23,8 @@ public class CurrentModel {
private final DbConstraintNaming.MaxLength maxLength;
private final boolean platformTypes;
private ModelContainer model;
private ChangeSet changeSet;
@@ -31,32 +32,31 @@ public class CurrentModel {
private DdlWrite write;
/**
* Construct with a given EbeanServer instance.
* Construct with a given EbeanServer instance for DDL create all generation, not migration.
*/
public CurrentModel(SpiEbeanServer server) {
this(server, server.getServerConfig().getConstraintNaming());
this(server, server.getServerConfig().getConstraintNaming(), true);
}
/**
* Construct with a given EbeanServer, platformDdl and constraintNaming convention.
* <p>
* Note the EbeanServer is just used to read the BeanDescriptors and platformDdl supplies
* the platform specific handling on
* Note the EbeanServer is just used to read the BeanDescriptors and platformDdl supplies
* the platform specific handling on
* </p>
*/
public CurrentModel(SpiEbeanServer server, DbConstraintNaming constraintNaming) {
this(server, constraintNaming, false);
}
private CurrentModel(SpiEbeanServer server, DbConstraintNaming constraintNaming, boolean platformTypes) {
this.server = server;
this.constraintNaming = constraintNaming;
this.maxLength = maxLength(server, constraintNaming);
this.platformTypes = platformTypes;
}
public CurrentModel(SpiEbeanServer server, DbConstraintNaming constraintNaming, int maxConstraintLength) {
this.server = server;
this.constraintNaming = constraintNaming;
this.maxLength = new DefaultConstraintMaxLength(maxConstraintLength);
}
private DbConstraintNaming.MaxLength maxLength(SpiEbeanServer server, DbConstraintNaming naming) {
private static DbConstraintNaming.MaxLength maxLength(SpiEbeanServer server, DbConstraintNaming naming) {
if (naming.getMaxLength() != null) {
return naming.getMaxLength();
@@ -73,7 +73,7 @@ public class CurrentModel {
if (model == null) {
model = new ModelContainer();
ModelBuildContext context = new ModelBuildContext(model, constraintNaming, maxLength);
ModelBuildContext context = new ModelBuildContext(model, constraintNaming, maxLength, platformTypes);
ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context);
VisitAllUsing visit = new VisitAllUsing(visitor, server);
visit.visitAllBeans();
@@ -9,6 +9,8 @@ import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
import com.avaje.ebean.dbmigration.migration.ChangeSet;
import com.avaje.ebean.dbmigration.migration.ChangeSetType;
import com.avaje.ebean.dbmigration.migration.Migration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileWriter;
@@ -21,6 +23,8 @@ import java.util.List;
*/
public class PlatformDdlWriter {
private static final Logger logger = LoggerFactory.getLogger(PlatformDdlWriter.class);
private final ServerConfig serverConfig;
private final DatabasePlatform platform;
@@ -67,7 +71,7 @@ public class PlatformDdlWriter {
protected void writePlatformDdl(DdlWrite write, File resourcePath, String fullVersion) throws IOException {
if (!write.isApplyEmpty()) {
FileWriter applyWriter = createWriter(resourcePath, fullVersion, "", config.getApplySuffix());
FileWriter applyWriter = createWriter(resourcePath, fullVersion, config.getApplySuffix());
try {
writeApplyDdl(applyWriter, write);
applyWriter.flush();
@@ -77,28 +81,12 @@ public class PlatformDdlWriter {
}
}
protected FileWriter createWriter(File path, String fullVersion, String subPath, String suffix) throws IOException {
protected FileWriter createWriter(File path, String fullVersion, String suffix) throws IOException {
String fileName = fullVersion;
if (!platformPrefix.isEmpty()) {
fileName += "-"+platformPrefix;
}
if (subPath != null && !subPath.isEmpty()) {
path = subPath(path, subPath);
}
fileName += suffix;
File applyFile = new File(path, fileName);
File applyFile = new File(path, fullVersion + suffix);
return new FileWriter(applyFile);
}
protected File subPath(File path, String suffix) {
File subPath = new File(path, suffix);
if (!subPath.exists()) {
subPath.mkdirs();
}
return subPath;
}
/**
* Write the 'Apply' DDL buffers to the writer.
*/
@@ -127,4 +115,17 @@ public class PlatformDdlWriter {
return platform.createDdlHandler(serverConfig);
}
/**
* Return a sub directory (for multi-platform ddl generation).
*/
public File subPath(File path, String suffix) {
File subPath = new File(path, suffix);
if (!subPath.exists()) {
if (!subPath.mkdirs()) {
logger.error("failed to create directories for " + subPath.getAbsolutePath());
}
}
return subPath;
}
}
@@ -29,10 +29,13 @@ public class ModelBuildContext {
private final DbConstraintNaming.MaxLength maxLength;
public ModelBuildContext(ModelContainer model, DbConstraintNaming naming, DbConstraintNaming.MaxLength maxLength) {
private final boolean platformTypes;
public ModelBuildContext(ModelContainer model, DbConstraintNaming naming, DbConstraintNaming.MaxLength maxLength, boolean platformTypes) {
this.model = model;
this.constraintNaming = naming;
this.maxLength = maxLength;
this.platformTypes = platformTypes;
}
/**
@@ -132,7 +135,7 @@ public class ModelBuildContext {
}
// can be the logical JSON types (JSON, JSONB, JSONClob, JSONBlob, JSONVarchar)
int dbType = p.getDbType();
int dbType = p.getDbType(platformTypes);
if (dbType == 0) {
throw new RuntimeException("No scalarType defined for " + p.getFullBeanName());
}
@@ -12,9 +12,12 @@ public class CamelCaseHelper {
*/
public static String toCamelFromUnderscore(String underscore) {
StringBuilder result = new StringBuilder();
String[] vals = underscore.split("_");
if (vals.length == 1) {
return underscore;
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < vals.length; i++) {
String lower = vals[i].toLowerCase();
if (i > 0) {
@@ -398,7 +398,7 @@ public class StringHelper {
int additionalSize, int startPos, int endPos) {
if (source == null) {
return source;
return null;
}
char match0 = match.charAt(0);
@@ -564,6 +564,11 @@ public interface SpiQuery<T> extends Query<T> {
*/
boolean tuneFetchProperties(OrmQueryDetail detail);
/**
* If this is a RawSql based entity set the default RawSql if not set.
*/
void setDefaultRawSqlIfRequired();
/**
* Set to true if this query has been tuned by autoTune.
*/
@@ -45,9 +45,8 @@ public class ProfileOriginNodeUsage {
if (path != null) {
ElPropertyValue elGetValue = rootDesc.getElGetValue(path);
if (elGetValue == null) {
desc = null;
logger.warn("AutoTune: Can't find join for path[" + path + "] for " + rootDesc.getName());
return;
} else {
BeanProperty beanProperty = elGetValue.getBeanProperty();
if (beanProperty instanceof BeanPropertyAssoc<?>) {
@@ -14,14 +14,14 @@ import java.util.Set;
*/
public class DefaultBeanState implements BeanState {
private final EntityBean entityBean;
private final EntityBeanIntercept intercept;
public DefaultBeanState(EntityBean entityBean){
this.entityBean = entityBean;
this.intercept = entityBean._ebean_getIntercept();
}
private final EntityBean entityBean;
private final EntityBeanIntercept intercept;
public DefaultBeanState(EntityBean entityBean) {
this.entityBean = entityBean;
this.intercept = entityBean._ebean_getIntercept();
}
public void setPropertyLoaded(String propertyName, boolean loaded) {
intercept.setPropertyLoaded(propertyName, loaded);
@@ -31,52 +31,57 @@ public class DefaultBeanState implements BeanState {
return intercept.isReference();
}
public boolean isNew() {
return intercept.isNew();
}
public boolean isNewOrDirty() {
return intercept.isNewOrDirty();
}
public boolean isDirty() {
return intercept.isDirty();
}
public Set<String> getLoadedProps() {
return intercept.getLoadedPropertyNames();
}
public Set<String> getChangedProps() {
return intercept.getDirtyPropertyNames();
public boolean isNew() {
return intercept.isNew();
}
public Map<String,ValuePair> getDirtyValues() {
public boolean isNewOrDirty() {
return intercept.isNewOrDirty();
}
public boolean isDirty() {
return intercept.isDirty();
}
public Set<String> getLoadedProps() {
return intercept.getLoadedPropertyNames();
}
public Set<String> getChangedProps() {
return intercept.getDirtyPropertyNames();
}
public Map<String, ValuePair> getDirtyValues() {
return intercept.getDirtyValues();
}
public boolean isReadOnly() {
return intercept.isReadOnly();
}
public void setReadOnly(boolean readOnly){
intercept.setReadOnly(readOnly);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
entityBean.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
entityBean.removePropertyChangeListener(listener);
}
public void setLoaded() {
intercept.setLoaded();
}
public boolean isReadOnly() {
return intercept.isReadOnly();
}
public void setReadOnly(boolean readOnly) {
intercept.setReadOnly(readOnly);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
entityBean.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
entityBean.removePropertyChangeListener(listener);
}
public void setLoaded() {
intercept.setLoaded();
}
@Override
public void setDisableLazyLoad(boolean disableLazyLoading) {
intercept.setDisableLazyLoad(disableLazyLoading);
}
@Override
public boolean isDisableLazyLoad() {
return intercept.isDisableLazyLoad();
}
}
@@ -122,22 +122,20 @@ public class DefaultContainer implements SpiContainer {
// generate and run DDL if required
// if there are any other tasks requiring action in their plugins, do them as well
if (!DbOffline.isRunningMigration()) {
if (!DbOffline.isGenerateMigration()) {
server.executePlugins(online);
}
// initialise prior to registering with clusterManager
server.initialise();
if (online) {
if (clusterManager.isClustering()) {
// register the server once it has been created
clusterManager.registerServer(server);
// initialise prior to registering with clusterManager
server.initialise();
if (online) {
if (clusterManager.isClustering()) {
// register the server once it has been created
clusterManager.registerServer(server);
}
}
// start any services after registering with clusterManager
server.start();
}
// start any services after registering with clusterManager
server.start();
DbOffline.reset();
return server;
}
@@ -244,7 +242,7 @@ public class DefaultContainer implements SpiContainer {
if (dbPlatform == null) {
DatabasePlatformFactory factory = new DatabasePlatformFactory();
DatabasePlatform db = factory.create(config);
db.configure(config.getProperties());
db.configure(config);
config.setDatabasePlatform(db);
logger.info("DatabasePlatform name:" + config.getName() + " platform:" + db.getName());
}
@@ -557,7 +557,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
InheritInfo inheritInfo = desc.getInheritInfo();
if (inheritInfo == null) {
return (T)desc.contextRef(pc, null, id);
return (T)desc.contextRef(pc, null, false, id);
}
BeanProperty idProp = desc.getIdProperty();
@@ -887,6 +887,21 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return createQuery(beanType);
}
@Override
public <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
BeanDescriptor<T> desc = getBeanDescriptor(beanType);
if (desc == null) {
throw new PersistenceException(beanType.getName() + " is NOT an Entity Bean registered with this server?");
}
RawSql rawSql = desc.getNamedRawSql(namedQuery);
if (rawSql != null) {
DefaultOrmQuery<T> query = createQuery(beanType);
query.setRawSql(rawSql);
return query;
}
throw new PersistenceException("No named query called " + namedQuery + " for bean:" + beanType.getName());
}
public <T> DefaultOrmQuery<T> createQuery(Class<T> beanType) {
BeanDescriptor<T> desc = getBeanDescriptor(beanType);
if (desc == null) {
@@ -945,6 +960,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private <T> SpiOrmQueryRequest<T> createQueryRequest(SpiQuery<T> query, Transaction t) {
query.setDefaultRawSqlIfRequired();
if (query.isAutoTunable() && !autoTuneService.tuneQuery(query)) {
// use deployment FetchType.LAZY/EAGER annotations
// to define the 'default' select clause
@@ -548,7 +548,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Create and return a new reference bean matching this beans Id value.
*/
public T createReference() {
return beanDescriptor.createReference(Boolean.FALSE, getBeanId(), null);
return beanDescriptor.createReference(Boolean.FALSE, false, getBeanId(), null);
}
/**
@@ -46,9 +46,11 @@ abstract class AssocOneHelp {
return existing;
}
Object ref = target.contextRef(pc, ctx.isReadOnly(), id);
EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept();
ctx.register(property.name, ebi);
boolean disableLazyLoading = ctx.isDisableLazyLoading();
Object ref = target.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
if (!disableLazyLoading) {
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
}
return ref;
}
@@ -53,9 +53,11 @@ class AssocOneHelpRefInherit extends AssocOneHelp {
}
// for inheritance hierarchy create the correct type for this row...
Object ref = desc.contextRef(pc, ctx.isReadOnly(), id);
EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept();
ctx.register(property.name, ebi);
boolean disableLazyLoading = ctx.isDisableLazyLoading();
Object ref = desc.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
if (disableLazyLoading) {
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
}
return ref;
}
@@ -109,6 +109,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
private final ConcurrentHashMap<String, ElComparator<T>> comparatorCache = new ConcurrentHashMap<String, ElComparator<T>>();
private final Map<String, RawSql> namedRawSql;
public void merge(EntityBean bean, EntityBean existing) {
EntityBeanIntercept fromEbi = bean._ebean_getIntercept();
@@ -412,6 +414,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
this.rootBeanType = PersistenceContextUtil.root(beanType);
this.prototypeEntityBean = createPrototypeEntityBean(beanType);
this.namedRawSql = deploy.getNamedRawSql();
this.inheritInfo = deploy.getInheritInfo();
this.beanFinder = deploy.getBeanFinder();
@@ -443,7 +446,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween();
this.dependentTables = deploy.getDependentTables();
this.dbComment = deploy.getDbComment();
this.autoTunable = EntityType.ORM.equals(entityType) && (beanFinder == null);
this.autoTunable = EntityType.ORM == entityType && (beanFinder == null);
// helper object used to derive lists of properties
DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy);
@@ -985,6 +988,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return this;
}
/**
* Return the named RawSql query.
*/
public RawSql getNamedRawSql(String named) {
return namedRawSql.get(named);
}
/**
* Return the type of DocStoreMode that should occur for this type of persist request
* given the transactions requested mode.
@@ -1559,9 +1569,9 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* Create a reference bean based on the id.
*/
@SuppressWarnings("unchecked")
public T createReference(Boolean readOnly, Object id, PersistenceContext pc) {
public T createReference(Boolean readOnly, boolean disableLazyLoad, Object id, PersistenceContext pc) {
if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) {
if (cacheSharableBeans && !disableLazyLoad && !Boolean.FALSE.equals(readOnly)) {
CachedBeanData d = cacheHelp.beanCacheGetData(id);
if (d != null) {
Object shareableBean = d.getSharableBean();
@@ -1578,7 +1588,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
id = convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
ebi.setBeanLoader(ebeanServer);
if (disableLazyLoad) {
ebi.setDisableLazyLoad(true);
} else {
ebi.setBeanLoader(ebeanServer);
}
ebi.setReference(idPropertyIndex);
if (Boolean.TRUE == readOnly) {
ebi.setReadOnly(true);
@@ -1750,8 +1764,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Create a reference bean and put it in the persistence context (and return it).
*/
public Object contextRef(PersistenceContext pc, Boolean readOnly, Object id) {
return createReference(readOnly, id, pc);
public Object contextRef(PersistenceContext pc, Boolean readOnly, boolean disableLazyLoad, Object id) {
return createReference(readOnly, disableLazyLoad, id, pc);
}
/**
@@ -2169,7 +2183,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* Return true if this is an embedded bean.
*/
public boolean isEmbedded() {
return EntityType.EMBEDDED.equals(entityType);
return EntityType.EMBEDDED == entityType;
}
/**
@@ -2295,15 +2309,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
/**
* Returns true if this bean is based on a table (or possibly view) and
* returns false if this bean is based on a raw sql select statement.
* <p>
* When false querying this bean is based on a supplied sql select statement
* placed in the orm xml file (as opposed to Ebean generated sql).
* </p>
* Returns true if this bean is based on RawSql.
*/
public boolean isSqlSelectBased() {
return EntityType.SQL.equals(entityType);
public boolean isRawSqlBased() {
return EntityType.SQL == entityType;
}
/**
@@ -266,7 +266,7 @@ final class BeanDescriptorCacheHelp<T> {
bc.checkEmptyLazyLoad();
for (int i = 0; i < idList.size(); i++) {
Object id = idList.get(i);
Object refBean = targetDescriptor.createReference(readOnly, id, persistenceContext);
Object refBean = targetDescriptor.createReference(readOnly, false, id, persistenceContext);
many.add(bc, (EntityBean) refBean);
}
return true;
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Model;
import com.avaje.ebean.RawSqlBuilder;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.cache.ServerCacheManager;
@@ -22,10 +23,10 @@ import com.avaje.ebean.plugin.BeanType;
import com.avaje.ebeaninternal.api.ConcurrencyMode;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.core.InternalConfiguration;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.IdBinderEmbedded;
@@ -47,6 +48,12 @@ import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader;
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo;
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfoFactory;
import com.avaje.ebeaninternal.server.properties.EnhanceBeanPropertyInfoFactory;
import com.avaje.ebeaninternal.xmlmapping.XmlMappingReader;
import com.avaje.ebeaninternal.xmlmapping.model.XmAliasMapping;
import com.avaje.ebeaninternal.xmlmapping.model.XmColumnMapping;
import com.avaje.ebeaninternal.xmlmapping.model.XmEbean;
import com.avaje.ebeaninternal.xmlmapping.model.XmEntity;
import com.avaje.ebeaninternal.xmlmapping.model.XmRawSql;
import com.avaje.ebeanservice.docstore.api.DocStoreBeanAdapter;
import com.avaje.ebeanservice.docstore.api.DocStoreFactory;
import org.slf4j.Logger;
@@ -56,12 +63,16 @@ import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -116,7 +127,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final DocStoreFactory docStoreFactory;
private int enhancedClassCount;
private int entityBeanCount;
private final boolean updateChangesOnly;
@@ -124,7 +135,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final String serverName;
private Map<Class<?>, DeployBeanInfo<?>> deplyInfoMap = new HashMap<Class<?>, DeployBeanInfo<?>>();
private Map<Class<?>, DeployBeanInfo<?>> deployInfoMap = new HashMap<Class<?>, DeployBeanInfo<?>>();
private final Map<Class<?>, BeanTable> beanTableMap = new HashMap<Class<?>, BeanTable>();
@@ -301,6 +312,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
try {
createListeners();
readEntityDeploymentInitial();
readXmlMapping();
readEmbeddedDeployment();
readEntityBeanTable();
readEntityDeploymentAssociations();
@@ -319,8 +331,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
logStatus();
deplyInfoMap.clear();
deplyInfoMap = null;
deployInfoMap.clear();
deployInfoMap = null;
return asOfTableMap;
@@ -330,6 +342,62 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
}
private void readXmlMapping() {
try {
ClassLoader classLoader = serverConfig.getClassLoadConfig().getClassLoader();
Enumeration<URL> resources = classLoader.getResources("ebean.xml");
List<XmEbean> mappings = new ArrayList<XmEbean>();
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
InputStream is = url.openStream();
mappings.add(XmlMappingReader.read(is));
is.close();
}
for (XmEbean mapping : mappings) {
List<XmEntity> entityDeploy = mapping.getEntity();
for (XmEntity deploy : entityDeploy) {
readEntityMapping(classLoader, deploy);
}
}
} catch (IOException e) {
throw new RuntimeException("Error reading ebean.xml", e);
}
}
private void readEntityMapping(ClassLoader classLoader, XmEntity entityDeploy) {
String entityClassName = entityDeploy.getClazz();
Class<?> entityClass;
try {
entityClass = Class.forName(entityClassName, false, classLoader);
} catch (Exception e) {
logger.error("Could not load entity bean class "+entityClassName+" for ebean.xml entry");
return;
}
DeployBeanInfo<?> info = deployInfoMap.get(entityClass);
if (info == null) {
logger.error("No entity bean for ebean.xml entry "+entityClassName);
} else {
for (XmRawSql sql : entityDeploy.getRawSql()) {
RawSqlBuilder builder = RawSqlBuilder.parse(sql.getQuery().getValue());
for (XmColumnMapping columnMapping : sql.getColumnMapping()) {
builder.columnMapping(columnMapping.getColumn(), columnMapping.getProperty());
}
for (XmAliasMapping aliasMapping : sql.getAliasMapping()) {
builder.tableAliasMapping(aliasMapping.getAlias(), aliasMapping.getProperty());
}
info.addRawSql(sql.getName(), builder.create());
}
}
}
/**
* Return the Encrypt key given the table and column name.
*/
@@ -381,7 +449,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (String depTable : viewInvalidation) {
List<BeanDescriptor<?>> list = tableToViewDescMap.get(depTable.toLowerCase());
if (list == null) {
if (list != null) {
for (int i = 0; i < list.size(); i++) {
list.get(i).queryCacheClear();
}
@@ -557,7 +625,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
private void logStatus() {
logger.info("Entities enhanced[" + enhancedClassCount + "]");
logger.debug("Entities[{}]", entityBeanCount);
}
private <T> BeanDescriptor<T> createEmbedded(Class<T> beanClass) {
@@ -569,7 +637,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* Return the bean deploy info for the given class.
*/
public <T> DeployBeanInfo<T> getDeploy(Class<T> cls) {
return (DeployBeanInfo<T>) deplyInfoMap.get(cls);
return (DeployBeanInfo<T>) deployInfoMap.get(cls);
}
private void registerBeanDescriptor(BeanDescriptor<?> desc) {
@@ -601,12 +669,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (Class<?> entityClass : bootupClasses.getEntities()) {
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
deplyInfoMap.put(entityClass, info);
deployInfoMap.put(entityClass, info);
}
for (Class<?> entityClass : bootupClasses.getEmbeddables()) {
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
readDeployAssociations(info);
deplyInfoMap.put(entityClass, info);
deployInfoMap.put(entityClass, info);
}
}
@@ -618,7 +686,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityBeanTable() {
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
BeanTable beanTable = createBeanTable(info);
beanTableMap.put(beanTable.getBeanType(), beanTable);
}
@@ -632,18 +700,18 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityDeploymentAssociations() {
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
readDeployAssociations(info);
}
}
private void readInheritedIdGenerators() {
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
DeployBeanDescriptor<?> descriptor = info.getDescriptor();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && !inheritInfo.isRoot()) {
DeployBeanInfo<?> rootBeanInfo = deplyInfoMap.get(inheritInfo.getRoot().getType());
DeployBeanInfo<?> rootBeanInfo = deployInfoMap.get(inheritInfo.getRoot().getType());
PlatformIdGenerator rootIdGen = rootBeanInfo.getDescriptor().getIdGenerator();
if (rootIdGen != null) {
descriptor.setIdGenerator(rootIdGen);
@@ -668,20 +736,20 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// We only perform 'circular' checks etc after we have
// all the DeployBeanDescriptors created and in the map.
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
checkMappedBy(info);
}
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
secondaryPropsJoins(info);
}
// Set inheritance info
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
setInheritanceInfo(info);
}
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
registerBeanDescriptor(new BeanDescriptor(this, info.getDescriptor()));
}
}
@@ -695,7 +763,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
if (!oneProp.isTransient()) {
DeployBeanInfo<?> assoc = deplyInfoMap.get(oneProp.getTargetType());
DeployBeanInfo<?> assoc = deployInfoMap.get(oneProp.getTargetType());
if (assoc != null) {
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
@@ -704,7 +772,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
if (!manyProp.isTransient()) {
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
DeployBeanInfo<?> assoc = deployInfoMap.get(manyProp.getTargetType());
if (assoc != null) {
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
@@ -763,7 +831,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private DeployBeanDescriptor<?> getTargetDescriptor(DeployBeanPropertyAssoc<?> prop) {
Class<?> targetType = prop.getTargetType();
DeployBeanInfo<?> info = deplyInfoMap.get(targetType);
DeployBeanInfo<?> info = deployInfoMap.get(targetType);
if (info == null) {
String msg = "Can not find descriptor [" + targetType + "] for " + prop.getFullBeanName();
throw new PersistenceException(msg);
@@ -1348,7 +1416,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
checkInheritedClasses(beanClass);
if (!beanClass.getName().startsWith("com.avaje.ebean.meta")) {
enhancedClassCount++;
entityBeanCount++;
}
}
@@ -27,6 +27,7 @@ import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.type.ScalarTypeBoolean;
import com.avaje.ebeaninternal.server.type.ScalarTypeEnum;
import com.avaje.ebeaninternal.server.type.ScalarTypeLogicalType;
import com.avaje.ebeaninternal.util.ValueUtil;
import com.avaje.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyMapping;
@@ -413,7 +414,7 @@ public class BeanProperty implements ElPropertyValue, Property {
this.generatedProperty = source.getGeneratedProperty();
this.getter = source.getter;
this.setter = source.setter;
this.dbType = source.getDbType();
this.dbType = source.getDbType(true);
this.scalarType = source.scalarType;
this.lob = isLobType(dbType);
this.propertyType = source.getPropertyType();
@@ -1087,9 +1088,14 @@ public class BeanProperty implements ElPropertyValue, Property {
/**
* Return the database jdbc data type this is mapped to.
*
* @param platformTypes Set as false when we want logical platform agnostic types.
*/
public int getDbType() {
return dbType;
public int getDbType(boolean platformTypes) {
if (platformTypes || !(scalarType instanceof ScalarTypeLogicalType)) {
return dbType;
}
return ((ScalarTypeLogicalType)scalarType).getLogicalType();
}
/**
@@ -322,7 +322,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
BeanProperty idProp = target.getIdProperty();
BeanProperty[] others = target.propertiesBaseScalar();
if (descriptor.isSqlSelectBased()) {
if (descriptor.isRawSqlBased()) {
String dbColumn = owner.getDbColumn();
return new ImportedIdSimple(owner, dbColumn, idProp, 0);
}
@@ -1028,7 +1028,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
if (isManyToMany()) {
if (liveBean == null) {
// add new relationship (Map not allowed here)
liveVal.addBean(targetDescriptor.createReference(Boolean.FALSE, id, null));
liveVal.addBean(targetDescriptor.createReference(Boolean.FALSE, false, id, null));
}
} else {
@@ -422,7 +422,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
// cacheData is the id value, maybe already in persistence context
Object assocBean = targetDescriptor.contextGet(context, cacheData);
if (assocBean == null) {
assocBean = targetDescriptor.createReference(Boolean.FALSE, cacheData, context);
assocBean = targetDescriptor.createReference(Boolean.FALSE, false, cacheData, context);
}
setValue(bean, assocBean);
}
@@ -84,4 +84,9 @@ public interface DbReadContext {
* Return true if the underlying query is a 'asDraft' query.
*/
boolean isDraftQuery();
/**
* Return true if this request disables lazy loading.
*/
boolean isDisableLazyLoading();
}
@@ -51,14 +51,14 @@ public abstract class DeployParser {
*/
public abstract Set<String> getIncludes();
public void setEncrypted(boolean encrytped) {
this.encrypted = encrytped;
}
public void setEncrypted(boolean encrypted) {
this.encrypted = encrypted;
}
public String parse(String source) {
if (source == null) {
return source;
return null;
}
pos = -1;
@@ -144,15 +144,13 @@ public class ImportedIdEmbedded implements ImportedId {
*/
public BeanProperty findMatchImport(String matchDbColumn) {
BeanProperty p = null;
for (int i = 0; i < imported.length; i++) {
p = imported[i].findMatchImport(matchDbColumn);
BeanProperty p = imported[i].findMatchImport(matchDbColumn);
if (p != null) {
return p;
}
}
return p;
return null;
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.meta;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.annotation.Cache;
import com.avaje.ebean.annotation.DocStore;
import com.avaje.ebean.annotation.DocStoreMode;
@@ -35,14 +36,18 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Describes Beans including their deployment information.
*/
public class DeployBeanDescriptor<T> {
private static final Map<String, RawSql> EMPTY_RAW_MAP = new HashMap<String, RawSql>();
private static class PropOrder implements Comparator<DeployBeanProperty> {
public int compare(DeployBeanProperty o1, DeployBeanProperty o2) {
@@ -66,6 +71,8 @@ public class DeployBeanDescriptor<T> {
*/
private LinkedHashMap<String, DeployBeanProperty> propMap = new LinkedHashMap<String, DeployBeanProperty>();
private Map<String, RawSql> namedRawSql;
private EntityType entityType;
private DeployBeanPropertyAssocOne<?> unidirectional;
@@ -1047,4 +1054,21 @@ public class DeployBeanDescriptor<T> {
if (docStorePersist != DocStoreMode.DEFAULT) return docStorePersist;
return serverConfig.getDocStoreConfig().getPersist();
}
/**
* Return the named RawSql queries.
*/
public Map<String, RawSql> getNamedRawSql() {
return (namedRawSql != null) ? namedRawSql : EMPTY_RAW_MAP;
}
/**
* Add a named RawSql from ebean.xml file.
*/
public void addRawSql(String name, RawSql rawSql) {
if (namedRawSql == null) {
namedRawSql = new HashMap<String, RawSql>();
}
namedRawSql.put(name, rawSql);
}
}
@@ -68,13 +68,11 @@ public class AnnotationClass extends AnnotationParser {
if (override != null) {
String propertyName = override.name();
Column column = override.column();
if (column != null) {
DeployBeanProperty beanProperty = descriptor.getBeanProperty(propertyName);
if (beanProperty == null) {
logger.error("AttributeOverride property [" + propertyName + "] not found on " + descriptor.getFullName());
} else {
readColumn(column, beanProperty);
}
DeployBeanProperty beanProperty = descriptor.getBeanProperty(propertyName);
if (beanProperty == null) {
logger.error("AttributeOverride property [" + propertyName + "] not found on " + descriptor.getFullName());
} else {
readColumn(column, beanProperty);
}
}
}
@@ -1,12 +1,13 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.util.HashMap;
import com.avaje.ebean.RawSql;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import java.util.HashMap;
/**
* Wraps information about a bean during deployment parsing.
*/
@@ -75,4 +76,10 @@ public class DeployBeanInfo<T> {
tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER);
}
/**
* Add named RawSql from ebean.xml.
*/
public void addRawSql(String name, RawSql rawSql) {
descriptor.addRawSql(name, rawSql);
}
}
@@ -105,7 +105,7 @@ public class BatchedPstmtHolder {
firstError = ex;
errorSql = bs.getSql();
} else {
logger.error(null, ex);
logger.error("Error executing batched PreparedStatement", ex);
}
isError = true;
@@ -113,8 +113,7 @@ public class BatchedPstmtHolder {
try {
bs.close();
} catch (SQLException ex) {
// error closing PreparedStatement
logger.error(null, ex);
logger.error("Error closing batched PreparedStatement", ex);
}
}
}
@@ -1294,7 +1294,7 @@ public final class DefaultPersister implements Persister {
// convert into a list of reference objects and perform delete by object
List<Object> refList = new ArrayList<Object>(childIds.size());
for (Object id : childIds) {
refList.add(targetDesc.createReference(null, id, null));
refList.add(targetDesc.createReference(null, false, id, null));
}
deleteList(refList, t, softDelete);
@@ -107,6 +107,8 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
private final SpiQuery<T> query;
private final boolean disableLazyLoading;
private Map<String, String> currentPathMap;
private String currentPrefix;
@@ -197,6 +199,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
this.lazyLoadManyProperty = query.getLazyLoadMany();
this.readOnly = request.isReadOnly();
this.disableLazyLoading = query.isDisableLazyLoading();
this.objectGraphNode = query.getParentNode();
this.profilingListener = query.getProfilingListener();
@@ -239,6 +242,11 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
return query.isAsDraft();
}
@Override
public boolean isDisableLazyLoading() {
return disableLazyLoading;
}
public Boolean isReadOnly() {
return readOnly;
}
@@ -197,7 +197,7 @@ public class CQueryFetchIds {
dataReader = null;
}
} catch (SQLException e) {
logger.error(null, e);
logger.error("Error closing DataReader", e);
}
try {
if (pstmt != null) {
@@ -205,7 +205,7 @@ public class CQueryFetchIds {
pstmt = null;
}
} catch (SQLException e) {
logger.error(null, e);
logger.error("Error closing PreparedStatement", e);
}
}
@@ -228,6 +228,11 @@ public class CQueryFetchIds {
return Boolean.FALSE;
}
@Override
public boolean isDisableLazyLoading() {
return false;
}
public boolean isRawSql() {
return false;
}
@@ -128,7 +128,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
// the bean has an Id property and we want to use it
this.readId = withId && (desc.getIdProperty() != null);
this.disableLazyLoad = disableLazyLoad || !readId || desc.isSqlSelectBased() || temporalVersions;
this.disableLazyLoad = disableLazyLoad || !readId || desc.isRawSqlBased() || temporalVersions;
this.partialObject = props.isPartialObject();
this.properties = props.getProps();
@@ -303,7 +303,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
if (!lazyLoadMany && localBean != null) {
ctx.setCurrentPrefix(prefix, pathMap);
if (readId && !temporalVersions) {
createListProxies(localDesc, ctx, localBean);
createListProxies(localDesc, ctx, localBean, disableLazyLoad);
}
if (temporalMode == SpiQuery.TemporalMode.DRAFT) {
localDesc.setDraft(localBean);
@@ -360,7 +360,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
* Create lazy loading proxies for the Many's except for the one that is
* included in the actual query.
*/
private void createListProxies(BeanDescriptor<?> localDesc, DbReadContext ctx, EntityBean localBean) {
private void createListProxies(BeanDescriptor<?> localDesc, DbReadContext ctx, EntityBean localBean, boolean disableLazyLoad) {
BeanPropertyAssocMany<?> fetchedMany = ctx.getManyProperty();
@@ -371,8 +371,13 @@ public class SqlTreeNodeBean implements SqlTreeNode {
if (fetchedMany == null || !fetchedMany.equals(manys[i])) {
// create a proxy for the many (deferred fetching)
BeanCollection<?> ref = manys[i].createReferenceIfNull(localBean);
if (ref != null && !ref.isRegisteredWithLoadContext()) {
ctx.register(manys[i].getName(), ref);
if (ref != null) {
if (disableLazyLoad) {
ref.setDisableLazyLoad(true);
}
if (!ref.isRegisteredWithLoadContext()) {
ctx.register(manys[i].getName(), ref);
}
}
}
}
@@ -433,15 +438,14 @@ public class SqlTreeNodeBean implements SqlTreeNode {
public void appendWhere(DbSqlContext ctx) {
// Only apply inheritance to root node as any join will alreay have the inheritance join include - see TableJoin
// Only apply inheritance to root node as any join will already have the inheritance join include - see TableJoin
if (inheritInfo != null && nodeBeanProp == null) {
if (!inheritInfo.isRoot()) {
// restrict to this type and
// sub types of this type.
// restrict to this type and sub types of this type.
if (ctx.length() > 0) {
ctx.append(" and");
}
ctx.append(" ").append(ctx.getTableAlias(prefix)).append(".");// tableAlias
ctx.append(" ").append(ctx.getTableAlias(prefix)).append(".");
ctx.append(inheritInfo.getWhere()).append(" ");
}
}
@@ -38,6 +38,8 @@ import java.util.Set;
*/
public class DefaultOrmQuery<T> implements SpiQuery<T> {
public static final String DEFAULT_QUERY_NAME = "default";
private final Class<T> beanType;
private final BeanDescriptor<T> beanDescriptor;
@@ -699,6 +701,13 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return forUpdate;
}
@Override
public void setDefaultRawSqlIfRequired() {
if (beanDescriptor.isRawSqlBased() && rawSql == null) {
rawSql = beanDescriptor.getNamedRawSql(DEFAULT_QUERY_NAME);
}
}
@Override
public DefaultOrmQuery<T> setAutoTune(boolean autoTune) {
this.autoTune = autoTune;
@@ -146,11 +146,10 @@ public class OrmQueryDetailParser {
}
private void readSelect() {
String path = null;
String props = parser.nextWord();
if (props.startsWith("(")) {
props = props.substring(1, props.length() - 1);
OrmQueryProperties base = new OrmQueryProperties(path, props);
OrmQueryProperties base = new OrmQueryProperties(null, props);
detail.setBase(base);
parser.nextWord();
} else {
@@ -11,6 +11,7 @@ import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.dbmigration.DbOffline;
import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutable;
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse;
@@ -156,6 +157,8 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
private final boolean postgres;
private final boolean offlineMigrationGeneration;
// OPTIONAL ScalarTypes registered if Jackson/JsonNode is in the classpath
/**
@@ -198,6 +201,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
this.extraTypeFactory = new DefaultTypeFactory(config);
this.postgres = isPostgres(config.getDatabasePlatform());
this.offlineMigrationGeneration = DbOffline.isGenerateMigration();
initialiseStandard(jsonDateTime, config);
initialiseJavaTimeTypes(jsonDateTime, config);
@@ -961,15 +965,13 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
nativeMap.put(Types.BIT, booleanType);
}
boolean nativeUuidType = databasePlatform.isNativeUuidType();
ServerConfig.DbUuid dbUuid = config.getDbUuid();
if (nativeUuidType && dbUuid == ServerConfig.DbUuid.AUTO) {
// DB has native support for UUID
if (offlineMigrationGeneration || (databasePlatform.isNativeUuidType() && dbUuid.useNativeType())) {
typeMap.put(UUID.class, new ScalarTypeUUIDNative());
} else {
// Store UUID as binary(16) or varchar(40)
ScalarType<?> uuidType = (ServerConfig.DbUuid.BINARY == dbUuid) ? new ScalarTypeUUIDBinary() : new ScalarTypeUUIDVarchar();
ScalarType<?> uuidType = dbUuid.useBinary() ? new ScalarTypeUUIDBinary() : new ScalarTypeUUIDVarchar();
typeMap.put(UUID.class, uuidType);
}
@@ -0,0 +1,12 @@
package com.avaje.ebeaninternal.server.type;
/**
* Marks types that can be mapped differently to different DB platforms.
*/
public interface ScalarTypeLogicalType {
/**
* Return the DB agnostic logical type.
*/
int getLogicalType();
}
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.type;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebeaninternal.server.core.BasicTypeConverter;
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType;
import com.fasterxml.jackson.core.JsonGenerator;
@@ -13,12 +14,17 @@ import java.util.UUID;
/**
* Base UUID type for string formatting, json handling etc.
*/
public abstract class ScalarTypeUUIDBase extends ScalarTypeBase<UUID> {
public abstract class ScalarTypeUUIDBase extends ScalarTypeBase<UUID> implements ScalarTypeLogicalType {
public ScalarTypeUUIDBase(boolean jdbcNative, int jdbcType) {
super(UUID.class, jdbcNative, jdbcType);
}
@Override
public int getLogicalType() {
return DbType.UUID;
}
@Override
public boolean isMutable() {
return false;
@@ -11,118 +11,118 @@ import java.lang.reflect.Modifier;
public class CheckImmutable {
private static final Logger logger = LoggerFactory.getLogger(CheckImmutable.class);
private final KnownImmutable knownImmutable;
private static final Logger logger = LoggerFactory.getLogger(CheckImmutable.class);
public CheckImmutable(KnownImmutable knownImmutable) {
this.knownImmutable = knownImmutable;
}
public CheckImmutableResponse checkImmutable(Class<?> cls) {
private final KnownImmutable knownImmutable;
CheckImmutableResponse res = new CheckImmutableResponse();
isImmutable(cls, res);
if (res.isImmutable()){
res.setCompoundType(isCompoundType(cls));
}
return res;
public CheckImmutable(KnownImmutable knownImmutable) {
this.knownImmutable = knownImmutable;
}
public CheckImmutableResponse checkImmutable(Class<?> cls) {
CheckImmutableResponse res = new CheckImmutableResponse();
isImmutable(cls, res);
if (res.isImmutable()) {
res.setCompoundType(isCompoundType(cls));
}
private boolean isCompoundType(Class<?> cls) {
int maxLength = 0;
Constructor<?> chosen = null;
// find the constructor with the most number of parameters
Constructor<?>[] constructors = cls.getConstructors();
for (int i = 0; i < constructors.length; i++) {
Class<?>[] parameterTypes = constructors[i].getParameterTypes();
if (parameterTypes.length > maxLength){
maxLength = parameterTypes.length;
chosen = constructors[i];
}
}
logger.debug("checkImmutable "+cls+" constructor "+chosen);
return maxLength > 1;
}
private boolean isImmutable(Class<?> cls, CheckImmutableResponse res) {
return res;
}
if (knownImmutable.isKnownImmutable(cls)) {
return true;
}
if (cls.isArray()) {
return false;
}
if (hasDefaultConstructor(cls)){
// must not have a default constructor to be considered immutable
res.setReasonNotImmutable(cls+" has a default constructor");
return false;
}
private boolean isCompoundType(Class<?> cls) {
// check super class
Class<?> superClass = cls.getSuperclass();
int maxLength = 0;
Constructor<?> chosen = null;
if (!isImmutable(superClass, res)) {
res.setReasonNotImmutable("Super not Immutable " + superClass);
return false;
}
if (!hasAllFinalFields(cls, res)){
return false;
}
// Lets hope we didn't forget something
return true;
// find the constructor with the most number of parameters
Constructor<?>[] constructors = cls.getConstructors();
for (int i = 0; i < constructors.length; i++) {
Class<?>[] parameterTypes = constructors[i].getParameterTypes();
if (parameterTypes.length > maxLength) {
maxLength = parameterTypes.length;
chosen = constructors[i];
}
}
private boolean hasAllFinalFields(Class<?> cls, CheckImmutableResponse res){
logger.debug("checkImmutable " + cls + " constructor " + chosen);
// Check all fields defined in the class for type and if they are final
Field[] objFields = cls.getDeclaredFields();
for (int i = 0; i < objFields.length; i++) {
if (!Modifier.isStatic(objFields[i].getModifiers())) {
if (!Modifier.isFinal(objFields[i].getModifiers())) {
res.setReasonNotImmutable("Non final field " + cls + "." + objFields[i].getName());
return false;
}
if (!isImmutable(objFields[i].getType(), res)) {
res.setReasonNotImmutable("Non Immutable field type " + objFields[i].getType());
return false;
}
}
}
return maxLength > 1;
}
return true;
private boolean isImmutable(Class<?> cls, CheckImmutableResponse res) {
if (knownImmutable.isKnownImmutable(cls)) {
return true;
}
private boolean hasDefaultConstructor(Class<?> cls) {
Class<?>[] noParams = new Class<?>[0];
try {
cls.getDeclaredConstructor(noParams);
return true;
} catch (SecurityException e) {
// this is ok
return false;
} catch (NoSuchMethodException e) {
// this is expected for our IVO's
return false;
}
if (cls.isArray()) {
return false;
}
if (hasDefaultConstructor(cls)) {
// must not have a default constructor to be considered immutable
res.setReasonNotImmutable(cls + " has a default constructor");
return false;
}
// check super class
Class<?> superClass = cls.getSuperclass();
if (!isImmutable(superClass, res)) {
res.setReasonNotImmutable("Super not Immutable " + superClass);
return false;
}
if (!hasAllFinalFields(cls, res)) {
return false;
}
// Lets hope we didn't forget something
return true;
}
private boolean hasAllFinalFields(Class<?> cls, CheckImmutableResponse res) {
// Check all fields defined in the class for type and if they are final
Field[] objFields = cls.getDeclaredFields();
for (int i = 0; i < objFields.length; i++) {
if (!Modifier.isStatic(objFields[i].getModifiers())) {
if (!Modifier.isFinal(objFields[i].getModifiers())) {
res.setReasonNotImmutable("Non final field " + cls + "." + objFields[i].getName());
return false;
}
if (!isImmutable(objFields[i].getType(), res)) {
res.setReasonNotImmutable("Non Immutable field type " + objFields[i].getType());
return false;
}
}
}
return true;
}
private boolean hasDefaultConstructor(Class<?> cls) {
Class<?>[] noParams = new Class<?>[0];
try {
cls.getDeclaredConstructor(noParams);
return true;
} catch (SecurityException e) {
// this is ok
return false;
} catch (NoSuchMethodException e) {
// this is expected for our IVO's
return false;
}
}
}
@@ -1,37 +1,36 @@
package com.avaje.ebeaninternal.server.type.reflect;
public class CheckImmutableResponse {
private boolean immutable = true;
private String reasonNotImmutable;
private boolean immutable = true;
private boolean compoundType;
public String toString(){
if(immutable){
return "immutable";
} else {
return "not immutable due to:"+reasonNotImmutable;
}
}
private String reasonNotImmutable;
public boolean isCompoundType() {
return compoundType;
}
private boolean compoundType;
public void setCompoundType(boolean compoundType) {
this.compoundType = compoundType;
public String toString() {
if (immutable) {
return "immutable";
} else {
return "not immutable due to:" + reasonNotImmutable;
}
}
public void setReasonNotImmutable(String error) {
this.immutable = false;
this.reasonNotImmutable = error;
}
public boolean isCompoundType() {
return compoundType;
}
public boolean isImmutable() {
return immutable;
}
public void setCompoundType(boolean compoundType) {
this.compoundType = compoundType;
}
public void setReasonNotImmutable(String error) {
this.immutable = false;
this.reasonNotImmutable = error;
}
public boolean isImmutable() {
return immutable;
}
}
@@ -6,25 +6,25 @@ import java.lang.reflect.Method;
public class ImmutableMeta {
private final Constructor<?> constructor;
private final Method[] readers;
public ImmutableMeta(Constructor<?> constructor, Method[] readers) {
this.constructor = constructor;
this.readers = readers;
}
public Constructor<?> getConstructor() {
return constructor;
}
private final Constructor<?> constructor;
public Method[] getReaders() {
return readers;
}
public boolean isCompoundType() {
return readers.length > 1;
}
private final Method[] readers;
public ImmutableMeta(Constructor<?> constructor, Method[] readers) {
this.constructor = constructor;
this.readers = readers;
}
public Constructor<?> getConstructor() {
return constructor;
}
public Method[] getReaders() {
return readers;
}
public boolean isCompoundType() {
return readers.length > 1;
}
}
@@ -14,213 +14,212 @@ import java.util.HashSet;
public class ImmutableMetaFactory {
private static final Logger logger = LoggerFactory.getLogger(ImmutableMetaFactory.class);
public ImmutableMeta createImmutableMeta(Class<?> cls) {
ScoreConstructor[] scoreConstructors = scoreConstructors(cls);
ArrayList<RuntimeException> errors = new ArrayList<RuntimeException>();
// search the constructors in score order ...
// ... we need to find a set of readers for each
// ... type in the constructor
for (int i = 0; i < scoreConstructors.length; i++) {
Constructor<?> constructor = scoreConstructors[i].constructor;
private static final Logger logger = LoggerFactory.getLogger(ImmutableMetaFactory.class);
try {
Method[] getters = findGetters(cls, constructor);
public ImmutableMeta createImmutableMeta(Class<?> cls) {
return new ImmutableMeta(constructor, getters);
} catch (NoSuchMethodException e){
String msg = "Error finding getter method on "+cls+" with constructor "+constructor;
errors.add(new RuntimeException(msg, e));
}
}
String msg = "Was unable to use reflection to find a constructor and appropriate getters for" +
"immutable type "+cls+". The errors while looking for the getter methods follow:";
logger.error(msg);
for (RuntimeException runtimeException : errors) {
logger.error("Error with " + cls, runtimeException);
}
ScoreConstructor[] scoreConstructors = scoreConstructors(cls);
msg = "Unable to use reflection to build ImmutableMeta for " + cls
+ ". Associated Errors trying to find a constructor and getter methods have been logged";
ArrayList<RuntimeException> errors = new ArrayList<RuntimeException>();
throw new RuntimeException(msg);
// search the constructors in score order ...
// ... we need to find a set of readers for each
// ... type in the constructor
for (int i = 0; i < scoreConstructors.length; i++) {
Constructor<?> constructor = scoreConstructors[i].constructor;
try {
Method[] getters = findGetters(cls, constructor);
return new ImmutableMeta(constructor, getters);
} catch (NoSuchMethodException e) {
String msg = "Error finding getter method on " + cls + " with constructor " + constructor;
errors.add(new RuntimeException(msg, e));
}
}
private ScoreConstructor getScore(Constructor<?> c) {
Class<?>[] parameterTypes = c.getParameterTypes();
int score = -1000 * parameterTypes.length;
for (int i = 0; i < parameterTypes.length; i++) {
if (parameterTypes[i].equals(String.class)){
// string is very generic and we would prefer
// a more specific type if that was available
score = score + 1;
} else if (parameterTypes[i].equals(BigDecimal.class)) {
score = score - 10;
} else if (parameterTypes[i].equals(Timestamp.class)) {
score = score - 10;
} else if (parameterTypes[i].equals(double.class)) {
score = score - 9;
} else if (parameterTypes[i].equals(Double.class)) {
score = score - 8;
} else if (parameterTypes[i].equals(float.class)) {
score = score - 7;
} else if (parameterTypes[i].equals(Float.class)) {
score = score - 6;
} else if (parameterTypes[i].equals(long.class)) {
score = score - 5;
} else if (parameterTypes[i].equals(Long.class)) {
score = score - 4;
} else if (parameterTypes[i].equals(int.class)) {
score = score - 3;
} else if (parameterTypes[i].equals(Integer.class)) {
score = score - 2;
}
}
return new ScoreConstructor(score, c);
String msg = "Was unable to use reflection to find a constructor and appropriate getters for" +
"immutable type " + cls + ". The errors while looking for the getter methods follow:";
logger.error(msg);
for (RuntimeException runtimeException : errors) {
logger.error("Error with " + cls, runtimeException);
}
private ScoreConstructor[] scoreConstructors(Class<?> cls) {
// find the constructor with the most number of parameters
int maxParamCount = 0;
Constructor<?>[] constructors = cls.getConstructors();
ScoreConstructor[] score = new ScoreConstructor[constructors.length];
for (int i = 0; i < constructors.length; i++) {
score[i] = getScore(constructors[i]);
if (score[i].hasDuplicateParamTypes()){
String msg = "Duplicate parameter types in "+score[i].constructor;
throw new IllegalStateException(msg);
}
if (score[i].getParamCount() > maxParamCount){
maxParamCount = score[i].getParamCount();
}
}
// filter out any constructors with less parameters than the max
ArrayList<ScoreConstructor> list = new ArrayList<ScoreConstructor>();
for (int i = 0; i < score.length; i++) {
if (score[i].getParamCount() == maxParamCount){
list.add(score[i]);
}
}
score = list.toArray(new ScoreConstructor[list.size()]);
// sort into score ascending order
Arrays.sort(score);
msg = "Unable to use reflection to build ImmutableMeta for " + cls
+ ". Associated Errors trying to find a constructor and getter methods have been logged";
return score;
throw new RuntimeException(msg);
}
private ScoreConstructor getScore(Constructor<?> c) {
Class<?>[] parameterTypes = c.getParameterTypes();
int score = -1000 * parameterTypes.length;
for (int i = 0; i < parameterTypes.length; i++) {
if (parameterTypes[i].equals(String.class)) {
// string is very generic and we would prefer
// a more specific type if that was available
score = score + 1;
} else if (parameterTypes[i].equals(BigDecimal.class)) {
score = score - 10;
} else if (parameterTypes[i].equals(Timestamp.class)) {
score = score - 10;
} else if (parameterTypes[i].equals(double.class)) {
score = score - 9;
} else if (parameterTypes[i].equals(Double.class)) {
score = score - 8;
} else if (parameterTypes[i].equals(float.class)) {
score = score - 7;
} else if (parameterTypes[i].equals(Float.class)) {
score = score - 6;
} else if (parameterTypes[i].equals(long.class)) {
score = score - 5;
} else if (parameterTypes[i].equals(Long.class)) {
score = score - 4;
} else if (parameterTypes[i].equals(int.class)) {
score = score - 3;
} else if (parameterTypes[i].equals(Integer.class)) {
score = score - 2;
}
}
private Method[] findGetters(Class<?> cls, Constructor<?> c) throws NoSuchMethodException {
Method[] methods = cls.getMethods();
return new ScoreConstructor(score, c);
}
Class<?>[] paramTypes = c.getParameterTypes();
Method[] readers = new Method[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
Method getter = findGetter(paramTypes[i], methods);
if (getter == null && paramTypes.length == 1 && paramTypes[i].equals(String.class)){
getter = findToString(cls);
}
if (getter == null) {
throw new NoSuchMethodException("Get Method not found for "+paramTypes[i]+" in "+cls);
}
readers[i] = getter;
}
return readers;
private ScoreConstructor[] scoreConstructors(Class<?> cls) {
// find the constructor with the most number of parameters
int maxParamCount = 0;
Constructor<?>[] constructors = cls.getConstructors();
ScoreConstructor[] score = new ScoreConstructor[constructors.length];
for (int i = 0; i < constructors.length; i++) {
score[i] = getScore(constructors[i]);
if (score[i].hasDuplicateParamTypes()) {
String msg = "Duplicate parameter types in " + score[i].constructor;
throw new IllegalStateException(msg);
}
if (score[i].getParamCount() > maxParamCount) {
maxParamCount = score[i].getParamCount();
}
}
private Method findToString(Class<?> cls) throws NoSuchMethodException {
try {
return cls.getDeclaredMethod("toString", new Class<?>[0]);
} catch (SecurityException e) {
throw new NoSuchMethodException("SecurityException "+e+" trying to find toString method on "+cls);
}
// filter out any constructors with less parameters than the max
ArrayList<ScoreConstructor> list = new ArrayList<ScoreConstructor>();
for (int i = 0; i < score.length; i++) {
if (score[i].getParamCount() == maxParamCount) {
list.add(score[i]);
}
}
private Method findGetter(Class<?> paramType, Method[] methods) {
for (int i = 0; i < methods.length; i++) {
if (!Modifier.isStatic(methods[i].getModifiers())) {
if (methods[i].getParameterTypes().length == 0) {
// could be a getter
String methName = methods[i].getName();
if (methName.equals("hashCode")){
} else if (methName.equals("toString")) {
score = list.toArray(new ScoreConstructor[list.size()]);
} else {
Class<?> returnType = methods[i].getReturnType();
if (paramType.equals(returnType)){
return methods[i];
}
}
}
// sort into score ascending order
Arrays.sort(score);
return score;
}
private Method[] findGetters(Class<?> cls, Constructor<?> c) throws NoSuchMethodException {
Method[] methods = cls.getMethods();
Class<?>[] paramTypes = c.getParameterTypes();
Method[] readers = new Method[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
Method getter = findGetter(paramTypes[i], methods);
if (getter == null && paramTypes.length == 1 && paramTypes[i].equals(String.class)) {
getter = findToString(cls);
}
if (getter == null) {
throw new NoSuchMethodException("Get Method not found for " + paramTypes[i] + " in " + cls);
}
readers[i] = getter;
}
return readers;
}
private Method findToString(Class<?> cls) throws NoSuchMethodException {
try {
return cls.getDeclaredMethod("toString", new Class<?>[0]);
} catch (SecurityException e) {
throw new NoSuchMethodException("SecurityException " + e + " trying to find toString method on " + cls);
}
}
private Method findGetter(Class<?> paramType, Method[] methods) {
for (int i = 0; i < methods.length; i++) {
if (!Modifier.isStatic(methods[i].getModifiers())) {
if (methods[i].getParameterTypes().length == 0) {
// could be a getter
String methName = methods[i].getName();
if (methName.equals("hashCode")) {
} else if (methName.equals("toString")) {
} else {
Class<?> returnType = methods[i].getReturnType();
if (paramType.equals(returnType)) {
return methods[i];
}
}
}
return null;
}
}
return null;
}
private static class ScoreConstructor implements Comparable<ScoreConstructor> {
final int score;
final Constructor<?> constructor;
private ScoreConstructor(int score, Constructor<?> constructor) {
this.score = score;
this.constructor = constructor;
}
private static class ScoreConstructor implements Comparable<ScoreConstructor>{
final int score;
final Constructor<?> constructor;
private ScoreConstructor(int score, Constructor<?> constructor) {
this.score = score;
this.constructor = constructor;
}
@Override
public boolean equals(Object obj) {
// remove FindBugs warning
return obj == this;
}
public int compareTo(ScoreConstructor o) {
return (score<o.score ? -1 : (score==o.score ? 0 : 1));
}
public int getParamCount() {
return constructor.getParameterTypes().length;
}
public boolean hasDuplicateParamTypes() {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length < 2){
return false;
}
HashSet<Class<?>> set = new HashSet<Class<?>>();
for (int i = 0; i < parameterTypes.length; i++) {
if (!set.add(parameterTypes[i])) {
return true;
}
}
return false;
}
@Override
public boolean equals(Object obj) {
// remove FindBugs warning
return obj == this;
}
public int compareTo(ScoreConstructor o) {
return (score < o.score ? -1 : (score == o.score ? 0 : 1));
}
public int getParamCount() {
return constructor.getParameterTypes().length;
}
public boolean hasDuplicateParamTypes() {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length < 2) {
return false;
}
HashSet<Class<?>> set = new HashSet<Class<?>>();
for (int i = 0; i < parameterTypes.length; i++) {
if (!set.add(parameterTypes[i])) {
return true;
}
}
return false;
}
}
}
@@ -6,41 +6,41 @@ import java.util.Arrays;
import com.avaje.ebean.config.CompoundType;
import com.avaje.ebean.config.CompoundTypeProperty;
@SuppressWarnings({ "rawtypes" })
@SuppressWarnings({"rawtypes"})
public class ReflectionBasedCompoundType implements CompoundType {
private final Constructor<?> constructor;
private final ReflectionBasedCompoundTypeProperty[] props;
public ReflectionBasedCompoundType(Constructor<?> constructor, ReflectionBasedCompoundTypeProperty[] props) {
this.constructor = constructor;
this.props = props;
}
public String toString() {
return "ReflectionBasedCompoundType "+constructor+" "+Arrays.toString(props);
}
public Object create(Object[] propertyValues) {
try {
return constructor.newInstance(propertyValues);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private final Constructor<?> constructor;
public CompoundTypeProperty[] getProperties() {
return props;
}
private final ReflectionBasedCompoundTypeProperty[] props;
public Class<?> getPropertyType(int i){
return props[i].getPropertyType();
public ReflectionBasedCompoundType(Constructor<?> constructor, ReflectionBasedCompoundTypeProperty[] props) {
this.constructor = constructor;
this.props = props;
}
public String toString() {
return "ReflectionBasedCompoundType " + constructor + " " + Arrays.toString(props);
}
public Object create(Object[] propertyValues) {
try {
return constructor.newInstance(propertyValues);
} catch (Exception e) {
throw new RuntimeException(e);
}
public Class<?> getCompoundType() {
return constructor.getDeclaringClass();
}
}
public CompoundTypeProperty[] getProperties() {
return props;
}
public Class<?> getPropertyType(int i) {
return props[i].getPropertyType();
}
public Class<?> getCompoundType() {
return constructor.getDeclaringClass();
}
}
@@ -4,47 +4,47 @@ import java.lang.reflect.Method;
import com.avaje.ebean.config.CompoundTypeProperty;
@SuppressWarnings({ "rawtypes" })
@SuppressWarnings({"rawtypes"})
public class ReflectionBasedCompoundTypeProperty implements CompoundTypeProperty {
private static final Object[] NO_ARGS = new Object[0];
private final Method reader;
private final String name;
private final Class<?> propertyType;
public ReflectionBasedCompoundTypeProperty(String name, Method reader, Class<?> propertyType) {
this.name = name;
this.reader = reader;
this.propertyType = propertyType;
}
public String toString() {
return name;
}
public int getDbType() {
return 0;
}
private static final Object[] NO_ARGS = new Object[0];
public String getName() {
return name;
}
private final Method reader;
public Object getValue(Object valueObject) {
try {
return reader.invoke(valueObject, NO_ARGS);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private final String name;
public Class<?> getPropertyType(){
return propertyType;
private final Class<?> propertyType;
public ReflectionBasedCompoundTypeProperty(String name, Method reader, Class<?> propertyType) {
this.name = name;
this.reader = reader;
this.propertyType = propertyType;
}
public String toString() {
return name;
}
public int getDbType() {
return 0;
}
public String getName() {
return name;
}
public Object getValue(Object valueObject) {
try {
return reader.invoke(valueObject, NO_ARGS);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Class<?> getPropertyType() {
return propertyType;
}
}
@@ -5,45 +5,45 @@ import java.lang.reflect.Method;
import com.avaje.ebean.config.ScalarTypeConverter;
@SuppressWarnings({ "rawtypes" })
@SuppressWarnings({"rawtypes"})
public class ReflectionBasedScalarTypeConverter implements ScalarTypeConverter {
private static final Object[] NO_ARGS = new Object[0];
private final Constructor<?> constructor;
private final Method reader;
public ReflectionBasedScalarTypeConverter(Constructor<?> constructor, Method reader) {
this.constructor = constructor;
this.reader = reader;
}
public Object getNullValue() {
return null;
}
private static final Object[] NO_ARGS = new Object[0];
public Object unwrapValue(Object beanType) {
if (beanType == null){
return null;
}
try {
return reader.invoke(beanType, NO_ARGS);
} catch (Exception e) {
String msg = "Error invoking read method "+reader.getName()
+" on "+beanType.getClass().getName();
throw new RuntimeException(msg);
}
private final Constructor<?> constructor;
private final Method reader;
public ReflectionBasedScalarTypeConverter(Constructor<?> constructor, Method reader) {
this.constructor = constructor;
this.reader = reader;
}
public Object getNullValue() {
return null;
}
public Object unwrapValue(Object beanType) {
if (beanType == null) {
return null;
}
try {
return reader.invoke(beanType, NO_ARGS);
} catch (Exception e) {
String msg = "Error invoking read method " + reader.getName()
+ " on " + beanType.getClass().getName();
throw new RuntimeException(msg);
}
}
public Object wrapValue(Object scalarType) {
try {
return constructor.newInstance(scalarType);
} catch (Exception e) {
String msg = "Error invoking constructor " + constructor + " with " + scalarType;
throw new RuntimeException(msg);
}
}
public Object wrapValue(Object scalarType) {
try {
return constructor.newInstance(scalarType);
} catch (Exception e) {
String msg = "Error invoking constructor "+constructor+" with "+scalarType;
throw new RuntimeException(msg);
}
}
}
@@ -9,70 +9,70 @@ import com.avaje.ebeaninternal.server.type.TypeManager;
public class ReflectionBasedTypeBuilder {
private final TypeManager typeManager;
public ReflectionBasedTypeBuilder(TypeManager typeManager) {
this.typeManager = typeManager;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public ScalarType<?> buildScalarType(ImmutableMeta meta) {
if (meta.isCompoundType()){
throw new RuntimeException("Must be scalar");
}
Constructor<?> constructor = meta.getConstructor();
Class<?> logicalType = constructor.getDeclaringClass();
Method[] readers = meta.getReaders();
Class<?> returnType = readers[0].getReturnType();
ScalarType<?> scalarType = typeManager.recursiveCreateScalarTypes(returnType);
ReflectionBasedScalarTypeConverter r = new ReflectionBasedScalarTypeConverter(constructor, readers[0]);
return new ScalarTypeWrapper(logicalType, scalarType, r);
}
public ReflectionBasedCompoundType buildCompound(ImmutableMeta meta) {
Constructor<?> constructor = meta.getConstructor();
private final TypeManager typeManager;
Method[] readers = meta.getReaders();
public ReflectionBasedTypeBuilder(TypeManager typeManager) {
this.typeManager = typeManager;
}
ReflectionBasedCompoundTypeProperty[] props = new ReflectionBasedCompoundTypeProperty[readers.length];
for (int i = 0; i < readers.length; i++) {
Class<?> returnType = readers[i].getReturnType();
// ensure that return type is also a ScalarDataReader
typeManager.recursiveCreateScalarDataReader(returnType);
String name = getPropertyName(readers[i]);
props[i] = new ReflectionBasedCompoundTypeProperty(name, readers[i], returnType);
}
return new ReflectionBasedCompoundType(constructor, props);
@SuppressWarnings({"unchecked", "rawtypes"})
public ScalarType<?> buildScalarType(ImmutableMeta meta) {
if (meta.isCompoundType()) {
throw new RuntimeException("Must be scalar");
}
private String getPropertyName(Method method){
String name = method.getName();
if (name.startsWith("is")){
return lowerFirstChar(name.substring(2));
} else if (name.startsWith("get")){
return lowerFirstChar(name.substring(3));
}
String msg = "Expecting method "+name+" to start with is or get "
+" so as to follow bean specification?";
throw new RuntimeException(msg);
Constructor<?> constructor = meta.getConstructor();
Class<?> logicalType = constructor.getDeclaringClass();
Method[] readers = meta.getReaders();
Class<?> returnType = readers[0].getReturnType();
ScalarType<?> scalarType = typeManager.recursiveCreateScalarTypes(returnType);
ReflectionBasedScalarTypeConverter r = new ReflectionBasedScalarTypeConverter(constructor, readers[0]);
return new ScalarTypeWrapper(logicalType, scalarType, r);
}
public ReflectionBasedCompoundType buildCompound(ImmutableMeta meta) {
Constructor<?> constructor = meta.getConstructor();
Method[] readers = meta.getReaders();
ReflectionBasedCompoundTypeProperty[] props = new ReflectionBasedCompoundTypeProperty[readers.length];
for (int i = 0; i < readers.length; i++) {
Class<?> returnType = readers[i].getReturnType();
// ensure that return type is also a ScalarDataReader
typeManager.recursiveCreateScalarDataReader(returnType);
String name = getPropertyName(readers[i]);
props[i] = new ReflectionBasedCompoundTypeProperty(name, readers[i], returnType);
}
private String lowerFirstChar(String name) {
return Character.toLowerCase(name.charAt(0))+name.substring(1);
return new ReflectionBasedCompoundType(constructor, props);
}
private String getPropertyName(Method method) {
String name = method.getName();
if (name.startsWith("is")) {
return lowerFirstChar(name.substring(2));
} else if (name.startsWith("get")) {
return lowerFirstChar(name.substring(3));
}
String msg = "Expecting method " + name + " to start with is or get "
+ " so as to follow bean specification?";
throw new RuntimeException(msg);
}
private String lowerFirstChar(String name) {
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
}
}
@@ -0,0 +1,27 @@
package com.avaje.ebeaninternal.xmlmapping;
import com.avaje.ebeaninternal.xmlmapping.model.XmEbean;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.InputStream;
public class XmlMappingReader {
/**
* Read and return a Migration from an xml document.
*/
public static XmEbean read(InputStream is) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(XmEbean.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
return (XmEbean) unmarshaller.unmarshal(is);
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,80 @@
package com.avaje.ebeaninternal.xmlmapping.model;
import javax.xml.bind.annotation.XmlRegistry;
/**
* This object contains factory methods for each
* Java content interface and Java element interface
* generated in the com.avaje.ebeaninternal.xmlmapping.model package.
* <p>An ObjectFactory allows you to programatically
* construct new instances of the Java representation
* for XML content. The Java representation of XML
* content can consist of schema derived interfaces
* and classes representing the binding of schema
* type definitions, element declarations and model
* groups. Factory methods for each of these are
* provided in this class.
*
*/
@XmlRegistry
public class ObjectFactory {
/**
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.avaje.ebeaninternal.xmlmapping.model
*
*/
public ObjectFactory() {
}
/**
* Create an instance of {@link XmRawSql }
*
*/
public XmRawSql createRawSql() {
return new XmRawSql();
}
/**
* Create an instance of {@link XmAliasMapping }
*
*/
public XmAliasMapping createAliasMapping() {
return new XmAliasMapping();
}
/**
* Create an instance of {@link XmColumnMapping }
*
*/
public XmColumnMapping createColumnMapping() {
return new XmColumnMapping();
}
/**
* Create an instance of {@link XmQuery }
*
*/
public XmQuery createQuery() {
return new XmQuery();
}
/**
* Create an instance of {@link XmEbean }
*
*/
public XmEbean createEbean() {
return new XmEbean();
}
/**
* Create an instance of {@link XmEntity }
*
*/
public XmEntity createEntity() {
return new XmEntity();
}
}
@@ -0,0 +1,87 @@
package com.avaje.ebeaninternal.xmlmapping.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="alias" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="property" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "alias-mapping")
public class XmAliasMapping {
@XmlAttribute(name = "alias", required = true)
protected String alias;
@XmlAttribute(name = "property", required = true)
protected String property;
/**
* Gets the value of the alias property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getAlias() {
return alias;
}
/**
* Sets the value of the alias property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setAlias(String value) {
this.alias = value;
}
/**
* Gets the value of the property property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getProperty() {
return property;
}
/**
* Sets the value of the property property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setProperty(String value) {
this.property = value;
}
}
@@ -0,0 +1,87 @@
package com.avaje.ebeaninternal.xmlmapping.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="column" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="property" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "column-mapping")
public class XmColumnMapping {
@XmlAttribute(name = "column", required = true)
protected String column;
@XmlAttribute(name = "property", required = true)
protected String property;
/**
* Gets the value of the column property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getColumn() {
return column;
}
/**
* Sets the value of the column property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setColumn(String value) {
this.column = value;
}
/**
* Gets the value of the property property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getProperty() {
return property;
}
/**
* Sets the value of the property property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setProperty(String value) {
this.property = value;
}
}
@@ -0,0 +1,71 @@
package com.avaje.ebeaninternal.xmlmapping.model;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/ebean}entity" maxOccurs="unbounded"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"entity"
})
@XmlRootElement(name = "ebean")
public class XmEbean {
@XmlElement(required = true)
protected List<XmEntity> entity;
/**
* Gets the value of the entity property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the entity property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getEntity().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link XmEntity }
*
*
*/
public List<XmEntity> getEntity() {
if (entity == null) {
entity = new ArrayList<XmEntity>();
}
return this.entity;
}
}
@@ -0,0 +1,99 @@
package com.avaje.ebeaninternal.xmlmapping.model;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/ebean}raw-sql" maxOccurs="unbounded"/>
* &lt;/sequence>
* &lt;attribute name="class" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"rawSql"
})
@XmlRootElement(name = "entity")
public class XmEntity {
@XmlElement(name = "raw-sql", required = true)
protected List<XmRawSql> rawSql;
@XmlAttribute(name = "class", required = true)
protected String clazz;
/**
* Gets the value of the rawSql property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the rawSql property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getRawSql().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link XmRawSql }
*
*
*/
public List<XmRawSql> getRawSql() {
if (rawSql == null) {
rawSql = new ArrayList<XmRawSql>();
}
return this.rawSql;
}
/**
* Gets the value of the clazz property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getClazz() {
return clazz;
}
/**
* Sets the value of the clazz property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setClazz(String value) {
this.clazz = value;
}
}
@@ -0,0 +1,61 @@
package com.avaje.ebeaninternal.xmlmapping.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;simpleContent>
* &lt;extension base="&lt;http://www.w3.org/2001/XMLSchema>string">
* &lt;/extension>
* &lt;/simpleContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"value"
})
@XmlRootElement(name = "query")
public class XmQuery {
@XmlValue
protected String value;
/**
* Gets the value of the value property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getValue() {
return value;
}
/**
* Sets the value of the value property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setValue(String value) {
this.value = value;
}
}
@@ -0,0 +1,160 @@
package com.avaje.ebeaninternal.xmlmapping.model;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/ebean}alias-mapping" maxOccurs="unbounded" minOccurs="0"/>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/ebean}column-mapping" maxOccurs="unbounded" minOccurs="0"/>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/ebean}query"/>
* &lt;/sequence>
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"aliasMapping",
"columnMapping",
"query"
})
@XmlRootElement(name = "raw-sql")
public class XmRawSql {
@XmlElement(name = "alias-mapping")
protected List<XmAliasMapping> aliasMapping;
@XmlElement(name = "column-mapping")
protected List<XmColumnMapping> columnMapping;
@XmlElement(required = true)
protected XmQuery query;
@XmlAttribute(name = "name", required = true)
protected String name;
/**
* Gets the value of the aliasMapping property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the aliasMapping property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getAliasMapping().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link XmAliasMapping }
*
*
*/
public List<XmAliasMapping> getAliasMapping() {
if (aliasMapping == null) {
aliasMapping = new ArrayList<XmAliasMapping>();
}
return this.aliasMapping;
}
/**
* Gets the value of the columnMapping property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the columnMapping property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getColumnMapping().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link XmColumnMapping }
*
*
*/
public List<XmColumnMapping> getColumnMapping() {
if (columnMapping == null) {
columnMapping = new ArrayList<XmColumnMapping>();
}
return this.columnMapping;
}
/**
* Gets the value of the query property.
*
* @return
* possible object is
* {@link XmQuery }
*
*/
public XmQuery getQuery() {
return query;
}
/**
* Sets the value of the query property.
*
* @param value
* allowed object is
* {@link XmQuery }
*
*/
public void setQuery(XmQuery value) {
this.query = value;
}
/**
* Gets the value of the name property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getName() {
return name;
}
/**
* Sets the value of the name property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setName(String value) {
this.name = value;
}
}
@@ -0,0 +1,2 @@
@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/ebean", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.avaje.ebeaninternal.xmlmapping.model;
+59
View File
@@ -0,0 +1,59 @@
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://ebean-orm.github.io/xml/ns/ebean"
targetNamespace="http://ebean-orm.github.io/xml/ns/ebean" elementFormDefault="qualified">
<!-- Root level type : extra-ddl -->
<xsd:element name="ebean">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="entity" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="entity">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="raw-sql" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="class" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="raw-sql">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="alias-mapping" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="column-mapping" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="query" minOccurs="1" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="alias-mapping">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" use="required"/>
<xsd:attribute name="property" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="column-mapping">
<xsd:complexType>
<xsd:attribute name="column" type="xsd:string" use="required"/>
<xsd:attribute name="property" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="query">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<!--<xsd:attribute name="class" type="xsd:string" use="required"/>-->
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
@@ -13,6 +13,16 @@ import static org.junit.Assert.assertNull;
public class TestRawSqlBuilder extends BaseTestCase {
@Test
public void testDeriveProperty() {
assertThat(RawSql.ColumnMapping.Column.derivePropertyName("item_total", "some_other")).isEqualTo("itemTotal");
assertThat(RawSql.ColumnMapping.Column.derivePropertyName(null, "some_other")).isEqualTo("someOther");
assertThat(RawSql.ColumnMapping.Column.derivePropertyName(null, "alias.some_other")).isEqualTo("someOther");
assertThat(RawSql.ColumnMapping.Column.derivePropertyName(null, "alias.someOther")).isEqualTo("someOther");
assertThat(RawSql.ColumnMapping.Column.derivePropertyName(null, "some")).isEqualTo("some");
assertThat(RawSql.ColumnMapping.Column.derivePropertyName(null, "someOther")).isEqualTo("someOther");
}
@Test
public void testSimple() {
@@ -192,9 +202,9 @@ public class TestRawSqlBuilder extends BaseTestCase {
assertEquals(0, columnMapping.getIndexPosition("id"));
assertEquals(1, columnMapping.getIndexPosition("status"));
assertEquals(2, columnMapping.getIndexPosition("budget"));
assertEquals(3, columnMapping.getIndexPosition("transaction_sum"));
assertEquals(3, columnMapping.getIndexPosition("transactionSum"));
assertEquals(4, columnMapping.getIndexPosition("balance"));
assertEquals(5, columnMapping.getIndexPosition("data_month"));
assertEquals(5, columnMapping.getIndexPosition("dataMonth"));
}
@Test
@@ -16,8 +16,6 @@ public class ServerConfigTest {
serverConfig.loadFromProperties();
assertEquals(PersistBatch.NONE, serverConfig.getPersistBatch());
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade());
assertNotNull(serverConfig.getProperties());
}
@@ -1,5 +1,6 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import org.junit.Test;
@@ -20,4 +21,27 @@ public class MySqlPlatformTest {
assertThat(ddl.convert("bit", false)).isEqualTo("tinyint(1) default 0");
}
@Test
public void uuid_default() {
MySqlPlatform platform = new MySqlPlatform();
platform.configure(new ServerConfig());
DbType dbType = platform.getDbTypeMap().get(DbType.UUID);
assertThat(dbType.renderType(0, 0)).isEqualTo("varchar(40)");
}
@Test
public void uuid_as_binary() {
MySqlPlatform platform = new MySqlPlatform();
ServerConfig serverConfig = new ServerConfig();
serverConfig.setDbUuid(ServerConfig.DbUuid.AUTO_BINARY);
platform.configure(serverConfig);
DbType dbType = platform.getDbTypeMap().get(DbType.UUID);
assertThat(dbType.renderType(0, 0)).isEqualTo("binary(16)");
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import org.junit.Test;
@@ -26,7 +27,30 @@ public class OraclePlatformTest {
assertThat(ddl.convert("boolean", false)).isEqualTo("number(1) default 0");
assertThat(ddl.convert("bit", false)).isEqualTo("bit");
assertThat(ddl.convert("tinyint", false)).isEqualTo("number(3)");
assertThat(ddl.convert("binary(16)", false)).isEqualTo("raw(16)");
}
@Test
public void uuid_default() {
OraclePlatform platform = new OraclePlatform();
platform.configure(new ServerConfig());
DbType dbType = platform.getDbTypeMap().get(DbType.UUID);
assertThat(dbType.renderType(0, 0)).isEqualTo("varchar2(40)");
}
@Test
public void uuid_as_binary() {
OraclePlatform platform = new OraclePlatform();
ServerConfig serverConfig = new ServerConfig();
serverConfig.setDbUuid(ServerConfig.DbUuid.AUTO_BINARY);
platform.configure(serverConfig);
DbType dbType = platform.getDbTypeMap().get(DbType.UUID);
assertThat(dbType.renderType(0, 0)).isEqualTo("raw(16)");
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import org.junit.Test;
@@ -7,11 +8,12 @@ import static org.assertj.core.api.Assertions.assertThat;
public class PostgresPlatformTest {
PostgresPlatform platform = new PostgresPlatform();
@Test
public void testTypeConversion() {
PostgresPlatform platform = new PostgresPlatform();
PlatformDdl ddl = platform.getPlatformDdl();
assertThat(ddl.convert("clob", false)).isEqualTo("text");
@@ -30,4 +32,16 @@ public class PostgresPlatformTest {
}
@Test
public void testUuidType() {
PostgresPlatform platform = new PostgresPlatform();
platform.configure(new ServerConfig());
DbType dbType = platform.getDbTypeMap().get(DbType.UUID);
String columnDefn = dbType.renderType(0, 0);
assertThat(columnDefn).isEqualTo("uuid");
}
}
@@ -62,7 +62,7 @@ public class BaseDdlHandlerTest extends BaseTestCase {
String createTableDDL = Helper.asText(this, "/assert/create-table.txt");
assertThat(write.apply().getBuffer()).isEqualTo(createTableDDL);
assertThat(write.dropAll().getBuffer().trim()).isEqualTo("drop table if exists foo;\ndrop sequence if exists foo_seq;");
assertThat(write.dropAll().getBuffer().trim()).isEqualTo("drop table if exists foo;");
}
@Test
@@ -3,6 +3,7 @@ package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.H2Platform;
import com.avaje.ebean.config.dbplatform.OraclePlatform;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
import com.avaje.ebean.dbmigration.ddlgeneration.Helper;
import com.avaje.ebean.dbmigration.migration.AddTableComment;
@@ -40,6 +41,23 @@ public class BaseTableDdlTest {
assertThat(ddl).contains("alter table mytab add constraint ck_mytab_acol check (acol in ('A','B'))");
}
@Test
public void testAddColumn_withTypeConversion() throws IOException {
BaseTableDdl ddlGen = new BaseTableDdl(serverConfig, new OraclePlatform().getPlatformDdl());
DdlWrite write = new DdlWrite();
Column column = new Column();
column.setName("col_name");
column.setType("varchar(20)");
ddlGen.alterTableAddColumn(write.apply(), "mytable", column, false);
String ddl = write.apply().getBuffer();
assertThat(ddl).contains("alter table mytable add column col_name varchar2(20)");
}
@Test
public void testAlterColumnComment() throws IOException {
@@ -25,7 +25,7 @@ public class ModelBuildBeanVisitorTest extends BaseTestCase {
DbConstraintNaming constraintNaming = defaultServer.getServerConfig().getConstraintNaming();
DefaultConstraintMaxLength maxLength = new DefaultConstraintMaxLength(60);
ModelBuildContext ctx = new ModelBuildContext(model, constraintNaming, maxLength);
ModelBuildContext ctx = new ModelBuildContext(model, constraintNaming, maxLength, true);
ModelBuildBeanVisitor addTable = new ModelBuildBeanVisitor(ctx);
@@ -0,0 +1,31 @@
package com.avaje.ebean.util;
import org.junit.Test;
import static org.junit.Assert.*;
public class CamelCaseHelperTest {
@Test
public void when_underscore() throws Exception {
assertEquals(CamelCaseHelper.toCamelFromUnderscore("hello_there"), "helloThere");
assertEquals(CamelCaseHelper.toCamelFromUnderscore("hello_there_jim"), "helloThereJim");
}
@Test
public void when_trailing_numbers() throws Exception {
assertEquals(CamelCaseHelper.toCamelFromUnderscore("hello_1"), "hello1");
assertEquals(CamelCaseHelper.toCamelFromUnderscore("hello_there_2"), "helloThere2");
}
@Test
public void when_already_camel() throws Exception {
assertEquals(CamelCaseHelper.toCamelFromUnderscore("helloThere"), "helloThere");
assertEquals(CamelCaseHelper.toCamelFromUnderscore("helloThereJim"), "helloThereJim");
assertEquals(CamelCaseHelper.toCamelFromUnderscore("hello"), "hello");
assertEquals(CamelCaseHelper.toCamelFromUnderscore("HELLO"), "HELLO");
}
}
@@ -285,6 +285,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
return null;
}
@Override
public <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
return null;
}
@Override
public <T> Query<T> createQuery(Class<T> beanType) {
return null;
@@ -18,7 +18,7 @@ public class BeanDescriptorTest extends BaseTestCase {
@Test
public void createReference() {
Customer bean = customerDesc.createReference(null, 42, null);
Customer bean = customerDesc.createReference(null, false, 42, null);
assertThat(bean.getId()).isEqualTo(42);
assertThat(server().getBeanState(bean).isReadOnly()).isFalse();
}
@@ -26,17 +26,24 @@ public class BeanDescriptorTest extends BaseTestCase {
@Test
public void createReference_whenReadOnly() {
Customer bean = customerDesc.createReference(Boolean.TRUE, 42, null);
Customer bean = customerDesc.createReference(Boolean.TRUE, false, 42, null);
assertThat(server().getBeanState(bean).isReadOnly()).isTrue();
}
@Test
public void createReference_whenNotReadOnly() {
Customer bean = customerDesc.createReference(Boolean.FALSE, 42, null);
Customer bean = customerDesc.createReference(Boolean.FALSE, false, 42, null);
assertThat(server().getBeanState(bean).isReadOnly()).isFalse();
}
@Test
public void createReference_when_disabledLazyLoad() {
Customer bean = customerDesc.createReference(Boolean.FALSE, true, 42, null);
assertThat(server().getBeanState(bean).isDisableLazyLoad()).isTrue();
}
@Test
public void allProperties() {
@@ -0,0 +1,21 @@
package com.avaje.ebeaninternal.xmlmapping.model;
import com.avaje.ebeaninternal.xmlmapping.XmlMappingReader;
import org.junit.Test;
import java.io.InputStream;
import static org.junit.Assert.assertNotNull;
public class XmlMappingReaderTest {
@Test
public void read() throws Exception {
InputStream is = XmlMappingReaderTest.class.getResourceAsStream("/test-ebean.xml");
XmEbean testMapping = XmlMappingReader.read(is);
assertNotNull(testMapping);
}
}
@@ -14,6 +14,7 @@ import com.avaje.tests.model.basic.OrderAggregate;
import com.avaje.tests.model.basic.ResetBasicData;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull;
public class TestOrderTotalAmountReportBean extends BaseTestCase {
@@ -50,7 +51,7 @@ public class TestOrderTotalAmountReportBean extends BaseTestCase {
}
@Test
public void test_when_explicitMapping() {
public void test_when_aliasInUnderscore() {
ResetBasicData.reset();
@@ -61,8 +62,6 @@ public class TestOrderTotalAmountReportBean extends BaseTestCase {
RawSql rawSql = RawSqlBuilder.parse(sql)
.columnMapping("order_id", "order.id")
.columnMapping("total_items", "totalItems")
.columnMapping("total_amount", "totalAmount")
.create();
Query<OrderAggregate> query = Ebean.find(OrderAggregate.class)
@@ -73,5 +72,83 @@ public class TestOrderTotalAmountReportBean extends BaseTestCase {
assertThat(query.getGeneratedSql()).contains("count(*) as total_items, sum(order_qty*unit_price) as total_amount");
}
@Test
public void test_when_aliasInCamelCase() {
ResetBasicData.reset();
String sql =
"select order_id, count(*) as totalItems, sum(order_qty*unit_price) as totalAmount \n" +
"from o_order_detail \n" +
"group by order_id";
RawSql rawSql = RawSqlBuilder.parse(sql)
.columnMapping("order_id", "order.id")
.create();
Query<OrderAggregate> query = Ebean.find(OrderAggregate.class)
.setRawSql(rawSql);
query.findList();
assertThat(query.getGeneratedSql()).contains("count(*) as totalItems, sum(order_qty*unit_price) as totalAmount");
}
@Test
public void testDefaultNamedRawSql() {
Query<OrderAggregate> query = Ebean.find(OrderAggregate.class);
List<OrderAggregate> list = query.findList();
assertThat(query.getGeneratedSql()).contains("count(*) as total_items, sum(order_qty*unit_price) as total_amount");
assertNotNull(list);
}
@Test
public void testNamedRawSql() {
ResetBasicData.reset();
Query<OrderAggregate> query = Ebean.getDefaultServer().createNamedQuery(OrderAggregate.class, "withMax");
List<OrderAggregate> list = query.findList();
assertThat(query.getGeneratedSql()).contains("count(*) as total_items, sum(order_qty*unit_price) as total_amount, max(order_qty*unit_price) as maxAmount from o_order_detail");
assertNotNull(list);
}
@Test
public void testNamedRawSql_with_extraPredicates() {
ResetBasicData.reset();
Query<OrderAggregate> query = Ebean.getDefaultServer().createNamedQuery(OrderAggregate.class, "withMax");
List<OrderAggregate> list = query
.where().gt("order.id", 1)
.having().gt("totalItems", 1)
.order().desc("totalAmount")
.findList();
assertThat(query.getGeneratedSql()).contains("count(*) as total_items, sum(order_qty*unit_price) as total_amount, max(order_qty*unit_price) as maxAmount from o_order_detail");
assertThat(query.getGeneratedSql()).contains("from o_order_detail where order_id > ? group by order_id having count(*) > ? order by sum(order_qty*unit_price) desc");
assertNotNull(list);
}
@Test
public void testNamedRawSql_with_param() {
ResetBasicData.reset();
Query<OrderAggregate> query = Ebean.getDefaultServer().createNamedQuery(OrderAggregate.class, "withParam");
List<OrderAggregate> list = query
.setParameter("minId", 2)
.where().isNotNull("order.id")
.having().lt("totalAmount", 100)
.order().desc("totalAmount")
.setMaxRows(10)
.findList();
assertThat(query.getGeneratedSql()).contains("count(*) as totalItems, sum(order_qty*unit_price) as totalAmount, max(order_qty*unit_price) as maxAmount from o_order_detail");
assertThat(query.getGeneratedSql()).contains("from o_order_detail where id > ? and order_id is not null group by order_id having sum(order_qty*unit_price) < ? order by sum(order_qty*unit_price) desc");
assertNotNull(list);
}
}
@@ -0,0 +1,90 @@
package com.avaje.tests.batchload;
import com.avaje.ebean.Ebean;
import com.avaje.tests.model.basic.Order;
import com.avaje.tests.model.basic.OrderDetail;
import com.avaje.tests.model.basic.ResetBasicData;
import org.avaje.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class TestQueryDisableLazyLoad {
@Test
public void onAssocMany() {
ResetBasicData.reset();
LoggedSqlCollector.start();
List<Order> l0 = Ebean.find(Order.class)
.setDisableLazyLoading(true)
.order().asc("id")
.findList();
assertThat(l0).isNotEmpty();
Order order = l0.get(0);
List<OrderDetail> details = order.getDetails();
assertEquals(details.size(), 0);
List<String> loggedSql = LoggedSqlCollector.stop();
assertThat(loggedSql).hasSize(1);
assertThat(loggedSql.get(0)).contains("select t0.id c0, t0.status c1, t0.order_date c2,");
assertThat(loggedSql.get(0)).contains(" from o_order t0 ");
}
@Test
public void onAssocOne() {
ResetBasicData.reset();
LoggedSqlCollector.start();
List<Order> l0 = Ebean.find(Order.class)
.setDisableLazyLoading(true)
.order().asc("id")
.findList();
assertThat(l0).isNotEmpty();
Order order = l0.get(0);
// normally invokes lazy loading
assertNull(order.getCustomer().getStatus());
List<String> loggedSql = LoggedSqlCollector.stop();
assertThat(loggedSql).hasSize(1);
}
@Test
public void onAssocOne_when_partial() {
ResetBasicData.reset();
LoggedSqlCollector.start();
List<Order> l0 = Ebean.find(Order.class)
.setDisableLazyLoading(true)
.fetch("customer","smallnote")
.order().asc("id")
.findList();
assertThat(l0).isNotEmpty();
Order order = l0.get(0);
// normally invokes lazy loading
assertNull(order.getCustomer().getStatus());
List<String> loggedSql = LoggedSqlCollector.stop();
assertThat(loggedSql).hasSize(1);
}
}
@@ -94,6 +94,9 @@ public class TestQueryJoinToAssocOne extends BaseTestCase {
// normally invokes lazy loading
orderDetail.getShipQty();
// normally invokes lazy loading
order.getShipments().size();
List<String> loggedSql = LoggedSqlCollector.stop();
assertThat(loggedSql).hasSize(2);
@@ -19,9 +19,11 @@ public class OrderAggregate {
@OneToOne
Order order;
Double maxAmount;
Double totalAmount;
Double totalItems;
Long totalItems;
public String toString() {
return order.getId() + " totalAmount:" + totalAmount + " totalItems:" + totalItems;
@@ -43,11 +45,19 @@ public class OrderAggregate {
this.totalAmount = totalAmount;
}
public Double getTotalItems() {
public Long getTotalItems() {
return totalItems;
}
public void setTotalItems(Double totalItems) {
public void setTotalItems(Long totalItems) {
this.totalItems = totalItems;
}
public Double getMaxAmount() {
return maxAmount;
}
public void setMaxAmount(Double maxAmount) {
this.maxAmount = maxAmount;
}
}
@@ -1,5 +1,6 @@
package com.avaje.tests.model.view;
import com.avaje.ebean.annotation.Cache;
import com.avaje.ebean.annotation.View;
import com.avaje.tests.model.basic.Order;
@@ -9,6 +10,7 @@ import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
@Cache(enableQueryCache = true)
@Entity
@View(name = "order_agg_vw", dependentTables = {"o_order", "o_order_detail"})
public class EOrderAgg {
@@ -22,6 +22,25 @@ import static org.assertj.core.api.Assertions.assertThat;
public class TestRawSqlOrmQuery extends BaseTestCase {
@Test
public void testNamed() {
ResetBasicData.reset();
Query<Order> query = Ebean.createNamedQuery(Order.class, "myRawTest");
query.setParameter("orderStatus", Order.Status.NEW);
query.setMaxRows(10);
List<Order> list = query.findList();
for (Order order : list) {
order.getCretime();
}
String sql = query.getGeneratedSql();
assertThat(sql).contains("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");
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() {
-57
View File
@@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings>
<entity class="com.avaje.tests.model.basic.OrderAggregate">
<raw-sql name="default">
<columnMapping column="order_id" property="order.id" />
<query>
select order_id, count(*) as totalItems, sum(order_qty*unit_price) as totalAmount
from o_order_detail
group by order_id
</query>
</raw-sql>
<raw-sql name="total.amount">
<columnMapping column="order_id" property="order.id" />
<query>
select order_id, sum(order_qty*unit_price) as totalAmount
from o_order_detail
group by order_id
</query>
</raw-sql>
<sql-select name="total.qty">
<query>
select order_id, count(*) as total_items, sum(order_qty*unit_price) as total_amount
from o_order_detail
group by order_id
</query>
</sql-select>
<sql-select name="total">
<query>
select order_id, sum(order_qty*unit_price) as total_amount
from o_order_detail
group by order_id
</query>
</sql-select>
</entity>
<entity class="com.avaje.tests.model.basic.TMapSuperEntity">
<raw-sql name="testTransient">
<columnMapping column="id" property="id" />
<columnMapping column="name" property="name" />
<query>
select id, name, 12 as myint from t_mapsuper1
</query>
</raw-sql>
</entity>
<entity class="com.avaje.tests.model.basic.MyAdHoc">
<raw-sql name="default">
<columnMapping column="order_id" property="order.id" />
<query>
select order_id, count(*) as detailCount from o_order_detail group by order_id
</query>
</raw-sql>
</entity>
</entity-mappings>
@@ -1,11 +1,10 @@
create table foo (
col1 varchar(4) not null,
col1 varchar(4) auto_increment not null,
col2 varchar(30) not null,
col3 varchar(30) not null,
constraint pk_foo primary key (col1)
);
comment on table foo is 'comment';
create sequence foo_seq;
alter table foo add column added_to_foo varchar(20);
@@ -1,3 +1,2 @@
drop table if exists foo;
drop sequence if exists foo_seq;
@@ -1,9 +1,8 @@
create table mytable (
id integer not null,
id integer auto_increment not null,
status varchar(1) not null,
order_id integer not null,
constraint ck_mytable_status check (status in ('A','B')),
constraint pk_mytable primary key (id)
);
create sequence mytable_seq;
@@ -1,3 +1,2 @@
drop table if exists mytable;
drop sequence if exists mytable_seq;
@@ -1,18 +1,16 @@
create table ckey_assoc (
id integer not null,
id integer auto_increment not null,
assoc_one varchar(255),
constraint pk_ckey_assoc primary key (id)
);
create sequence ckey_assoc_seq;
create table ckey_detail (
id integer not null,
id integer auto_increment not null,
something varchar(255),
one_key integer,
two_key varchar(255),
constraint pk_ckey_detail primary key (id)
);
create sequence ckey_detail_seq;
create table ckey_parent (
one_key integer not null,
@@ -1,19 +1,17 @@
create table persons (
id bigint not null,
id bigint auto_increment not null,
surname varchar(64) not null,
name varchar(64) not null,
constraint pk_persons primary key (id)
);
create sequence PERSONS_seq start with 1000 increment by 40;
create table phones (
id bigint not null,
id bigint auto_increment not null,
phone_number varchar(7) not null,
person_id bigint not null,
constraint uq_phones_phone_number unique (phone_number),
constraint pk_phones primary key (id)
);
create sequence PHONES_seq;
alter table phones add constraint fk_phones_person_id foreign key (person_id) references persons (id) on delete restrict on update restrict;
create index ix_phones_person_id on phones (person_id);

Some files were not shown because too many files have changed in this diff Show More