mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
48
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
905a0ce9ec | ||
|
|
bfa8837e61 | ||
|
|
8d5845b9a2 | ||
|
|
87a7d4958e | ||
|
|
511ebd8a29 | ||
|
|
11f4c8d7a3 | ||
|
|
d417a92843 | ||
|
|
4bd5e52df3 | ||
|
|
788a200d16 | ||
|
|
e18d2004dc | ||
|
|
b326724139 | ||
|
|
fc0b47342b | ||
|
|
ec1a68a133 | ||
|
|
8ca12521d7 | ||
|
|
2362ef4130 | ||
|
|
7e233edcc3 | ||
|
|
a8b98282a1 | ||
|
|
b4506aa718 | ||
|
|
848a3b8888 | ||
|
|
67c711d75f | ||
|
|
3d62f58930 | ||
|
|
a30948d38d | ||
|
|
a8232d2d8b | ||
|
|
d4b8f23a5a | ||
|
|
bcf2a44187 | ||
|
|
f3102ba02c | ||
|
|
0351eab3a7 | ||
|
|
409e2b75ce | ||
|
|
dc968f16bf | ||
|
|
bbbb6482fb | ||
|
|
001968fcf8 | ||
|
|
6abcb186a4 | ||
|
|
f250447fbe | ||
|
|
bfa86f241b | ||
|
|
19459b71b4 | ||
|
|
5e97c3bbbc | ||
|
|
986eadd023 | ||
|
|
c6787d0901 | ||
|
|
3792e06859 | ||
|
|
c77446ba24 | ||
|
|
dba245c517 | ||
|
|
489eb483c0 | ||
|
|
11301846be | ||
|
|
d022e4aec8 | ||
|
|
aa788edb68 | ||
|
|
288adcdf68 | ||
|
|
ddb07ca202 | ||
|
|
524b592e82 |
@@ -5,16 +5,7 @@
|
||||
|
||||
Ebean ORM
|
||||
==============
|
||||
[http://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Website")
|
||||
|
||||
|
||||
Maven Dependency
|
||||
----------------
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>4.7.3</version>
|
||||
</dependency>
|
||||
[https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Website")
|
||||
|
||||
|
||||
[Maven central - avaje-ebeanorm](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.avaje.ebeanorm%22%20AND%20a%3A%22avaje-ebeanorm%22 "maven central ebeanorm")
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>6.8.1</version>
|
||||
<version>6.10.4</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -61,6 +61,14 @@
|
||||
<version>1.7.7</version>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope for Spring boot loader classpath scanning support -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-loader</artifactId>
|
||||
<version>1.2.7.RELEASE</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope for JsonNode support -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
@@ -220,7 +228,7 @@
|
||||
<configuration>
|
||||
<classSource>target/test-classes</classSource>
|
||||
<packages>com.avaje.tests.**</packages>
|
||||
<transformArgs>debug=2</transformArgs>
|
||||
<transformArgs>debug=1</transformArgs>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>enhance</goal>
|
||||
@@ -265,11 +273,11 @@
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<configuration>
|
||||
<doctitle>Ebean 4</doctitle>
|
||||
<doctitle>Ebean 6</doctitle>
|
||||
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
|
||||
<source>1.8</source>
|
||||
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
|
||||
<excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
|
||||
<excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util:com.avaje.ebean.dbmigration.ddlgeneration:com.avaje.ebean.dbmigration.migration:com.avaje.ebean.dbmigration.migrationreader:com.avaje.ebean.dbmigration.model</excludePackageNames>
|
||||
<docletArtifact>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>pygments-doclet</artifactId>
|
||||
|
||||
@@ -38,6 +38,33 @@ public interface BeanState {
|
||||
*/
|
||||
void setDisableLazyLoad(boolean disableLazyLoading);
|
||||
|
||||
/**
|
||||
* Set the loaded state of the property given it's name.
|
||||
*
|
||||
* <p>
|
||||
* Typically this would be used to set the loaded state of a property
|
||||
* to false to ensure that the specific property is excluded from a
|
||||
* stateless update.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // populate a bean via say JSON
|
||||
* User user = ...;
|
||||
*
|
||||
* // set loaded state on the email property to false so that
|
||||
* // the email property is not included in a stateless update
|
||||
* Ebean.getBeanState(user).setPropertyLoaded("email", false);
|
||||
*
|
||||
* user.update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* This will throw an IllegalArgumentException if the property is unknown.
|
||||
*/
|
||||
void setPropertyLoaded(String propertyName, boolean loaded);
|
||||
|
||||
/**
|
||||
* For partially populated beans returns the properties that are loaded on the
|
||||
* bean.
|
||||
|
||||
@@ -76,6 +76,11 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression jsonLessOrEqualTo(String propertyName, String path, Object val);
|
||||
|
||||
/**
|
||||
* Between - for the given path in a JSON document.
|
||||
*/
|
||||
Expression jsonBetween(String propertyName, String path, Object lowerValue, Object upperValue);
|
||||
|
||||
/**
|
||||
* Equal To - property equal to the given value.
|
||||
*/
|
||||
|
||||
@@ -412,7 +412,7 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> jsonGreaterThan(String propertyName, String path, Object val);
|
||||
ExpressionList<T> jsonGreaterThan(String propertyName, String path, Object value);
|
||||
|
||||
/**
|
||||
* Greater than or equal to - for the given path in a JSON document.
|
||||
@@ -423,7 +423,7 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> jsonGreaterOrEqual(String propertyName, String path, Object val);
|
||||
ExpressionList<T> jsonGreaterOrEqual(String propertyName, String path, Object value);
|
||||
|
||||
/**
|
||||
* Less than - for the given path in a JSON document.
|
||||
@@ -434,7 +434,7 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> jsonLessThan(String propertyName, String path, Object val);
|
||||
ExpressionList<T> jsonLessThan(String propertyName, String path, Object value);
|
||||
|
||||
/**
|
||||
* Less than or equal to - for the given path in a JSON document.
|
||||
@@ -445,7 +445,18 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> jsonLessOrEqualTo(String propertyName, String path, Object val);
|
||||
ExpressionList<T> jsonLessOrEqualTo(String propertyName, String path, Object value);
|
||||
|
||||
/**
|
||||
* Between - for the given path in a JSON document.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonBetween("content", "orderDate", lowerDateTime, upperDateTime)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> jsonBetween(String propertyName, String path, Object lowerValue, Object upperValue);
|
||||
|
||||
/**
|
||||
* Add an Expression to the list.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -193,7 +194,31 @@ public abstract class Model {
|
||||
public void markAsDirty() {
|
||||
db().markAsDirty(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mark the property as unset or 'not loaded'.
|
||||
* <p>
|
||||
* This would be used to specify a property that we did not wish to include in a stateless update.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // populate an entity bean from JSON or whatever
|
||||
* User user = ...;
|
||||
*
|
||||
* // mark the email property as 'unset' so that it is not
|
||||
* // included in a 'stateless update'
|
||||
* user.markPropertyUnset("email");
|
||||
*
|
||||
* user.update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyName the name of the property on the bean to be marked as 'unset'
|
||||
*/
|
||||
public void markPropertyUnset(String propertyName) {
|
||||
((EntityBean)this)._ebean_getIntercept().setPropertyLoaded(propertyName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update this entity depending on its state.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
/**
|
||||
* Specify the DB storage type used to with <code>@DbEnumValue</code>.
|
||||
*/
|
||||
public enum DbEnumType {
|
||||
|
||||
/**
|
||||
* Store values as database INTEGER.
|
||||
*/
|
||||
INTEGER,
|
||||
|
||||
/**
|
||||
* Store values as database VARCHAR.
|
||||
*/
|
||||
VARCHAR
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specify a method on an Enum that returns the value that should be stored in the DB.
|
||||
* <p>
|
||||
* This is the preferred option for mapping Enum's to DB values (preferred over the JPA
|
||||
* standard @Enumerated and Ebean's @EnumValue annotations).
|
||||
* </p>
|
||||
* <h3>Example:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* public enum Status {
|
||||
* NEW("N"),
|
||||
* ACTIVE("A"),
|
||||
* INACTIVE("I");
|
||||
*
|
||||
* String dbValue;
|
||||
* Status(String dbValue) {
|
||||
* this.dbValue = dbValue;
|
||||
* }
|
||||
*
|
||||
* @DbEnumValue
|
||||
* public String getValue() {
|
||||
* return dbValue;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface DbEnumValue {
|
||||
|
||||
/**
|
||||
* Specify the database type used to store the values (VARCHAR or INTEGER).
|
||||
*/
|
||||
DbEnumType storage() default DbEnumType.VARCHAR;
|
||||
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import java.lang.annotation.Target;
|
||||
* }</pre>
|
||||
* <p>
|
||||
* This is an alternative to using the JPA standard approach or Ebean's
|
||||
* {@link EnumMapping} annotation.
|
||||
* {@link DbEnumValue} annotation.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that if all the EnumValue values are parsable as Integers then Ebean
|
||||
|
||||
@@ -449,7 +449,18 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public int getPropertyLength() {
|
||||
return owner._ebean_getPropertyNames().length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the loaded state of the property given it's name.
|
||||
*/
|
||||
public void setPropertyLoaded(String propertyName, boolean loaded) {
|
||||
int position = findProperty(propertyName);
|
||||
if (position == -1) {
|
||||
throw new IllegalArgumentException("Property "+propertyName+" not found");
|
||||
}
|
||||
loadedProps[position] = loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property to be treated as unloaded. Used for properties initialised in default
|
||||
* constructor.
|
||||
|
||||
@@ -45,13 +45,29 @@ public class DbConstraintNaming {
|
||||
protected String ckPrefix = "ck_";
|
||||
protected String ckSuffix = "";
|
||||
|
||||
protected boolean lowerCaseNames = true;
|
||||
|
||||
protected MaxLength maxLength;
|
||||
|
||||
protected DbConstraintNormalise normalise = new DbConstraintNormalise();
|
||||
protected DbConstraintNormalise normalise;
|
||||
|
||||
/**
|
||||
* Construct using default of lower case for both table and column names.
|
||||
*/
|
||||
public DbConstraintNaming() {
|
||||
this(true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct specifying if lower case should be used (for both table and column names).
|
||||
*/
|
||||
public DbConstraintNaming(boolean lowerCase) {
|
||||
this(lowerCase, lowerCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct specifying if lower case should be used for both table and column names.
|
||||
*/
|
||||
public DbConstraintNaming(boolean lowerCaseTableNames, boolean lowerCaseColumnNames) {
|
||||
this.normalise = new DbConstraintNormalise(lowerCaseTableNames, lowerCaseColumnNames);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,7 +183,6 @@ public class DbConstraintNaming {
|
||||
* quoted identifier characters (",',[,] etc).
|
||||
*/
|
||||
public String normaliseTable(String tableName) {
|
||||
|
||||
return normalise.normaliseTable(tableName);
|
||||
}
|
||||
|
||||
@@ -175,18 +190,20 @@ public class DbConstraintNaming {
|
||||
* Normalise the column name by removing any quoted identifier characters (",',[,] etc).
|
||||
*/
|
||||
public String normaliseColumn(String tableName) {
|
||||
|
||||
return normalise.normaliseColumn(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower case the table or column name checking for quoted identifiers.
|
||||
* Lower case the table name checking for quoted identifiers.
|
||||
*/
|
||||
public String lowerName(String tableName) {
|
||||
if (lowerCaseNames && normalise.notQuoted(tableName)) {
|
||||
return tableName.toLowerCase();
|
||||
}
|
||||
return tableName;
|
||||
public String lowerTableName(String tableName) {
|
||||
return normalise.lowerTableName(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower case the column name checking for quoted identifiers.
|
||||
*/
|
||||
public String lowerColumnName(String name) {
|
||||
return normalise.lowerColumnName(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,17 @@ public class DbConstraintNormalise {
|
||||
|
||||
protected final String[] quotedIdentifiers;
|
||||
|
||||
protected boolean lowerCaseTables = true;
|
||||
protected final boolean lowerCaseTables;
|
||||
|
||||
protected boolean lowerCaseColumns = true;
|
||||
protected final boolean lowerCaseColumns;
|
||||
|
||||
public DbConstraintNormalise() {
|
||||
this(true, true);
|
||||
}
|
||||
|
||||
public DbConstraintNormalise(boolean lowerCaseTables, boolean lowerCaseColumns) {
|
||||
this.lowerCaseTables = lowerCaseTables;
|
||||
this.lowerCaseColumns = lowerCaseColumns;
|
||||
this.quotedIdentifiers = new String[]{"\"", "'", "[", "]", "`"};
|
||||
}
|
||||
|
||||
@@ -46,6 +52,26 @@ public class DbConstraintNormalise {
|
||||
return columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower case the table name checking for quoted identifiers.
|
||||
*/
|
||||
public String lowerTableName(String tableName) {
|
||||
if (lowerCaseTables && notQuoted(tableName)) {
|
||||
return tableName.toLowerCase();
|
||||
}
|
||||
return tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower case the column name checking for quoted identifiers.
|
||||
*/
|
||||
public String lowerColumnName(String name) {
|
||||
if (lowerCaseColumns && notQuoted(name)) {
|
||||
return name.toLowerCase();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim off the platform quoted identifier quotes like [ ' and ".
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,14 @@ public final class PropertyMap implements Serializable {
|
||||
|
||||
private final LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Return properties loaded from test-ebean.properties.
|
||||
*/
|
||||
public static Properties testProperties() {
|
||||
PropertyMap propertyMap = PropertyMapLoader.loadTestProperties();
|
||||
return (propertyMap == null) ? new Properties() : propertyMap.asProperties();
|
||||
}
|
||||
|
||||
public static Properties defaultProperties() {
|
||||
PropertyMap propertyMap = PropertyMapLoader.loadGlobalProperties();
|
||||
return (propertyMap == null) ? new Properties() : propertyMap.asProperties();
|
||||
|
||||
@@ -14,17 +14,34 @@ final class PropertyMapLoader {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PropertyMapLoader.class);
|
||||
|
||||
/**
|
||||
* Load the <code>test-ebean.properties</code>.
|
||||
*/
|
||||
public static PropertyMap loadTestProperties() {
|
||||
return load(null, "test-ebean.properties", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the ebean.properties (and test-ebean.properties if present).
|
||||
*/
|
||||
public static PropertyMap loadGlobalProperties() {
|
||||
|
||||
boolean loadTestProperties = false;
|
||||
String fileName = System.getenv("EBEAN_PROPS_FILE");
|
||||
if (fileName == null) {
|
||||
fileName = System.getProperty("ebean.props.file");
|
||||
if (fileName == null) {
|
||||
loadTestProperties = true;
|
||||
fileName = "ebean.properties";
|
||||
}
|
||||
}
|
||||
|
||||
return load(null, fileName);
|
||||
PropertyMap map = load(null, fileName, true);
|
||||
if (loadTestProperties) {
|
||||
// load test properties if present in classpath
|
||||
load(map, "test-ebean.properties", false);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,11 +52,13 @@ final class PropertyMapLoader {
|
||||
* @param fileName
|
||||
* the name of the properties file to load.
|
||||
*/
|
||||
public static PropertyMap load(PropertyMap p, String fileName) {
|
||||
public static PropertyMap load(PropertyMap p, String fileName, boolean errorOnNull) {
|
||||
|
||||
InputStream is = findInputStream(fileName);
|
||||
if (is == null) {
|
||||
logger.error(fileName + " not found");
|
||||
if (errorOnNull) {
|
||||
logger.error(fileName + " not found");
|
||||
}
|
||||
return p;
|
||||
} else {
|
||||
return load(p, is);
|
||||
|
||||
@@ -281,9 +281,9 @@ public class ServerConfig {
|
||||
private boolean updatesDeleteMissingChildren = true;
|
||||
|
||||
/**
|
||||
* Setting to indicate if UUID should be stored as binary(16) or varchar(40).
|
||||
* Setting to indicate if UUID should be stored as binary(16) or varchar(40) or native DB type (for H2 and Postgres).
|
||||
*/
|
||||
private boolean uuidStoreAsBinary;
|
||||
private DbUuid dbUuid = DbUuid.AUTO;
|
||||
|
||||
|
||||
private List<BeanFindController> findControllers = new ArrayList<BeanFindController>();
|
||||
@@ -1466,19 +1466,18 @@ public class ServerConfig {
|
||||
this.dbEncrypt = dbEncrypt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if UUID should be stored as binary(16) (as opposed to varchar(40)).
|
||||
* Return the DB type used to store UUID.
|
||||
*/
|
||||
public boolean isUuidStoreAsBinary() {
|
||||
return uuidStoreAsBinary;
|
||||
public DbUuid getDbUuid() {
|
||||
return dbUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if UUID should be stored as binary(16) (as opposed to varchar(40)).
|
||||
* Set the DB type used to store UUID.
|
||||
*/
|
||||
public void setUuidStoreAsBinary(boolean uuidStoreAsBinary) {
|
||||
this.uuidStoreAsBinary = uuidStoreAsBinary;
|
||||
public void setDbUuid(DbUuid dbUuid) {
|
||||
this.dbUuid = dbUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2041,6 +2040,21 @@ public class ServerConfig {
|
||||
loadSettings(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from test-ebean.properties and do nothing if the properties is not found.
|
||||
* <p>
|
||||
* This is typically used when test-ebean.properties is put into the test class path and used
|
||||
* to configure Ebean for running tests.
|
||||
* </p>
|
||||
*/
|
||||
public void loadTestProperties() {
|
||||
Properties properties = PropertyMap.testProperties();
|
||||
if (!properties.isEmpty()) {
|
||||
PropertiesWrapper p = new PropertiesWrapper("ebean", name, properties);
|
||||
loadSettings(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties that we used for configuration and were set via a call to loadFromProperties().
|
||||
*/
|
||||
@@ -2049,8 +2063,11 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T createInstance(PropertiesWrapper p, Class<T> pluginType, String key) {
|
||||
private <T> T createInstance(PropertiesWrapper p, Class<T> pluginType, String key, T instance) {
|
||||
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
String classname = p.get(key, null);
|
||||
return classname == null ? null : (T) ClassUtil.newInstance(classname);
|
||||
}
|
||||
@@ -2098,15 +2115,15 @@ public class ServerConfig {
|
||||
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
|
||||
|
||||
currentUserProvider = createInstance(p, CurrentUserProvider.class, "currentUserProvider");
|
||||
disableClasspathSearch = p.getBoolean("disableClasspathSearch", disableClasspathSearch);
|
||||
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
|
||||
encryptKeyManager = createInstance(p, EncryptKeyManager.class, "encryptKeyManager");
|
||||
encryptDeployManager = createInstance(p, EncryptDeployManager.class, "encryptDeployManager");
|
||||
encryptor = createInstance(p, Encryptor.class, "encryptor");
|
||||
dbEncrypt = createInstance(p, DbEncrypt.class, "dbEncrypt");
|
||||
serverCacheFactory = createInstance(p, ServerCacheFactory.class, "serverCacheFactory");
|
||||
serverCacheManager = createInstance(p, ServerCacheManager.class, "serverCacheManager");
|
||||
currentUserProvider = createInstance(p, CurrentUserProvider.class, "currentUserProvider", currentUserProvider);
|
||||
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform", databasePlatform);
|
||||
encryptKeyManager = createInstance(p, EncryptKeyManager.class, "encryptKeyManager", encryptKeyManager);
|
||||
encryptDeployManager = createInstance(p, EncryptDeployManager.class, "encryptDeployManager", encryptDeployManager);
|
||||
encryptor = createInstance(p, Encryptor.class, "encryptor", encryptor);
|
||||
dbEncrypt = createInstance(p, DbEncrypt.class, "dbEncrypt", dbEncrypt);
|
||||
serverCacheFactory = createInstance(p, ServerCacheFactory.class, "serverCacheFactory", serverCacheFactory);
|
||||
serverCacheManager = createInstance(p, ServerCacheManager.class, "serverCacheManager", serverCacheManager);
|
||||
cacheWarmingDelay = p.getInt("cacheWarmingDelay", cacheWarmingDelay);
|
||||
classPathReaderClassName = p.get("classpathreader");
|
||||
|
||||
@@ -2152,7 +2169,10 @@ public class ServerConfig {
|
||||
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
|
||||
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
|
||||
databasePlatformName = p.get("databasePlatformName", databasePlatformName);
|
||||
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", uuidStoreAsBinary);
|
||||
dbUuid = p.getEnum(DbUuid.class, "dbuuid", dbUuid);
|
||||
if (p.getBoolean("uuidStoreAsBinary", false)) {
|
||||
dbUuid = DbUuid.BINARY;
|
||||
}
|
||||
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
|
||||
|
||||
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
|
||||
@@ -2174,7 +2194,7 @@ public class ServerConfig {
|
||||
|
||||
private NamingConvention createNamingConvention(PropertiesWrapper properties, NamingConvention namingConvention) {
|
||||
|
||||
NamingConvention nc = createInstance(properties, NamingConvention.class, "namingconvention");
|
||||
NamingConvention nc = createInstance(properties, NamingConvention.class, "namingconvention", null);
|
||||
return (nc != null) ? nc : namingConvention;
|
||||
}
|
||||
|
||||
@@ -2274,4 +2294,25 @@ public class ServerConfig {
|
||||
public void setExpressionEqualsWithNullAsNoop(boolean expressionEqualsWithNullAsNoop) {
|
||||
this.expressionEqualsWithNullAsNoop = expressionEqualsWithNullAsNoop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify how UUID is stored.
|
||||
*/
|
||||
public enum DbUuid {
|
||||
|
||||
/**
|
||||
* Store using native UUID in H2 and Postgres.
|
||||
*/
|
||||
AUTO,
|
||||
|
||||
/**
|
||||
* Store using DB VARCHAR.
|
||||
*/
|
||||
VARCHAR,
|
||||
|
||||
/**
|
||||
* Store using DB BINARY.
|
||||
*/
|
||||
BINARY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.Query;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Database platform specific settings.
|
||||
*/
|
||||
@@ -54,28 +52,33 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected OnQueryOnly onQueryOnly = OnQueryOnly.ROLLBACK;
|
||||
|
||||
/**
|
||||
* The open quote used by quoted identifiers.
|
||||
/**
|
||||
* The open quote used by quoted identifiers.
|
||||
*/
|
||||
protected String openQuote = "\"";
|
||||
|
||||
/**
|
||||
* The close quote used by quoted identifiers.
|
||||
* The close quote used by quoted identifiers.
|
||||
*/
|
||||
protected String closeQuote = "\"";
|
||||
|
||||
/**
|
||||
* For limit/offset, row_number etc limiting of SQL queries.
|
||||
/**
|
||||
* For limit/offset, row_number etc limiting of SQL queries.
|
||||
*/
|
||||
protected SqlLimiter sqlLimiter = new LimitOffsetSqlLimiter();
|
||||
|
||||
/**
|
||||
* Mapping of JDBC to Database types.
|
||||
/**
|
||||
* Mapping of JDBC to Database types.
|
||||
*/
|
||||
protected DbTypeMap dbTypeMap = new DbTypeMap();
|
||||
|
||||
/**
|
||||
* Defines DB identity/sequence features.
|
||||
* Set to true if the DB has native UUID type support.
|
||||
*/
|
||||
protected boolean nativeUuidType;
|
||||
|
||||
/**
|
||||
* Defines DB identity/sequence features.
|
||||
*/
|
||||
protected DbIdentity dbIdentity = new DbIdentity();
|
||||
|
||||
@@ -84,28 +87,28 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected DbHistorySupport historySupport;
|
||||
|
||||
/**
|
||||
* The JDBC type to map booleans to (by default).
|
||||
/**
|
||||
* The JDBC type to map booleans to (by default).
|
||||
*/
|
||||
protected int booleanDbType = Types.BOOLEAN;
|
||||
|
||||
/**
|
||||
* The JDBC type to map Blob to.
|
||||
/**
|
||||
* The JDBC type to map Blob to.
|
||||
*/
|
||||
protected int blobDbType = Types.BLOB;
|
||||
|
||||
/**
|
||||
* The JDBC type to map Clob to.
|
||||
/**
|
||||
* The JDBC type to map Clob to.
|
||||
*/
|
||||
protected int clobDbType = Types.CLOB;
|
||||
|
||||
/**
|
||||
* For Oracle treat empty strings as null.
|
||||
/**
|
||||
* For Oracle treat empty strings as null.
|
||||
*/
|
||||
protected boolean treatEmptyStringsAsNull;
|
||||
|
||||
/**
|
||||
* The database platform name.
|
||||
/**
|
||||
* The database platform name.
|
||||
*/
|
||||
protected String name = "generic";
|
||||
|
||||
@@ -124,7 +127,7 @@ public class DatabasePlatform {
|
||||
* The like clause. Can be overridden to disable default escape character.
|
||||
*/
|
||||
protected String likeClause = "like ?";
|
||||
|
||||
|
||||
protected DbEncrypt dbEncrypt;
|
||||
|
||||
protected boolean idInExpandedForm;
|
||||
@@ -219,16 +222,12 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return a DB Sequence based IdGenerator.
|
||||
*
|
||||
* @param be
|
||||
* the BackgroundExecutor that can be used to load the sequence if
|
||||
* desired
|
||||
* @param ds
|
||||
* the DataSource
|
||||
* @param seqName
|
||||
* the name of the sequence
|
||||
* @param batchSize
|
||||
* the number of sequences that should be loaded
|
||||
*
|
||||
* @param be the BackgroundExecutor that can be used to load the sequence if
|
||||
* desired
|
||||
* @param ds the DataSource
|
||||
* @param seqName the name of the sequence
|
||||
* @param batchSize the number of sequences that should be loaded
|
||||
*/
|
||||
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
|
||||
return null;
|
||||
@@ -276,9 +275,16 @@ public class DatabasePlatform {
|
||||
this.historySupport = historySupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the DB supports native UUID.
|
||||
*/
|
||||
public boolean isNativeUuidType() {
|
||||
return nativeUuidType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mapping of JDBC to DB types.
|
||||
*
|
||||
*
|
||||
* @return the db type map
|
||||
*/
|
||||
public DbTypeMap getDbTypeMap() {
|
||||
@@ -315,7 +321,7 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return the close quote for quoted identifiers.
|
||||
*
|
||||
*
|
||||
* @return the close quote
|
||||
*/
|
||||
public String getCloseQuote() {
|
||||
@@ -324,7 +330,7 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return the open quote for quoted identifiers.
|
||||
*
|
||||
*
|
||||
* @return the open quote
|
||||
*/
|
||||
public String getOpenQuote() {
|
||||
@@ -333,7 +339,7 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return the JDBC type used to store booleans.
|
||||
*
|
||||
*
|
||||
* @return the boolean db type
|
||||
*/
|
||||
public int getBooleanDbType() {
|
||||
@@ -363,7 +369,7 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return true if empty strings should be treated as null.
|
||||
*
|
||||
*
|
||||
* @return true, if checks if is treat empty strings as null
|
||||
*/
|
||||
public boolean isTreatEmptyStringsAsNull() {
|
||||
@@ -399,7 +405,7 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return the DB identity/sequence features for this platform.
|
||||
*
|
||||
*
|
||||
* @return the db identity
|
||||
*/
|
||||
public DbIdentity getDbIdentity() {
|
||||
@@ -412,7 +418,7 @@ public class DatabasePlatform {
|
||||
* <p>
|
||||
* Basically add the clauses for limit/offset, rownum, row_number().
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return the sql limiter
|
||||
*/
|
||||
public SqlLimiter getSqlLimiter() {
|
||||
@@ -421,15 +427,12 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Convert backticks to the platform specific open quote and close quote
|
||||
*
|
||||
* <p>
|
||||
* Specific plugins may implement this method to cater for platform specific
|
||||
* naming rules.
|
||||
* </p>
|
||||
*
|
||||
* @param dbName
|
||||
* the db name
|
||||
*
|
||||
*
|
||||
* @param dbName the db name
|
||||
* @return the string
|
||||
*/
|
||||
public String convertQuotedIdentifiers(String dbName) {
|
||||
|
||||
@@ -8,6 +8,11 @@ package com.avaje.ebean.config.dbplatform;
|
||||
*/
|
||||
public class DbType {
|
||||
|
||||
/**
|
||||
* DB native UUID type (H2 and Postgres).
|
||||
*/
|
||||
public static final int UUID = 5010;
|
||||
|
||||
/**
|
||||
* Type to map Map content to Postgres HSTORE.
|
||||
*/
|
||||
|
||||
@@ -49,6 +49,8 @@ public class DbTypeMap {
|
||||
lookup.put("TIME", Types.TIME);
|
||||
lookup.put("TIMESTAMP", Types.TIMESTAMP);
|
||||
|
||||
lookup.put("UUID", DbType.UUID);
|
||||
|
||||
// Not standard java.sql.Types
|
||||
// logical JSON storage types
|
||||
lookup.put("JSON", DbType.JSON);
|
||||
@@ -101,6 +103,9 @@ 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"));
|
||||
|
||||
if (logicalTypes) {
|
||||
// keep it logical for 2 layer DDL generation
|
||||
put(DbType.HSTORE, new DbType("hstore"));
|
||||
|
||||
@@ -16,6 +16,7 @@ public class H2Platform extends DatabasePlatform {
|
||||
this.dbEncrypt = new H2DbEncrypt();
|
||||
this.platformDdl = new H2Ddl(this.dbTypeMap, dbIdentity);
|
||||
this.historySupport = new H2HistorySupport();
|
||||
this.nativeUuidType = true;
|
||||
|
||||
// only support getGeneratedKeys with non-batch JDBC
|
||||
// so generally use SEQUENCE instead of IDENTITY for H2
|
||||
|
||||
@@ -23,10 +23,10 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
// OnQueryOnly.CLOSE as a performance optimisation on Postgres
|
||||
this.onQueryOnly = OnQueryOnly.CLOSE;
|
||||
this.likeClause = "like ? escape''";
|
||||
|
||||
this.selectCountWithAlias = true;
|
||||
this.blobDbType = Types.LONGVARBINARY;
|
||||
this.clobDbType = Types.VARCHAR;
|
||||
this.nativeUuidType = true;
|
||||
|
||||
this.dbEncrypt = new PostgresDbEncrypt();
|
||||
this.historySupport = new PostgresHistorySupport();
|
||||
|
||||
+14
-11
@@ -98,7 +98,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
reset();
|
||||
|
||||
String tableName = lowerName(createTable.getName());
|
||||
String tableName = lowerTableName(createTable.getName());
|
||||
List<Column> columns = createTable.getColumn();
|
||||
List<Column> pk = determinePrimaryKeyColumns(columns);
|
||||
|
||||
@@ -248,7 +248,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeForeignKey(DdlWrite write, String fkName, String tableName, String[] columns, String refTable, String[] refColumns, String indexName) throws IOException {
|
||||
|
||||
tableName = lowerName(tableName);
|
||||
tableName = lowerTableName(tableName);
|
||||
DdlBuffer fkeyBuffer = write.applyForeignKeys();
|
||||
alterTableAddForeignKey(fkeyBuffer, fkName, tableName, columns, refTable, refColumns);
|
||||
|
||||
@@ -282,7 +282,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
if (i > 0) {
|
||||
buffer.append(",");
|
||||
}
|
||||
buffer.append(lowerName(columns[i].trim()));
|
||||
buffer.append(lowerColumnName(columns[i].trim()));
|
||||
}
|
||||
buffer.append(")");
|
||||
}
|
||||
@@ -367,7 +367,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
buffer.append(",").newLine();
|
||||
buffer.append(" constraint ").append(uqName).append(" unique ");
|
||||
buffer.append("(");
|
||||
buffer.append(lowerName(column.getName()));
|
||||
buffer.append(lowerColumnName(column.getName()));
|
||||
buffer.append(")");
|
||||
}
|
||||
|
||||
@@ -401,14 +401,17 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the table or column name to lower case.
|
||||
* <p>
|
||||
* This is passed up to the platformDdl to override as desired.
|
||||
* Generally lower case with underscore is a good cross database
|
||||
* choice for column/table names.
|
||||
* Convert the table lower case.
|
||||
*/
|
||||
protected String lowerName(String name) {
|
||||
return naming.lowerName(name);
|
||||
protected String lowerTableName(String name) {
|
||||
return naming.lowerTableName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the column name to lower case.
|
||||
*/
|
||||
protected String lowerColumnName(String name) {
|
||||
return naming.lowerColumnName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -176,7 +176,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
|
||||
String platformType = platformDdl.convert(type, false);
|
||||
buffer.append(" ");
|
||||
buffer.append(platformDdl.lowerName(columnName), 29);
|
||||
buffer.append(platformDdl.lowerColumnName(columnName), 29);
|
||||
buffer.append(platformType);
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ public class PlatformDdl {
|
||||
String platformType = convert(column.getType(), identityColumn);
|
||||
|
||||
buffer.append(" ");
|
||||
buffer.append(lowerName(column.getName()), 29);
|
||||
buffer.append(lowerColumnName(column.getName()), 29);
|
||||
buffer.append(platformType);
|
||||
if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) {
|
||||
buffer.append(" not null");
|
||||
@@ -254,7 +254,7 @@ public class PlatformDdl {
|
||||
appendColumns(columns, buffer);
|
||||
buffer
|
||||
.append(" references ")
|
||||
.append(lowerName(refTable));
|
||||
.append(lowerTableName(refTable));
|
||||
appendColumns(refColumns, buffer);
|
||||
appendWithSpace(foreignKeyRestrict, buffer);
|
||||
|
||||
@@ -346,7 +346,7 @@ public class PlatformDdl {
|
||||
if (i > 0) {
|
||||
buffer.append(",");
|
||||
}
|
||||
buffer.append(lowerName(columns[i].trim()));
|
||||
buffer.append(lowerColumnName(columns[i].trim()));
|
||||
}
|
||||
buffer.append(")");
|
||||
}
|
||||
@@ -358,16 +358,26 @@ public class PlatformDdl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the table or column name to lower case.
|
||||
* Convert the table to lower case.
|
||||
* <p>
|
||||
* This is passed up to the platformDdl to override as desired.
|
||||
* Generally lower case with underscore is a good cross database
|
||||
* Override as desired. Generally lower case with underscore is a good cross database
|
||||
* choice for column/table names.
|
||||
*/
|
||||
protected String lowerName(String name) {
|
||||
return naming.lowerName(name);
|
||||
protected String lowerTableName(String name) {
|
||||
return naming.lowerTableName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the column name to lower case.
|
||||
* <p>
|
||||
* Override as desired. Generally lower case with underscore is a good cross database
|
||||
* choice for column/table names.
|
||||
*/
|
||||
protected String lowerColumnName(String name) {
|
||||
return naming.lowerColumnName(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Null safe Boolean true test.
|
||||
*/
|
||||
|
||||
+2
-2
@@ -81,12 +81,12 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
appendInsertIntoHistory(apply, historyTable, includedColumns);
|
||||
apply
|
||||
.append(" NEW.").append(sysPeriod).append(" = tstzrange(CURRENT_TIMESTAMP,null);").newLine()
|
||||
.append(" return new;").newLine().newLine();
|
||||
.append(" return new;").newLine();
|
||||
apply
|
||||
.append(" elsif (TG_OP = 'DELETE') then").newLine();
|
||||
appendInsertIntoHistory(apply, historyTable, includedColumns);
|
||||
apply
|
||||
.append(" return old;").newLine().newLine();
|
||||
.append(" return old;").newLine();
|
||||
apply
|
||||
.append(" end if;").newLine()
|
||||
.append("end;").newLine()
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.util.List;
|
||||
*/
|
||||
public class MCompoundForeignKey {
|
||||
|
||||
private final String name;
|
||||
private String name;
|
||||
private final String referenceTable;
|
||||
private final List<String> columns = new ArrayList<String>();
|
||||
private final List<String> referenceColumns = new ArrayList<String>();
|
||||
@@ -47,6 +47,28 @@ public class MCompoundForeignKey {
|
||||
return fk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a counter to the foreign key and index names to avoid duplication.
|
||||
*/
|
||||
public void addNameSuffix(int counter) {
|
||||
this.name = name + "_" + counter;
|
||||
this.indexName = indexName + "_" + counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the foreign key name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index name.
|
||||
*/
|
||||
public String getIndexName() {
|
||||
return indexName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the columns making up the foreign key in order.
|
||||
*/
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.avaje.ebean.dbmigration.migration.UniqueConstraint;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
@@ -482,4 +483,32 @@ public class MTable {
|
||||
return (value == 0) ? null : BigInteger.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are duplicate foreign keys.
|
||||
* <p>
|
||||
* This can occur when an ManyToMany relates back to itself.
|
||||
* </p>
|
||||
*/
|
||||
public void checkDuplicateForeignKeys() {
|
||||
|
||||
if (hasDuplicateForeignKeys()) {
|
||||
int counter = 1;
|
||||
for (MCompoundForeignKey fk : compoundKeys) {
|
||||
fk.addNameSuffix(counter++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the foreign key names are not unique.
|
||||
*/
|
||||
private boolean hasDuplicateForeignKeys() {
|
||||
Set<String> fkNames = new HashSet<String>();
|
||||
for (MCompoundForeignKey fk : compoundKeys) {
|
||||
if (!fkNames.add(fk.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,8 +213,8 @@ public class ModelContainer {
|
||||
/**
|
||||
* Add a table (typically from reading EbeanServer meta data).
|
||||
*/
|
||||
public void addTable(MTable table) {
|
||||
tables.put(table.getName(), table);
|
||||
public MTable addTable(MTable table) {
|
||||
return tables.put(table.getName(), table);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,8 +67,8 @@ public class ModelBuildContext {
|
||||
return maxLength(constraintNaming.checkConstraintName(tableName, columnName), checkCount);
|
||||
}
|
||||
|
||||
public void addTable(MTable table) {
|
||||
model.addTable(table);
|
||||
public MTable addTable(MTable table) {
|
||||
return model.addTable(table);
|
||||
}
|
||||
|
||||
public void addIndex(String indexName, String tableName, String columnName) {
|
||||
|
||||
+7
-1
@@ -35,7 +35,11 @@ public class ModelBuildIntersectionTable {
|
||||
public void build() {
|
||||
|
||||
intersectionTable = createTable();
|
||||
ctx.addTable(intersectionTable);
|
||||
MTable existingTable = ctx.addTable(intersectionTable);
|
||||
if (existingTable != null) {
|
||||
throw new IllegalStateException("Property " + manyProp.getFullBeanName() + " has duplicate ManyToMany intersection table " + intersectionTable.getName()
|
||||
+ ". Please use @JoinTable to define unique table to use");
|
||||
}
|
||||
|
||||
buildFkConstraints();
|
||||
}
|
||||
@@ -47,6 +51,8 @@ public class ModelBuildIntersectionTable {
|
||||
|
||||
BeanDescriptor<?> targetDesc = manyProp.getTargetDescriptor();
|
||||
buildFkConstraints(targetDesc, tableJoin.columns(), false);
|
||||
|
||||
intersectionTable.checkDuplicateForeignKeys();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Generation DDL migration scripts based on changes to the model.
|
||||
* <p>
|
||||
* You can use <code>DbMigration</code> to compare the entity bean model to its prior state
|
||||
* and generate DDL for the differences - adding tables, columns etc.
|
||||
* </p>
|
||||
*/
|
||||
package com.avaje.ebean.dbmigration;
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Provides a built in change log mechanism and can audit changes.
|
||||
* <p>
|
||||
* Although you can build something similar using existing event adapters such as <code>BeanPersistController</code>
|
||||
* this is built for purpose to reduce the effort and provide optimal performance for auditing or logging changes.
|
||||
* </p>
|
||||
* <p>
|
||||
* By default you can annotate beans with <code>@ChangeLog</code> and associated change events are logged by default
|
||||
* in a JSON form with appropriate auditing attributes such as who made the changes and ip address of the user etc
|
||||
* via implementation of <code>ChangeLogPrepare</code>
|
||||
* </p>
|
||||
*/
|
||||
package com.avaje.ebean.event.changelog;
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Provides Auditing of read events including queries and L2 cache.
|
||||
* <p>
|
||||
* Provides a built support for supplied an audit of all the 'read events' for beans annotated
|
||||
* with <code>@ReadAudit</code>
|
||||
* </p>
|
||||
*/
|
||||
package com.avaje.ebean.event.readaudit;
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Provides a API for plugins.
|
||||
* <p>
|
||||
* Plugins can get and read meta data about the entity beans and enhance or utilise the
|
||||
* functionality of the EbeanServer.
|
||||
* </p>
|
||||
*/
|
||||
package com.avaje.ebean.plugin;
|
||||
@@ -59,11 +59,6 @@ public class ScopedTransaction implements SpiTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endQueryOnly() {
|
||||
transaction.endQueryOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLogPrefix() {
|
||||
return transaction.getLogPrefix();
|
||||
|
||||
@@ -19,11 +19,6 @@ import com.avaje.ebeaninternal.server.persist.BatchControl;
|
||||
*/
|
||||
public interface SpiTransaction extends Transaction {
|
||||
|
||||
/**
|
||||
* End the transaction when had query only use.
|
||||
*/
|
||||
void endQueryOnly();
|
||||
|
||||
/**
|
||||
* Return the string prefix with the transactin id and label used in logging.
|
||||
*/
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.BeanState;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Default implementation of BeanState.
|
||||
*/
|
||||
@@ -23,9 +23,13 @@ public class DefaultBeanState implements BeanState {
|
||||
this.intercept = entityBean._ebean_getIntercept();
|
||||
}
|
||||
|
||||
public boolean isReference() {
|
||||
return intercept.isReference();
|
||||
}
|
||||
public void setPropertyLoaded(String propertyName, boolean loaded) {
|
||||
intercept.setPropertyLoaded(propertyName, loaded);
|
||||
}
|
||||
|
||||
public boolean isReference() {
|
||||
return intercept.isReference();
|
||||
}
|
||||
|
||||
public boolean isNew() {
|
||||
return intercept.isNew();
|
||||
|
||||
@@ -1301,6 +1301,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.DELETE, query, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
request.markNotQueryOnly();
|
||||
return request.delete();
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
|
||||
@@ -10,6 +10,7 @@ import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.*;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanFindController;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
@@ -71,6 +72,13 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
this.readOnly = query.isReadOnly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the transaction as not being query only.
|
||||
*/
|
||||
@Override
|
||||
public void markNotQueryOnly() {
|
||||
transaction.markNotQueryOnly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the database platform like clause.
|
||||
@@ -151,6 +159,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return persistenceContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the bean to the persistence context.
|
||||
*/
|
||||
public void persistenceContextAdd(EntityBean bean) {
|
||||
Object id = beanDescriptor.getId(bean);
|
||||
persistenceContext.put(id, bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will create a local (readOnly) transaction if no current transaction
|
||||
* exists.
|
||||
@@ -210,7 +226,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
*/
|
||||
public void endTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
transaction.endQueryOnly();
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ public final class RelationalQueryRequest {
|
||||
*/
|
||||
public void endTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
trans.endQueryOnly();
|
||||
trans.commit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,4 +112,8 @@ public interface SpiOrmQueryRequest<T> {
|
||||
*/
|
||||
String getDBLikeClause();
|
||||
|
||||
/**
|
||||
* Mark the underlying transaction as not being query only.
|
||||
*/
|
||||
void markNotQueryOnly();
|
||||
}
|
||||
+29
-14
@@ -73,7 +73,31 @@ public class DeployBeanPropertyLists {
|
||||
|
||||
this.propertyMap = new LinkedHashMap<String, BeanProperty>();
|
||||
|
||||
// see if there is a discriminator property we should add
|
||||
String discriminatorColumn = null;
|
||||
BeanProperty discProperty = null;
|
||||
|
||||
InheritInfo inheritInfo = deploy.getInheritInfo();
|
||||
if (inheritInfo != null) {
|
||||
// Create a BeanProperty for the discriminator column to support
|
||||
// using RawSql queries with inheritance
|
||||
discriminatorColumn = inheritInfo.getDiscriminatorColumn();
|
||||
DeployBeanProperty discDeployProp = new DeployBeanProperty(deploy, String.class, new ScalarTypeString(), null);
|
||||
discDeployProp.setDiscriminator();
|
||||
discDeployProp.setName(discriminatorColumn);
|
||||
discDeployProp.setDbColumn(discriminatorColumn);
|
||||
|
||||
// only register it in the propertyMap. This might not be used if
|
||||
// an explicit property is mapped to the discriminator on the bean
|
||||
discProperty = new BeanProperty(desc, discDeployProp);
|
||||
}
|
||||
|
||||
for (DeployBeanProperty prop : deploy.propertiesAll()) {
|
||||
if (discriminatorColumn != null && discriminatorColumn.equals(prop.getDbColumn())) {
|
||||
// we have an explicit property mapped to the discriminator column
|
||||
prop.setDiscriminator();
|
||||
discProperty = null;
|
||||
}
|
||||
BeanProperty beanProp = createBeanProperty(owner, prop);
|
||||
propertyMap.put(beanProp.getName(), beanProp);
|
||||
}
|
||||
@@ -84,21 +108,12 @@ public class DeployBeanPropertyLists {
|
||||
allocateToList(prop);
|
||||
}
|
||||
|
||||
InheritInfo inheritInfo = deploy.getInheritInfo();
|
||||
if (inheritInfo != null) {
|
||||
// Create a BeanProperty for the discriminator column to support
|
||||
// using RawSql queries with inheritance
|
||||
String discriminatorColumn = inheritInfo.getDiscriminatorColumn();
|
||||
DeployBeanProperty discDeployProp = new DeployBeanProperty(deploy, String.class, new ScalarTypeString(), null);
|
||||
discDeployProp.setDiscriminator();
|
||||
discDeployProp.setName(discriminatorColumn);
|
||||
discDeployProp.setDbColumn(discriminatorColumn);
|
||||
|
||||
// create the discriminator BeanProperty and only register it in the propertyMap
|
||||
BeanProperty dprop = new BeanProperty(desc, discDeployProp);
|
||||
propertyMap.put(dprop.getName(), dprop);
|
||||
if (discProperty != null) {
|
||||
// put the discriminator property into the property map only
|
||||
// (after the real properties have been organised into their lists)
|
||||
propertyMap.put(discProperty.getName(), discProperty);
|
||||
}
|
||||
|
||||
|
||||
List<DeployTableJoin> deployTableJoins = deploy.getTableJoins();
|
||||
tableJoins = new TableJoin[deployTableJoins.size()];
|
||||
for (int i = 0; i < deployTableJoins.size(); i++) {
|
||||
|
||||
@@ -76,6 +76,8 @@ public class AnnotationFields extends AnnotationParser {
|
||||
*/
|
||||
private void readAssocOne(DeployBeanProperty prop) {
|
||||
|
||||
readJsonAnnotations(prop);
|
||||
|
||||
Id id = get(prop, Id.class);
|
||||
if (id != null) {
|
||||
prop.setId();
|
||||
@@ -117,25 +119,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
readColumn(column, prop);
|
||||
}
|
||||
|
||||
if (jacksonAnnotationsPresent) {
|
||||
com.fasterxml.jackson.annotation.JsonIgnore jsonIgnore = get(prop, com.fasterxml.jackson.annotation.JsonIgnore.class);
|
||||
if (jsonIgnore != null) {
|
||||
prop.setJsonSerialize(!jsonIgnore.value());
|
||||
prop.setJsonDeserialize(!jsonIgnore.value());
|
||||
}
|
||||
}
|
||||
|
||||
Expose expose = get(prop, Expose.class);
|
||||
if (expose != null) {
|
||||
prop.setJsonSerialize(expose.serialize());
|
||||
prop.setJsonDeserialize(expose.deserialize());
|
||||
}
|
||||
|
||||
JsonIgnore jsonIgnore = get(prop, JsonIgnore.class);
|
||||
if (jsonIgnore != null) {
|
||||
prop.setJsonSerialize(jsonIgnore.serialize());
|
||||
prop.setJsonDeserialize(jsonIgnore.deserialize());
|
||||
}
|
||||
readJsonAnnotations(prop);
|
||||
|
||||
if (prop.getDbColumn() == null) {
|
||||
// No @Column annotation or @Column.name() not set
|
||||
@@ -291,6 +275,28 @@ public class AnnotationFields extends AnnotationParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void readJsonAnnotations(DeployBeanProperty prop) {
|
||||
if (jacksonAnnotationsPresent) {
|
||||
com.fasterxml.jackson.annotation.JsonIgnore jsonIgnore = get(prop, com.fasterxml.jackson.annotation.JsonIgnore.class);
|
||||
if (jsonIgnore != null) {
|
||||
prop.setJsonSerialize(!jsonIgnore.value());
|
||||
prop.setJsonDeserialize(!jsonIgnore.value());
|
||||
}
|
||||
}
|
||||
|
||||
Expose expose = get(prop, Expose.class);
|
||||
if (expose != null) {
|
||||
prop.setJsonSerialize(expose.serialize());
|
||||
prop.setJsonDeserialize(expose.deserialize());
|
||||
}
|
||||
|
||||
JsonIgnore jsonIgnore = get(prop, JsonIgnore.class);
|
||||
if (jsonIgnore != null) {
|
||||
prop.setJsonSerialize(jsonIgnore.serialize());
|
||||
prop.setJsonDeserialize(jsonIgnore.deserialize());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasRelationshipItem(DeployBeanProperty prop) {
|
||||
return get(prop, OneToMany.class) != null ||
|
||||
get(prop, ManyToOne.class) != null ||
|
||||
|
||||
@@ -111,8 +111,8 @@ public class DeployUtil {
|
||||
}
|
||||
ScalarType<?> scalarType = typeManager.getScalarType(enumType);
|
||||
if (scalarType == null) {
|
||||
// see if it has a Mapping in avaje.properties
|
||||
scalarType = typeManager.createEnumScalarType(enumType);
|
||||
// look for @DbEnumValue or @EnumValue annotations etc
|
||||
scalarType = typeManager.createEnumScalarType((Class<? extends Enum<?>>)enumType);
|
||||
if (scalarType == null) {
|
||||
// use JPA normal Enum type (without mapping)
|
||||
EnumType type = enumerated != null ? enumerated.value() : null;
|
||||
|
||||
+16
-12
@@ -44,28 +44,32 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
return new JsonPathExpression(propertyName, path, Op.NOT_EXISTS, null);
|
||||
}
|
||||
|
||||
public Expression jsonEqualTo(String propertyName, String path, Object val) {
|
||||
return new JsonPathExpression(propertyName, path, Op.EQ, val);
|
||||
public Expression jsonEqualTo(String propertyName, String path, Object value) {
|
||||
return new JsonPathExpression(propertyName, path, Op.EQ, value);
|
||||
}
|
||||
|
||||
public Expression jsonNotEqualTo(String propertyName, String path, Object val) {
|
||||
return new JsonPathExpression(propertyName, path, Op.NOT_EQ, val);
|
||||
public Expression jsonNotEqualTo(String propertyName, String path, Object value) {
|
||||
return new JsonPathExpression(propertyName, path, Op.NOT_EQ, value);
|
||||
}
|
||||
|
||||
public Expression jsonGreaterThan(String propertyName, String path, Object val) {
|
||||
return new JsonPathExpression(propertyName, path, Op.GT, val);
|
||||
public Expression jsonGreaterThan(String propertyName, String path, Object value) {
|
||||
return new JsonPathExpression(propertyName, path, Op.GT, value);
|
||||
}
|
||||
|
||||
public Expression jsonGreaterOrEqual(String propertyName, String path, Object val) {
|
||||
return new JsonPathExpression(propertyName, path, Op.GT_EQ, val);
|
||||
public Expression jsonGreaterOrEqual(String propertyName, String path, Object value) {
|
||||
return new JsonPathExpression(propertyName, path, Op.GT_EQ, value);
|
||||
}
|
||||
|
||||
public Expression jsonLessThan(String propertyName, String path, Object val) {
|
||||
return new JsonPathExpression(propertyName, path, Op.LT, val);
|
||||
public Expression jsonLessThan(String propertyName, String path, Object value) {
|
||||
return new JsonPathExpression(propertyName, path, Op.LT, value);
|
||||
}
|
||||
|
||||
public Expression jsonLessOrEqualTo(String propertyName, String path, Object val) {
|
||||
return new JsonPathExpression(propertyName, path, Op.LT_EQ, val);
|
||||
public Expression jsonLessOrEqualTo(String propertyName, String path, Object value) {
|
||||
return new JsonPathExpression(propertyName, path, Op.LT_EQ, value);
|
||||
}
|
||||
|
||||
public Expression jsonBetween(String propertyName, String path, Object lowerValue, Object upperValue) {
|
||||
return new JsonPathExpression(propertyName, path, lowerValue, upperValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,11 +30,31 @@ class JsonPathExpression extends AbstractExpression {
|
||||
*/
|
||||
protected final Object value;
|
||||
|
||||
/**
|
||||
* For Between this is the upper bind value.
|
||||
*/
|
||||
protected final Object upperValue;
|
||||
|
||||
/**
|
||||
* Construct for Operator (not BETWEEN though).
|
||||
*/
|
||||
JsonPathExpression(String propertyName, String path, Op operator, Object value) {
|
||||
super(propertyName);
|
||||
this.path = path;
|
||||
this.operator = operator;
|
||||
this.value = value;
|
||||
this.upperValue = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for BETWEEN expression.
|
||||
*/
|
||||
JsonPathExpression(String propertyName, String path, Object value, Object upperValue) {
|
||||
super(propertyName);
|
||||
this.path = path;
|
||||
this.operator = Op.BETWEEN;
|
||||
this.value = value;
|
||||
this.upperValue = upperValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,7 +69,9 @@ class JsonPathExpression extends AbstractExpression {
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return (value == null) ? 0 : value.hashCode();
|
||||
int hc = (value == null) ? 0 : value.hashCode();
|
||||
hc = (upperValue == null) ? hc : hc * 31 + upperValue.hashCode();
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,7 +85,12 @@ class JsonPathExpression extends AbstractExpression {
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
if (value != null) {
|
||||
// value is null for EXISTS/NOT EXISTS
|
||||
request.addBindValue(value);
|
||||
}
|
||||
if (upperValue != null) {
|
||||
// upperValue only for BETWEEN operator
|
||||
request.addBindValue(upperValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,6 +377,14 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
return exprList.jsonLessOrEqualTo(propertyName, path, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Between - for the given path in a JSON document.
|
||||
*/
|
||||
@Override
|
||||
public ExpressionList<T> jsonBetween(String propertyName, String path, Object lowerValue, Object upperValue){
|
||||
return exprList.jsonBetween(propertyName, path, lowerValue, upperValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> ge(String propertyName, Object value) {
|
||||
return exprList.ge(propertyName, value);
|
||||
|
||||
@@ -15,6 +15,11 @@ public enum Op {
|
||||
*/
|
||||
NOT_EXISTS(" is null "),
|
||||
|
||||
/**
|
||||
* Between (JSON).
|
||||
*/
|
||||
BETWEEN(" between ? and ? "),
|
||||
|
||||
/**
|
||||
* Equal to
|
||||
*/
|
||||
|
||||
@@ -64,12 +64,16 @@ public final class ShutdownManager {
|
||||
|
||||
/**
|
||||
* Deregister the Shutdown hook.
|
||||
* <p>
|
||||
* In calling this method it is expected that application code will invoke
|
||||
* the shutdown() method.
|
||||
* </p>
|
||||
* <p>
|
||||
* For running in a Servlet Container a redeploy will cause a shutdown, and
|
||||
* for that case we need to make sure the shutdown hook is deregistered.
|
||||
* </p>
|
||||
*/
|
||||
protected static void deregisterShutdownHook() {
|
||||
public static void deregisterShutdownHook() {
|
||||
synchronized (servers) {
|
||||
try {
|
||||
Runtime.getRuntime().removeShutdownHook(shutdownHook);
|
||||
@@ -87,7 +91,10 @@ public final class ShutdownManager {
|
||||
protected static void registerShutdownHook() {
|
||||
synchronized (servers) {
|
||||
try {
|
||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||
String value = System.getProperty("ebean.registerShutdownHook");
|
||||
if (value == null || !value.trim().equalsIgnoreCase("false")) {
|
||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||
}
|
||||
} catch (IllegalStateException ex) {
|
||||
if (!ex.getMessage().equals("Shutdown in progress")) {
|
||||
throw ex;
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.List;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.server.core.JsonExpressionHandler;
|
||||
import com.avaje.ebeaninternal.server.core.Message;
|
||||
@@ -307,6 +308,11 @@ public class Binder {
|
||||
b.setBytes((byte[]) data);
|
||||
break;
|
||||
|
||||
case DbType.UUID:
|
||||
// native UUID support in H2 and Postgres
|
||||
b.setObject(data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.OTHER:
|
||||
b.setObject(data);
|
||||
break;
|
||||
|
||||
@@ -643,6 +643,15 @@ public final class DefaultPersister implements Persister {
|
||||
private boolean isCascade() {
|
||||
return cascade;
|
||||
}
|
||||
|
||||
public void modifyListenReset(BeanCollection<?> c) {
|
||||
if (insertedParent) {
|
||||
// after insert set the modify listening mode
|
||||
// for private owned etc
|
||||
c.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
c.modifyReset();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMany(SaveManyPropRequest saveMany, boolean insertMode) {
|
||||
@@ -684,6 +693,7 @@ public final class DefaultPersister implements Persister {
|
||||
|
||||
BeanCollection<?> c = (BeanCollection<?>) details;
|
||||
Set<?> modifyRemovals = c.getModifyRemovals();
|
||||
saveMany.modifyListenReset(c);
|
||||
if (modifyRemovals != null && !modifyRemovals.isEmpty()) {
|
||||
|
||||
SpiTransaction t = saveMany.getTransaction();
|
||||
@@ -908,6 +918,10 @@ public final class DefaultPersister implements Persister {
|
||||
t.depth(+1);
|
||||
|
||||
if (additions != null && !additions.isEmpty()) {
|
||||
// ensure any cascade batch has been flushed prior
|
||||
// to inserting into the intersection table
|
||||
t.flushBatch();
|
||||
|
||||
for (Object other : additions) {
|
||||
EntityBean otherBean = (EntityBean) other;
|
||||
// the object from the 'other' side of the ManyToMany
|
||||
@@ -933,6 +947,10 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
}
|
||||
if (deletions != null && !deletions.isEmpty()) {
|
||||
// ensure any cascade batch has been flushed prior
|
||||
// to inserting into the intersection table
|
||||
t.flushBatch();
|
||||
|
||||
for (Object other : deletions) {
|
||||
EntityBean otherDelete = (EntityBean) other;
|
||||
// the object from the 'other' side of the ManyToMany
|
||||
|
||||
@@ -433,6 +433,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
} else {
|
||||
// nextBean set to previously read currentBean
|
||||
nextBean = currentBean;
|
||||
request.persistenceContextAdd(nextBean);
|
||||
// check the current row we have just moved to
|
||||
if (checkForDifferentBean()) {
|
||||
return true;
|
||||
|
||||
@@ -484,6 +484,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
copy.profilingListener = profilingListener;
|
||||
|
||||
copy.query = query;
|
||||
copy.rootTableAlias = rootTableAlias;
|
||||
copy.additionalWhere = additionalWhere;
|
||||
copy.additionalHaving = additionalHaving;
|
||||
copy.distinct = distinct;
|
||||
|
||||
@@ -125,9 +125,12 @@ public class DJsonContext implements JsonContext {
|
||||
|
||||
List<T> list = new ArrayList<T>();
|
||||
|
||||
JsonToken event = src.nextToken();
|
||||
if (event != JsonToken.START_ARRAY) {
|
||||
throw new JsonParseException("Expecting start_array event but got " + event, src.getCurrentLocation());
|
||||
JsonToken currentToken = src.getCurrentToken();
|
||||
if (currentToken != JsonToken.START_ARRAY) {
|
||||
JsonToken event = src.nextToken();
|
||||
if (event != JsonToken.START_ARRAY) {
|
||||
throw new JsonParseException("Expecting start_array event but got " + event, src.getCurrentLocation());
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
@@ -849,23 +849,6 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
connection.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* End the transaction on a query only request.
|
||||
*/
|
||||
@Override
|
||||
public void endQueryOnly() {
|
||||
if (!isActive()) {
|
||||
throw new IllegalStateException(illegalStateMessage);
|
||||
}
|
||||
try {
|
||||
connectionEndForQueryOnly();
|
||||
} finally {
|
||||
// these will not throw an exception
|
||||
deactivate();
|
||||
notifyQueryOnly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the transaction.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.annotation.DbEnumType;
|
||||
import com.avaje.ebean.annotation.DbEnumValue;
|
||||
import com.avaje.ebean.annotation.EnumMapping;
|
||||
import com.avaje.ebean.annotation.EnumValue;
|
||||
import com.avaje.ebean.config.*;
|
||||
@@ -21,6 +23,7 @@ import org.slf4j.LoggerFactory;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
@@ -419,8 +422,10 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
return value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private boolean isIntegerType(String s) {
|
||||
protected boolean isIntegerType(String s) {
|
||||
if (isLeadingZeros(s)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Integer.parseInt(s);
|
||||
return true;
|
||||
@@ -429,6 +434,13 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat leading zeros as a non-integer for enum values.
|
||||
*/
|
||||
private boolean isLeadingZeros(String s) {
|
||||
return s.length() > 1 && s.charAt(0) == '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the Mapping of Enum fields to DB values using EnumValue annotations.
|
||||
* <p>
|
||||
@@ -469,7 +481,16 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
* much shorter codes used in the DB.
|
||||
* </p>
|
||||
*/
|
||||
public ScalarType<?> createEnumScalarType(Class<?> enumType) {
|
||||
public ScalarType<?> createEnumScalarType(Class<? extends Enum<?>> enumType) {
|
||||
|
||||
Method[] methods = enumType.getMethods();
|
||||
for (int i = 0; i <methods.length; i++) {
|
||||
DbEnumValue dbValue = methods[i].getAnnotation(DbEnumValue.class);
|
||||
if (dbValue != null) {
|
||||
boolean integerValues = DbEnumType.INTEGER == dbValue.storage();
|
||||
return createEnumScalarTypeDbValue(enumType, methods[i], integerValues);
|
||||
}
|
||||
}
|
||||
|
||||
// get the mapping information from EnumMapping
|
||||
EnumMapping enumMapping = enumType.getAnnotation(EnumMapping.class);
|
||||
@@ -487,6 +508,33 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
return createEnumScalarType(enumType, nameValueMap, integerType, dbColumnLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the Mapping of Enum fields to DB values using EnumValue annotations.
|
||||
* <p>
|
||||
* Return null if the EnumValue annotations are not present/used.
|
||||
* </p>
|
||||
*/
|
||||
private ScalarType<?> createEnumScalarTypeDbValue(Class<? extends Enum<?>> enumType, Method method, boolean integerType) {
|
||||
|
||||
Map<String, String> nameValueMap = new HashMap<String, String>();
|
||||
|
||||
Enum<?>[] enumConstants = enumType.getEnumConstants();
|
||||
for (int i = 0; i < enumConstants.length; i++) {
|
||||
try {
|
||||
Object value = method.invoke(enumConstants[i]);
|
||||
nameValueMap.put(enumConstants[i].name(), value.toString());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Error trying to invoke DbEnumValue method on "+enumConstants[i], e);
|
||||
}
|
||||
}
|
||||
if (nameValueMap.isEmpty()) {
|
||||
// Not using EnumValue here
|
||||
return null;
|
||||
}
|
||||
|
||||
return createEnumScalarType(enumType, nameValueMap, integerType, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the name value mapping and integer/string type and explicit DB column
|
||||
* length create the ScalarType for the Enum.
|
||||
@@ -768,7 +816,6 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
*/
|
||||
protected void initialiseStandard(JsonConfig.DateTime mode, ServerConfig config) {
|
||||
|
||||
boolean binaryUUID = config.isUuidStoreAsBinary();
|
||||
DatabasePlatform databasePlatform = config.getDatabasePlatform();
|
||||
int platformClobType = databasePlatform.getClobDbType();
|
||||
int platformBlobType = databasePlatform.getBlobDbType();
|
||||
@@ -795,9 +842,17 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
nativeMap.put(Types.BIT, booleanType);
|
||||
}
|
||||
|
||||
// Store UUID as binary(16) or varchar(40)
|
||||
ScalarType<?> uuidType = (binaryUUID) ? new ScalarTypeUUIDBinary() : new ScalarTypeUUIDVarchar();
|
||||
typeMap.put(UUID.class, uuidType);
|
||||
boolean nativeUuidType = databasePlatform.isNativeUuidType();
|
||||
ServerConfig.DbUuid dbUuid = config.getDbUuid();
|
||||
|
||||
if (nativeUuidType && dbUuid == ServerConfig.DbUuid.AUTO) {
|
||||
// DB has native support for UUID
|
||||
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();
|
||||
typeMap.put(UUID.class, uuidType);
|
||||
}
|
||||
|
||||
typeMap.put(File.class, fileType);
|
||||
typeMap.put(InetAddress.class, inetAddressType);
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import com.avaje.ebean.text.json.JsonWriter;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Postgres Hstore type which maps Map<String,String> to a single 'HStore column' in the DB.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class ScalarTypeUUIDNative extends ScalarTypeBase<UUID> {
|
||||
|
||||
public ScalarTypeUUIDNative() {
|
||||
super(UUID.class, false, DbType.UUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public UUID read(DataReader dataReader) throws SQLException {
|
||||
|
||||
Object value = dataReader.getObject();
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return (UUID)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind b, UUID value) throws SQLException {
|
||||
b.setObject(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toJdbcType(Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID toBeanType(Object value) {
|
||||
return (UUID) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(UUID v) {
|
||||
return v.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID parse(String value) {
|
||||
return UUID.fromString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID convertFromMillis(long dateTime) {
|
||||
throw new RuntimeException("Should never be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDateTimeCapable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID readData(DataInput dataInput) throws IOException {
|
||||
if (!dataInput.readBoolean()) {
|
||||
return null;
|
||||
} else {
|
||||
String json = dataInput.readUTF();
|
||||
return parse(json);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(DataOutput dataOutput, UUID v) throws IOException {
|
||||
if (v == null) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
dataOutput.writeBoolean(true);
|
||||
String json = format(v);
|
||||
dataOutput.writeUTF(json);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonWriter writer, String name, UUID value) throws IOException {
|
||||
// write the field name followed by the Map/JSON Object
|
||||
if (value == null) {
|
||||
writer.writeNullField(name);
|
||||
} else {
|
||||
writer.writeStringField(name, formatValue(value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID jsonRead(JsonParser parser, JsonToken event) throws IOException {
|
||||
// at this point the BeanProperty has read the START_OBJECT token
|
||||
// to check for a null value. Pass the START_OBJECT token through to
|
||||
// the EJson parsing so that it knows the first token has been read
|
||||
String strValue = parser.getValueAsString();
|
||||
return strValue == null ? null : parse(strValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public interface TypeManager {
|
||||
* Create a ScalarType for an Enum using a mapping (rather than JPA Ordinal
|
||||
* or String which has limitations).
|
||||
*/
|
||||
ScalarType<?> createEnumScalarType(Class<?> enumType);
|
||||
ScalarType<?> createEnumScalarType(Class<? extends Enum<?>> enumType);
|
||||
|
||||
/**
|
||||
* Return the ScalarType used to handle JSON content.
|
||||
|
||||
@@ -4,13 +4,24 @@ import com.avaje.ebeaninternal.api.ClassPathSearchService;
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.loader.jar.JarEntryData;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
@@ -157,7 +168,7 @@ public class ClassPathSearch implements ClassPathSearchService {
|
||||
|
||||
} else if (element.isJarOrWar()) {
|
||||
// search name including the ! offset if it is there
|
||||
if (classPathSize == 1 || filter.isSearchJar(element.getJarNameWithOffset())) {
|
||||
if (classPathSize == 1 || filter.isSearchJar(element.getJarNameWithOffset(), element.getJarOffset())) {
|
||||
scanJar(element);
|
||||
}
|
||||
|
||||
@@ -208,7 +219,7 @@ public class ClassPathSearch implements ClassPathSearchService {
|
||||
|
||||
private void scanDirectory(File directory) {
|
||||
List<String> directoryFiles = getDirectoryFiles(directory);
|
||||
searchFiles(Collections.enumeration(directoryFiles), null, null);
|
||||
searchFiles(Collections.enumeration(directoryFiles), null, null, null);
|
||||
}
|
||||
|
||||
private void scanUri(URI uri) throws IOException {
|
||||
@@ -234,12 +245,14 @@ public class ClassPathSearch implements ClassPathSearchService {
|
||||
File file = classPathEntry.classPath;
|
||||
module = new JarFile(file);
|
||||
|
||||
logger.trace("scanJar file:{}", file);
|
||||
|
||||
List<URI> classPathFromManifest = getClassPathFromManifest(file, module.getManifest());
|
||||
for (URI uri : classPathFromManifest) {
|
||||
scanUri(uri);
|
||||
}
|
||||
|
||||
searchFiles(module.entries(), classPathEntry.getJarName(), classPathEntry.jarOffset);
|
||||
searchFiles(module.entries(), classPathEntry.getJarName(), classPathEntry.jarOffset, file);
|
||||
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new IOException("Bad classpath error: ", ex);
|
||||
@@ -284,26 +297,30 @@ public class ClassPathSearch implements ClassPathSearchService {
|
||||
/**
|
||||
* Searches through the Java Archive (jar or war file) looking for classes
|
||||
* that match our requirements.
|
||||
*
|
||||
* @param files - all of the files in the Java Archive, this is an enumeration
|
||||
* @param entries - all of the entries in the Java Archive, this is an enumeration
|
||||
* provided by the Jar file
|
||||
* @param jarFileName - the name of the java archive
|
||||
* @param jarOffset - an offset inside the archive to chop off the name of the class -
|
||||
* this is used when we have bang path offsets (e.g.
|
||||
* file:///myfile.war!/WEB-INF/classes)
|
||||
* @param module the containing jar/war file (used for spring boot embedded jar scanning)
|
||||
*/
|
||||
private void searchFiles(Enumeration<?> files, String jarFileName, String jarOffset) {
|
||||
private void searchFiles(Enumeration<?> entries, String jarFileName, String jarOffset, File module) {
|
||||
|
||||
if (files == null) {
|
||||
if (entries == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Strips the first character off as all entries in a jar file have no /
|
||||
* prefix. We want to come out with a name like WEB-INF/classes/ to ensure
|
||||
* we filter the contents of the war/jar file by this.
|
||||
*/
|
||||
if (jarOffset != null) {
|
||||
logger.debug("searchFiles jarFileName:{} jarOffset:{}", jarFileName, jarOffset);
|
||||
|
||||
// Strips the first character off as all entries in a jar file have no /
|
||||
// prefix. We want to come out with a name like WEB-INF/classes/ to ensure
|
||||
// we filter the contents of the war/jar file by this.
|
||||
|
||||
if ("/".equals(jarOffset)) {
|
||||
// root level for runnable jar (spring boot etc)
|
||||
jarOffset = null;
|
||||
|
||||
} else if (jarOffset != null) {
|
||||
if (jarOffset.startsWith("/")) {
|
||||
jarOffset = jarOffset.substring(1);
|
||||
}
|
||||
@@ -313,48 +330,90 @@ public class ClassPathSearch implements ClassPathSearchService {
|
||||
}
|
||||
}
|
||||
|
||||
while (files.hasMoreElements()) {
|
||||
while (entries.hasMoreElements()) {
|
||||
|
||||
String fileName = files.nextElement().toString();
|
||||
Object element = entries.nextElement();
|
||||
String entryName = element.toString();
|
||||
|
||||
// we only want the class files
|
||||
if (fileName.endsWith(".class") && (jarOffset == null || fileName.startsWith(jarOffset))) {
|
||||
if (isEntryEmbeddedJar(module, entryName)) {
|
||||
scanSpringBootEmbeddedJar(jarFileName, module, entryName);
|
||||
}
|
||||
|
||||
if (jarOffset != null) {
|
||||
// we got through here only if there is an offset and we
|
||||
// matched it, so strip it off the file
|
||||
// as we are trying to find the className
|
||||
fileName = fileName.substring(jarOffset.length());
|
||||
if (isEntryClass(jarOffset, entryName)) {
|
||||
// check if it an 'interesting' class - entity etc
|
||||
registerScannedClass(jarFileName, jarOffset, entryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is an embedded jar that should be scanned.
|
||||
*/
|
||||
private boolean isEntryEmbeddedJar(File module, String entryName) {
|
||||
return entryName.endsWith(".jar") && module != null && filter.isSearchJar(entryName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a class that should be checked (for entity, interesting interface etc).
|
||||
*/
|
||||
private boolean isEntryClass(String jarOffset, String entryName) {
|
||||
return entryName.endsWith(".class") && (jarOffset == null || entryName.startsWith(jarOffset));
|
||||
}
|
||||
|
||||
private void scanSpringBootEmbeddedJar(String jarFileName, File module, String fileName) {
|
||||
// spring boot embedded jar
|
||||
logger.debug("spring boot embedded:{} : module:{}", fileName, module.getAbsoluteFile());
|
||||
try {
|
||||
org.springframework.boot.loader.jar.JarFile jarFile = new org.springframework.boot.loader.jar.JarFile(module);
|
||||
org.springframework.boot.loader.jar.JarFile jarEntryFile = jarFile.getNestedJarFile(jarFile.getJarEntryData(fileName));
|
||||
Iterator<JarEntryData> iterator = jarEntryFile.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
JarEntryData jarEntryData = iterator.next();
|
||||
if (jarEntryData.getName().toString().endsWith(".class")) {
|
||||
logger.debug("... spring boot class entry:{}", jarEntryData.getName().toString());
|
||||
registerScannedClass(jarFileName, null, jarEntryData.getName().toString());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerScannedClass(String jarFileName, String jarOffset, String fileName) {
|
||||
|
||||
if (jarOffset != null) {
|
||||
// we got through here only if there is an offset and we
|
||||
// matched it, so strip it off the file
|
||||
// as we are trying to find the className
|
||||
fileName = fileName.substring(jarOffset.length());
|
||||
}
|
||||
|
||||
String className = fileName.replace('/', '.').substring(0, fileName.length() - 6);
|
||||
int lastPeriod = className.lastIndexOf(".");
|
||||
|
||||
String pckgName;
|
||||
if (lastPeriod > 0) {
|
||||
pckgName = className.substring(0, lastPeriod);
|
||||
} else {
|
||||
pckgName = "";
|
||||
}
|
||||
|
||||
if (filter.isSearchPackage(pckgName)) {
|
||||
// get the class for our class name
|
||||
try {
|
||||
Class<?> theClass = Class.forName(className, false, classLoader);
|
||||
|
||||
if (matcher.isMatch(theClass)) {
|
||||
matchList.add(theClass);
|
||||
registerHit(jarFileName, theClass);
|
||||
}
|
||||
|
||||
String className = fileName.replace('/', '.').substring(0, fileName.length() - 6);
|
||||
int lastPeriod = className.lastIndexOf(".");
|
||||
|
||||
String pckgName;
|
||||
if (lastPeriod > 0) {
|
||||
pckgName = className.substring(0, lastPeriod);
|
||||
} else {
|
||||
pckgName = "";
|
||||
}
|
||||
|
||||
if (filter.isSearchPackage(pckgName)) {
|
||||
// get the class for our class name
|
||||
try {
|
||||
Class<?> theClass = Class.forName(className, false, classLoader);
|
||||
|
||||
if (matcher.isMatch(theClass)) {
|
||||
matchList.add(theClass);
|
||||
registerHit(jarFileName, theClass);
|
||||
}
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
// expected to get this hence trace
|
||||
logger.trace("Error searching classpath" + e.getMessage());
|
||||
} catch (NoClassDefFoundError e) {
|
||||
// expected to get this hence trace
|
||||
logger.trace("Error searching classpath" + e.getMessage());
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// expected to get this hence trace
|
||||
logger.trace("Error searching classpath" + e.getMessage());
|
||||
} catch (NoClassDefFoundError e) {
|
||||
// expected to get this hence trace
|
||||
logger.trace("Error searching classpath" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -498,6 +557,10 @@ public class ClassPathSearch implements ClassPathSearchService {
|
||||
String getJarNameWithOffset() {
|
||||
return (jarOffset == null) ? classPath.getName() : classPath.getName() + "!" + jarOffset;
|
||||
}
|
||||
|
||||
String getJarOffset() {
|
||||
return jarOffset;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -122,17 +122,19 @@ public class ClassPathSearchFilter {
|
||||
/**
|
||||
* Return true if the jar should be included in the search.
|
||||
*/
|
||||
public boolean isSearchJar(String jarName) {
|
||||
public boolean isSearchJar(String jarName, String jarOffset) {
|
||||
|
||||
if (containedIn(includeJarSet, jarName)) {
|
||||
return true;
|
||||
}
|
||||
if (containedIn(includeJarSet, jarName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (containedIn(excludeJarSet, jarName)) {
|
||||
return false;
|
||||
}
|
||||
return defaultJarMatch;
|
||||
}
|
||||
if (containedIn(excludeJarSet, jarName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// by default scan the root level in runnable jar (spring boot etc)
|
||||
return "/".equals(jarOffset) || defaultJarMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine is a match is contained in the set.
|
||||
|
||||
@@ -435,6 +435,15 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Between - for the given path in a JSON document.
|
||||
*/
|
||||
@Override
|
||||
public ExpressionList<T> jsonBetween(String propertyName, String path, Object lowerValue, Object upperValue){
|
||||
add(expr.jsonBetween(propertyName, path, lowerValue, upperValue));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> eq(String propertyName, Object value) {
|
||||
add(expr.eq(propertyName, value));
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.tests.model.converstation.User;
|
||||
import org.avaje.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestUnsetLoadedProperties extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testUnsetPropertyLoadedState() {
|
||||
|
||||
// populate db with a user
|
||||
User seed = new User();
|
||||
seed.setName("someName");
|
||||
seed.setEmail("some@junk.com");
|
||||
seed.save();
|
||||
|
||||
|
||||
// our bean to perform stateless update
|
||||
User user = new User();
|
||||
user.setId(seed.getId());
|
||||
user.setName("name mod");
|
||||
user.setEmail("change@junk.com");
|
||||
|
||||
|
||||
BeanState beanState = Ebean.getBeanState(user);
|
||||
assertThat(beanState.getLoadedProps()).containsExactly("id", "name", "email");
|
||||
|
||||
user.markPropertyUnset("email");
|
||||
|
||||
assertThat(beanState.getLoadedProps()).containsExactly("id", "name");
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
user.update();
|
||||
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
assertThat(loggedSql).hasSize(1);
|
||||
assertThat(loggedSql.get(0)).doesNotContain("email");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnloadVia_EntityBeanIntercept_setPropertyLoaded() {
|
||||
|
||||
// our bean to perform stateless update
|
||||
User user = new User();
|
||||
user.setId(42L);
|
||||
user.setName("name mod");
|
||||
user.setEmail("change@junk.com");
|
||||
|
||||
BeanState beanState = Ebean.getBeanState(user);
|
||||
assertThat(beanState.getLoadedProps()).containsExactly("id", "name", "email");
|
||||
|
||||
// unset the loaded state for email
|
||||
((EntityBean)user)._ebean_getIntercept().setPropertyLoaded("email", false);
|
||||
|
||||
assertThat(beanState.getLoadedProps()).containsExactly("id", "name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnloadVia_Model_markPropertyUnset() {
|
||||
|
||||
// our bean to perform stateless update
|
||||
User user = new User();
|
||||
user.setId(42L);
|
||||
user.setName("name mod");
|
||||
user.setEmail("change@junk.com");
|
||||
|
||||
BeanState beanState = Ebean.getBeanState(user);
|
||||
assertThat(beanState.getLoadedProps()).containsExactly("id", "name", "email");
|
||||
|
||||
|
||||
user.markPropertyUnset("email");
|
||||
assertThat(beanState.getLoadedProps()).containsExactly("id", "name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnloadVia_BeanState_setPropertyLoaded() {
|
||||
|
||||
// our bean to perform stateless update
|
||||
User user = new User();
|
||||
user.setId(42L);
|
||||
user.setName("name mod");
|
||||
user.setEmail("change@junk.com");
|
||||
|
||||
BeanState beanState = Ebean.getBeanState(user);
|
||||
assertThat(beanState.getLoadedProps()).containsExactly("id", "name", "email");
|
||||
|
||||
Ebean.getBeanState(user).setPropertyLoaded("email", false);
|
||||
assertThat(beanState.getLoadedProps()).containsExactly("id", "name");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,4 +34,40 @@ public class DbConstraintNamingTest {
|
||||
assertThat(naming.normaliseTable("foo_bar]")).isEqualTo("foo_bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultToLower() throws Exception {
|
||||
assertThat(naming.normaliseTable("SCH.FOO_BAR]")).isEqualTo("foo_bar");
|
||||
|
||||
assertThat(naming.lowerTableName("SCH.FOO_BAR]")).isEqualTo("SCH.FOO_BAR]");
|
||||
assertThat(naming.lowerTableName("SCH.FOO_BAR")).isEqualTo("sch.foo_bar");
|
||||
assertThat(naming.lowerColumnName("SCH.FOO_BAR]")).isEqualTo("SCH.FOO_BAR]");
|
||||
assertThat(naming.lowerColumnName("SCH.FOO_BAR")).isEqualTo("sch.foo_bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoLowerCaseTable() throws Exception {
|
||||
|
||||
DbConstraintNaming naming = new DbConstraintNaming(false, true);
|
||||
assertThat(naming.normaliseTable("SCH.FOO_BAR]")).isEqualTo("FOO_BAR");
|
||||
assertThat(naming.normaliseColumn("SCH.FOO_BAR]")).isEqualTo("sch.foo_bar");
|
||||
assertThat(naming.lowerTableName("SCH.FOO_BAR]")).isEqualTo("SCH.FOO_BAR]");
|
||||
// table name not lowered
|
||||
assertThat(naming.lowerTableName("SCH.FOO_BAR")).isEqualTo("SCH.FOO_BAR");
|
||||
assertThat(naming.lowerColumnName("SCH.FOO_BAR]")).isEqualTo("SCH.FOO_BAR]");
|
||||
assertThat(naming.lowerColumnName("SCH.FOO_BAR")).isEqualTo("sch.foo_bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoLowerCaseColumn() throws Exception {
|
||||
|
||||
DbConstraintNaming naming = new DbConstraintNaming(true, false);
|
||||
assertThat(naming.normaliseTable("SCH.FOO_BAR]")).isEqualTo("foo_bar");
|
||||
assertThat(naming.normaliseColumn("SCH.FOO_BAR]")).isEqualTo("SCH.FOO_BAR");
|
||||
assertThat(naming.lowerTableName("SCH.FOO_BAR]")).isEqualTo("SCH.FOO_BAR]");
|
||||
assertThat(naming.lowerTableName("SCH.FOO_BAR")).isEqualTo("sch.foo_bar");
|
||||
assertThat(naming.lowerColumnName("SCH.FOO_BAR]")).isEqualTo("SCH.FOO_BAR]");
|
||||
// column name not lowered
|
||||
assertThat(naming.lowerColumnName("SCH.FOO_BAR")).isEqualTo("SCH.FOO_BAR");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,11 +31,13 @@ public class ServerConfigTest {
|
||||
Properties props = new Properties();
|
||||
props.setProperty("persistBatch", "INSERT");
|
||||
props.setProperty("persistBatchOnCascade", "INSERT");
|
||||
props.setProperty("dbuuid","binary");
|
||||
|
||||
serverConfig.loadFromProperties(props);
|
||||
|
||||
assertEquals(PersistBatch.INSERT, serverConfig.getPersistBatch());
|
||||
assertEquals(PersistBatch.INSERT, serverConfig.getPersistBatchOnCascade());
|
||||
assertEquals(ServerConfig.DbUuid.BINARY, serverConfig.getDbUuid());
|
||||
|
||||
serverConfig.setPersistBatch(PersistBatch.NONE);
|
||||
serverConfig.setPersistBatchOnCascade(PersistBatch.NONE);
|
||||
@@ -45,6 +47,7 @@ public class ServerConfigTest {
|
||||
props1.setProperty("ebean.persistBatchOnCascade", "ALL");
|
||||
|
||||
serverConfig.loadFromProperties(props1);
|
||||
serverConfig.loadTestProperties();
|
||||
|
||||
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch());
|
||||
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade());
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.avaje.ebeaninternal.server.lib;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ShutdownManagerTest extends BaseTestCase {
|
||||
|
||||
/**
|
||||
* Run this test manually.
|
||||
*/
|
||||
@Ignore
|
||||
@Test
|
||||
public void test_deregisterShutdownHook() {
|
||||
|
||||
Ebean.getDefaultServer();
|
||||
ShutdownManager.deregisterShutdownHook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run this test manually.
|
||||
*/
|
||||
@Ignore
|
||||
@Test
|
||||
public void test_noShutdownHook() {
|
||||
|
||||
System.setProperty("ebean.registerShutdownHook","false");
|
||||
Ebean.getDefaultServer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.PostgresPlatform;
|
||||
import com.avaje.ebeaninternal.server.core.BootupClasses;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class DefaultTypeManagerTest {
|
||||
|
||||
DefaultTypeManager typeManager;
|
||||
|
||||
public DefaultTypeManagerTest() {
|
||||
ServerConfig serverConfig = new ServerConfig();
|
||||
serverConfig.setDatabasePlatform(new PostgresPlatform());
|
||||
BootupClasses bootupClasses = new BootupClasses();
|
||||
typeManager = new DefaultTypeManager(serverConfig, bootupClasses);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isIntegerType() {
|
||||
|
||||
assertTrue(typeManager.isIntegerType("1"));
|
||||
assertTrue(typeManager.isIntegerType("0"));
|
||||
|
||||
assertFalse(typeManager.isIntegerType("A"));
|
||||
assertFalse(typeManager.isIntegerType("01"));
|
||||
assertFalse(typeManager.isIntegerType(" 01"));
|
||||
assertFalse(typeManager.isIntegerType(" 0"));
|
||||
assertFalse(typeManager.isIntegerType(" 1"));
|
||||
assertFalse(typeManager.isIntegerType(" A"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.tests.model.basic.UUOne;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class TestBinaryUUID extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
|
||||
|
||||
UUOne one0 = new UUOne();
|
||||
one0.setName("first one");
|
||||
|
||||
@@ -27,26 +27,31 @@ public class TestBinaryUUID extends BaseTestCase {
|
||||
|
||||
Ebean.save(one0);
|
||||
Ebean.save(one1);
|
||||
|
||||
|
||||
UUOne fetch0 = Ebean.find(UUOne.class, one0.getId());
|
||||
UUOne fetch1 = Ebean.find(UUOne.class, one1.getId());
|
||||
|
||||
Assert.assertEquals(one0.getId(), fetch0.getId());
|
||||
Assert.assertEquals(one0.getName(), fetch0.getName());
|
||||
|
||||
Assert.assertEquals(one1.getId(), fetch1.getId());
|
||||
Assert.assertEquals(one1.getName(), fetch1.getName());
|
||||
assertEquals(one0.getId(), fetch0.getId());
|
||||
assertEquals(one0.getName(), fetch0.getName());
|
||||
|
||||
assertEquals(one1.getId(), fetch1.getId());
|
||||
assertEquals(one1.getName(), fetch1.getName());
|
||||
|
||||
String sql = "select id, name from uuone";
|
||||
List<SqlRow> list = Ebean.createSqlQuery(sql).findList();
|
||||
for (SqlRow sqlRow : list) {
|
||||
Object sqlId = sqlRow.get("id");
|
||||
Assert.assertNotNull(sqlId);
|
||||
Assert.assertTrue(sqlId instanceof byte[]);
|
||||
assertNotNull(sqlId);
|
||||
UUID uuid = sqlRow.getUUID("id");
|
||||
Assert.assertNotNull(uuid);
|
||||
assertNotNull(uuid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
String asJson = Ebean.json().toJson(fetch0);
|
||||
UUOne bean = Ebean.json().toBean(UUOne.class, asJson);
|
||||
|
||||
assertEquals(fetch0.getId(), bean.getId());
|
||||
assertEquals(fetch0.getName(), bean.getName());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.tests.basic.type;
|
||||
|
||||
import com.avaje.tests.model.basic.EBasicEnumInt;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -11,6 +12,9 @@ import com.avaje.tests.model.basic.EBasic;
|
||||
import com.avaje.tests.model.basic.EBasic.Status;
|
||||
import com.avaje.tests.model.basic.EBasicEnumId;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class TestEnumValueAnnotation extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
@@ -28,7 +32,7 @@ public class TestEnumValueAnnotation extends BaseTestCase {
|
||||
SqlRow sqlRow = q.findUnique();
|
||||
String strStatus = sqlRow.getString("status");
|
||||
|
||||
Assert.assertEquals("N", strStatus);
|
||||
assertEquals("N", strStatus);
|
||||
|
||||
EBasic b2 = new EBasic();
|
||||
b2.setName("Apple");
|
||||
@@ -40,9 +44,9 @@ public class TestEnumValueAnnotation extends BaseTestCase {
|
||||
b3.setName("Orange");
|
||||
|
||||
Ebean.save(b3);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsId() {
|
||||
EBasicEnumId b = new EBasicEnumId();
|
||||
b.setName("Banana");
|
||||
@@ -56,7 +60,7 @@ public class TestEnumValueAnnotation extends BaseTestCase {
|
||||
SqlRow sqlRow = q.findUnique();
|
||||
String strStatus = sqlRow.getString("status");
|
||||
|
||||
Assert.assertEquals("N", strStatus);
|
||||
assertEquals("N", strStatus);
|
||||
|
||||
try {
|
||||
b = Ebean.find(EBasicEnumId.class, b.getStatus());
|
||||
@@ -64,7 +68,32 @@ public class TestEnumValueAnnotation extends BaseTestCase {
|
||||
Assert.fail("The use of an enum as id should work : " + iae.getLocalizedMessage());
|
||||
}
|
||||
|
||||
Assert.assertEquals(EBasicEnumId.Status.NEW, b.getStatus());
|
||||
assertEquals(EBasicEnumId.Status.NEW, b.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDbEnumValueInt() {
|
||||
|
||||
EBasicEnumInt b = new EBasicEnumInt();
|
||||
b.setName("Banana");
|
||||
b.setStatus(EBasicEnumInt.Status.NEW);
|
||||
|
||||
Ebean.save(b);
|
||||
|
||||
SqlQuery q = Ebean.createSqlQuery("select * from e_basic_eni where id = :id");
|
||||
q.setParameter("id", b.getId());
|
||||
|
||||
SqlRow sqlRow = q.findUnique();
|
||||
Integer intStatus = sqlRow.getInteger("status");
|
||||
|
||||
assertEquals(Integer.valueOf(1), intStatus);
|
||||
|
||||
|
||||
EBasicEnumInt b2 = Ebean.find(EBasicEnumInt.class)
|
||||
.where().eq("id", b.getId())
|
||||
.eq("status", EBasicEnumInt.Status.NEW)
|
||||
.findUnique();
|
||||
|
||||
assertNotNull(b2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package com.avaje.tests.cascade;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.tests.model.basic.TSDetail;
|
||||
import com.avaje.tests.model.basic.TSMaster;
|
||||
import org.avaje.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TestPrivateOwned extends BaseTestCase {
|
||||
|
||||
@@ -33,9 +36,9 @@ public class TestPrivateOwned extends BaseTestCase {
|
||||
BeanCollection<?> bc = (BeanCollection<?>) details;
|
||||
Set<?> modifyRemovals = bc.getModifyRemovals();
|
||||
|
||||
Assert.assertNotNull(modifyRemovals);
|
||||
Assert.assertTrue(modifyRemovals.size() == 1);
|
||||
Assert.assertTrue(modifyRemovals.contains(removedDetail));
|
||||
assertNotNull(modifyRemovals);
|
||||
assertTrue(modifyRemovals.size() == 1);
|
||||
assertTrue(modifyRemovals.contains(removedDetail));
|
||||
|
||||
Ebean.save(master);
|
||||
|
||||
@@ -43,11 +46,42 @@ public class TestPrivateOwned extends BaseTestCase {
|
||||
List<TSDetail> detailsReload = masterReload.getDetails();
|
||||
|
||||
// the removed bean has really been removed
|
||||
Assert.assertTrue(detailsReload.size() == 1);
|
||||
assertTrue(detailsReload.size() == 1);
|
||||
|
||||
TSMaster master3 = Ebean.find(TSMaster.class, m0.getId());
|
||||
List<TSDetail> details3 = master3.getDetails();
|
||||
details3.clear();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void insertThenUpdate() {
|
||||
|
||||
TSMaster m0 = new TSMaster();
|
||||
m0.setName("m2");
|
||||
|
||||
m0.addDetail(new TSDetail("m2 detail 1"));
|
||||
m0.addDetail(new TSDetail("m2 detail 2"));
|
||||
|
||||
Ebean.save(m0);
|
||||
assertThat(m0.getDetails()).hasSize(2);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
m0.getDetails().remove(0);
|
||||
Ebean.save(m0);
|
||||
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
assertThat(loggedSql).hasSize(1);
|
||||
assertThat(loggedSql.get(0)).contains("delete from t_detail_with_other_namexxxyy where id=?");
|
||||
|
||||
TSMaster masterReload = Ebean.find(TSMaster.class, m0.getId());
|
||||
assertThat(masterReload.getDetails()).hasSize(1);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
Ebean.save(m0);
|
||||
loggedSql = LoggedSqlCollector.stop();
|
||||
assertThat(loggedSql).hasSize(0);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.tests.model.basic.Contact;
|
||||
import com.avaje.tests.model.basic.Customer;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
import org.avaje.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -49,4 +51,29 @@ public class TestDeleteByQuery extends BaseTestCase {
|
||||
assertThat(list).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommit() {
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
if (server.getName().equals("mysql")) {
|
||||
// MySql does not the sub query selecting from the delete table
|
||||
return;
|
||||
}
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
List<Customer> all = Customer.find.all();
|
||||
Contact contact = new Contact();
|
||||
contact.setFirstName("DelByQueryFirstName");
|
||||
contact.setLastName("deleteMe");
|
||||
contact.setCustomer(all.get(0));
|
||||
|
||||
Ebean.save(contact);
|
||||
|
||||
Ebean.find(Contact.class).select("id").where().eq("firstName", "DelByQueryFirstName").delete();
|
||||
|
||||
Contact contactFind = Ebean.find(Contact.class, contact.getId());
|
||||
assertThat(contactFind).isNull();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package com.avaje.tests.inheritance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
@@ -13,6 +8,11 @@ import com.avaje.tests.model.basic.Animal;
|
||||
import com.avaje.tests.model.basic.AnimalShelter;
|
||||
import com.avaje.tests.model.basic.Cat;
|
||||
import com.avaje.tests.model.basic.Dog;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class TestInheritanceOnMany extends BaseTestCase {
|
||||
|
||||
@@ -40,8 +40,9 @@ public class TestInheritanceOnMany extends BaseTestCase {
|
||||
BeanList<?> beanList = (BeanList<?>)animals;
|
||||
ModifyListenMode modifyListenMode = beanList.getModifyListenMode();
|
||||
|
||||
Assert.assertNotNull(modifyListenMode);
|
||||
|
||||
assertNotNull(modifyListenMode);
|
||||
|
||||
assertNotNull(Ebean.find(Animal.class).findList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.avaje.tests.iud;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.info.InfoCompany;
|
||||
import com.avaje.tests.model.info.InfoCustomer;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class TestInfoOneToOne extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
@@ -20,13 +24,17 @@ public class TestInfoOneToOne extends BaseTestCase {
|
||||
|
||||
customer.save();
|
||||
|
||||
// assert JsonIgnore working as expected
|
||||
String asJson = Ebean.json().toJson(company);
|
||||
assertThat(asJson).doesNotContain("contacts");
|
||||
|
||||
// assert both are inserted
|
||||
Assert.assertNotNull(customer.getId());
|
||||
Assert.assertNotNull(company.getId());
|
||||
assertNotNull(customer.getId());
|
||||
assertNotNull(company.getId());
|
||||
|
||||
// both can be fetched
|
||||
Assert.assertNotNull(InfoCustomer.find.byId(customer.getId()));
|
||||
Assert.assertNotNull(InfoCompany.find.byId(company.getId()));
|
||||
assertNotNull(InfoCustomer.find.byId(customer.getId()));
|
||||
assertNotNull(InfoCompany.find.byId(company.getId()));
|
||||
|
||||
|
||||
// just update the customer
|
||||
@@ -47,8 +55,8 @@ public class TestInfoOneToOne extends BaseTestCase {
|
||||
|
||||
// delete both customer and company
|
||||
fetchedCustomer.delete();
|
||||
Assert.assertNull(InfoCustomer.find.byId(customer.getId()));
|
||||
Assert.assertNull(InfoCompany.find.byId(company.getId()));
|
||||
assertNull(InfoCustomer.find.byId(customer.getId()));
|
||||
assertNull(InfoCompany.find.byId(company.getId()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.avaje.tests.m2m;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.SqlQuery;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.tests.model.basic.MnocRole;
|
||||
import com.avaje.tests.model.basic.MnocUser;
|
||||
import com.avaje.tests.model.m2m.MnyA;
|
||||
import com.avaje.tests.model.m2m.MnyB;
|
||||
import com.avaje.tests.model.m2m.MnyC;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class TestM2MDeleteCascadeFromParent extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
MnyC c0 = new MnyC();
|
||||
c0.setName("c0");
|
||||
c0.save();
|
||||
|
||||
MnyA a0 = new MnyA();
|
||||
a0.setName("a0");
|
||||
a0.save();
|
||||
|
||||
MnyB b0 = new MnyB();
|
||||
b0.setName("b0");
|
||||
b0.setA(a0);
|
||||
b0.save();
|
||||
|
||||
c0.getBs().add(b0);
|
||||
Ebean.saveAssociation(c0, "bs");
|
||||
|
||||
SqlQuery sqlQuery = Ebean.createSqlQuery("select count(*) as count from mny_b_mny_c");
|
||||
SqlRow unique = sqlQuery.findUnique();
|
||||
assertEquals(Long.valueOf(1), unique.getLong("count"));
|
||||
|
||||
|
||||
//Ebean.deleteManyToManyAssociations(b0, "cs");
|
||||
a0.delete();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.avaje.tests.m2m;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.m2m.MailBox;
|
||||
import com.avaje.tests.model.m2m.MailUser;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class TestM2MMultipleLists extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
MailUser u = new MailUser();
|
||||
u.setName("mailUser1");
|
||||
|
||||
MailBox m1 = new MailBox();
|
||||
m1.setName("mailBox1");
|
||||
|
||||
u.getInbox().add(m1);
|
||||
Ebean.save(u);
|
||||
|
||||
u = Ebean.find(MailUser.class, u.getId());
|
||||
|
||||
assertEquals(1, u.getInbox().size());
|
||||
assertEquals(0, u.getOutbox().size());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.avaje.tests.m2m;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.m2m.MnyTopic;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Added to test DDL generation for ManyToMany related back to itself.
|
||||
*/
|
||||
public class TestM2MSelfRelationship extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
Ebean.getDefaultServer();
|
||||
|
||||
// Create 2 roles r0 and r1
|
||||
MnyTopic r0 = new MnyTopic("r0");
|
||||
MnyTopic r1 = new MnyTopic("r1");
|
||||
|
||||
// Save r1 and r2
|
||||
Ebean.save(r0);
|
||||
Ebean.save(r1);
|
||||
|
||||
r0.getSubTopics().add(r1);
|
||||
Ebean.save(r0);
|
||||
}
|
||||
}
|
||||
@@ -68,4 +68,28 @@ public class TestM2mDeleteObject extends BaseTestCase {
|
||||
Assert.assertEquals(2, permissionList2.size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test_deleteCascading() {
|
||||
|
||||
Permission p1 = new Permission();
|
||||
Permission p2 = new Permission();
|
||||
p1.setName("p1");
|
||||
p2.setName("p2");
|
||||
Ebean.save(p1);
|
||||
Ebean.save(p2);
|
||||
|
||||
Role role1 = new Role();
|
||||
role1.setName("role");
|
||||
|
||||
Set<Permission> permissions = new HashSet<Permission>();
|
||||
permissions.add(p2);
|
||||
role1.setPermissions(permissions);
|
||||
Ebean.save(role1);
|
||||
|
||||
// only deletes as not associated
|
||||
Ebean.delete(p1);
|
||||
// delete cascades
|
||||
Ebean.delete(role1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.tests.model.basic;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.DiscriminatorColumn;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
@@ -21,6 +22,9 @@ public abstract class Animal {
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
@Column(name="species", insertable = false, updatable = false, nullable = false)
|
||||
String species;
|
||||
|
||||
@ManyToOne
|
||||
AnimalShelter shelter;
|
||||
|
||||
@@ -40,6 +44,14 @@ public abstract class Animal {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getSpecies() {
|
||||
return species;
|
||||
}
|
||||
|
||||
public void setSpecies(String species) {
|
||||
this.species = species;
|
||||
}
|
||||
|
||||
public AnimalShelter getShelter() {
|
||||
return shelter;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.avaje.tests.model.basic;
|
||||
|
||||
import com.avaje.ebean.annotation.ChangeLog;
|
||||
import com.avaje.ebean.annotation.ChangeLogInsertMode;
|
||||
import com.avaje.ebean.annotation.EnumValue;
|
||||
import com.avaje.ebean.annotation.DbEnumValue;
|
||||
import com.avaje.ebean.annotation.JsonIgnore;
|
||||
import com.avaje.ebean.annotation.Where;
|
||||
import com.avaje.tests.model.basic.finder.CustomerFinder;
|
||||
@@ -36,14 +36,19 @@ public class Customer extends BasicDomain {
|
||||
* EnumValue is an Ebean specific mapping for enums.
|
||||
*/
|
||||
public enum Status {
|
||||
@EnumValue("N")
|
||||
NEW,
|
||||
NEW("N"),
|
||||
ACTIVE("A"),
|
||||
INACTIVE("I");
|
||||
|
||||
@EnumValue("A")
|
||||
ACTIVE,
|
||||
String dbValue;
|
||||
Status(String dbValue) {
|
||||
this.dbValue = dbValue;
|
||||
}
|
||||
|
||||
@EnumValue("I")
|
||||
INACTIVE
|
||||
@DbEnumValue
|
||||
public String getValue() {
|
||||
return dbValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Transient
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.avaje.tests.model.basic;
|
||||
|
||||
import com.avaje.ebean.annotation.DbEnumType;
|
||||
import com.avaje.ebean.annotation.DbEnumValue;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@Entity
|
||||
@Table(name = "e_basic_eni")
|
||||
public class EBasicEnumInt {
|
||||
|
||||
public enum Status {
|
||||
NEW("1"),
|
||||
ACTIVE("2"),
|
||||
INACTIVE("3");
|
||||
|
||||
String value;
|
||||
Status(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
@DbEnumValue(storage = DbEnumType.INTEGER)
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Id
|
||||
Integer id;
|
||||
|
||||
Status status;
|
||||
|
||||
String name;
|
||||
|
||||
String description;
|
||||
|
||||
Timestamp someDate;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Timestamp getSomeDate() {
|
||||
return someDate;
|
||||
}
|
||||
|
||||
public void setSomeDate(Timestamp someDate) {
|
||||
this.someDate = someDate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package com.avaje.tests.model.basic;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.TxRunnable;
|
||||
import com.avaje.tests.model.basic.Order.Status;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ResetBasicData {
|
||||
|
||||
private static boolean runOnce;
|
||||
@@ -135,6 +135,7 @@ public class ResetBasicData {
|
||||
createOrder2(cust2);
|
||||
createOrder3(cust1);
|
||||
createOrder4(cust1);
|
||||
createOrder5(cust2);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -313,4 +314,13 @@ public class ResetBasicData {
|
||||
|
||||
Ebean.save(order);
|
||||
}
|
||||
|
||||
private void createOrder5(Customer customer) {
|
||||
|
||||
Order order = new Order();
|
||||
order.setCustomer(customer);
|
||||
order.addShipment(new OrderShipment());
|
||||
|
||||
Ebean.save(order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.tests.model.info;
|
||||
|
||||
import com.avaje.ebean.Model;
|
||||
import com.avaje.ebean.annotation.JsonIgnore;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
@@ -19,6 +20,7 @@ public class InfoCompany extends Model {
|
||||
|
||||
String name;
|
||||
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "company", cascade = CascadeType.ALL)
|
||||
List<InfoContact> contacts = new ArrayList<InfoContact>();
|
||||
|
||||
|
||||
@@ -2,7 +2,11 @@ package com.avaje.tests.model.info;
|
||||
|
||||
import com.avaje.ebean.Model;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Version;
|
||||
|
||||
@Entity
|
||||
public class InfoCustomer extends Model {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.avaje.tests.model.m2m;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
|
||||
@Entity
|
||||
public class MailBox {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
String name;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.avaje.tests.model.m2m;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Version;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
public class MailUser {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
String name;
|
||||
|
||||
@ManyToMany(cascade = CascadeType.ALL)
|
||||
@JoinTable(name = "mail_user_inbox")
|
||||
List<MailBox> inbox;
|
||||
|
||||
@ManyToMany(cascade = CascadeType.ALL)
|
||||
@JoinTable(name = "mail_user_outbox")
|
||||
List<MailBox> outbox;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<MailBox> getInbox() {
|
||||
return inbox;
|
||||
}
|
||||
|
||||
public void setInbox(List<MailBox> inbox) {
|
||||
this.inbox = inbox;
|
||||
}
|
||||
|
||||
public List<MailBox> getOutbox() {
|
||||
return outbox;
|
||||
}
|
||||
|
||||
public void setOutbox(List<MailBox> outbox) {
|
||||
this.outbox = outbox;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.avaje.tests.model.m2m;
|
||||
|
||||
import com.avaje.tests.model.BaseModel;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
public class MnyA extends BaseModel {
|
||||
|
||||
String name;
|
||||
|
||||
@OneToMany(mappedBy = "a", cascade = CascadeType.REMOVE)
|
||||
List<MnyB> bs;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<MnyB> getBs() {
|
||||
return bs;
|
||||
}
|
||||
|
||||
public void setBs(List<MnyB> bs) {
|
||||
this.bs = bs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.avaje.tests.model.m2m;
|
||||
|
||||
import com.avaje.tests.model.BaseModel;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
public class MnyB extends BaseModel {
|
||||
|
||||
String name;
|
||||
|
||||
@ManyToOne
|
||||
MnyA a;
|
||||
|
||||
@ManyToMany(cascade = CascadeType.REMOVE)
|
||||
List<MnyC> cs;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public MnyA getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA(MnyA a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public List<MnyC> getCs() {
|
||||
return cs;
|
||||
}
|
||||
|
||||
public void setCs(List<MnyC> cs) {
|
||||
this.cs = cs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.avaje.tests.model.m2m;
|
||||
|
||||
import com.avaje.tests.model.BaseModel;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToMany;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
public class MnyC extends BaseModel {
|
||||
|
||||
String name;
|
||||
|
||||
@ManyToMany(mappedBy = "cs")
|
||||
List<MnyB> bs;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<MnyB> getBs() {
|
||||
return bs;
|
||||
}
|
||||
|
||||
public void setBs(List<MnyB> bs) {
|
||||
this.bs = bs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.avaje.tests.model.m2m;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Version;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
public class MnyTopic {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(name = "subtopics",
|
||||
joinColumns = @JoinColumn(name = "topic", referencedColumnName = "id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "subtopic", referencedColumnName = "id"))
|
||||
List<MnyTopic> subTopics;
|
||||
|
||||
public MnyTopic() {
|
||||
|
||||
}
|
||||
|
||||
public MnyTopic(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public List<MnyTopic> getSubTopics() {
|
||||
return subTopics;
|
||||
}
|
||||
|
||||
public void setSubTopics(List<MnyTopic> subTopics) {
|
||||
this.subTopics = subTopics;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.avaje.tests.query;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.QueryIterator;
|
||||
import com.avaje.tests.model.basic.Customer;
|
||||
import com.avaje.tests.model.basic.Order;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class TestOneToManyCorrectGrouping extends BaseTestCase {
|
||||
|
||||
public static final int EXPECTED_ITERATIONS = 2;
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
Query<Customer> customerQuery = Ebean.find(Customer.class)
|
||||
.fetch("orders")
|
||||
.where().le("id", 2)
|
||||
.query();
|
||||
|
||||
QueryIterator<Customer> customerQueryIterator = customerQuery.findIterate();
|
||||
|
||||
try {
|
||||
int count = 0;
|
||||
while (customerQueryIterator.hasNext()) {
|
||||
Customer customer = customerQueryIterator.next();
|
||||
System.out.println(String.format("Customer id %s", customer.getId()));
|
||||
for (Order order : customer.getOrders()) {
|
||||
System.out.println(String.format("--> Order id %s", order.getId()));
|
||||
}
|
||||
count++;
|
||||
}
|
||||
assertEquals(EXPECTED_ITERATIONS, count);
|
||||
|
||||
} finally {
|
||||
customerQueryIterator.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,4 +110,27 @@ public class TestQueryFindPagedList extends BaseTestCase {
|
||||
Ebean.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test_usingAlias() throws ExecutionException, InterruptedException {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
PagedList<Order> pagedList = Ebean.find(Order.class)
|
||||
.alias("b")
|
||||
.where().raw("b.id > 0")
|
||||
.findPagedList(0, 6);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
pagedList.getTotalRowCount();
|
||||
pagedList.getList();
|
||||
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
|
||||
assertEquals(2, loggedSql.size());
|
||||
assertThat(loggedSql.get(0)).contains("select count(*) from o_order b where b.id > 0");
|
||||
assertThat(loggedSql.get(1)).contains("select b.id c0, b.status c1, b.order_date c2");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.avaje.tests.update;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.SqlQuery;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.tests.model.basic.Customer;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class TestUpdatePartial extends BaseTestCase {
|
||||
|
||||
@@ -18,6 +20,7 @@ public class TestUpdatePartial extends BaseTestCase {
|
||||
c.setSmallnote("a note");
|
||||
|
||||
Ebean.save(c);
|
||||
checkDbStatusValue(c.getId(), "A");
|
||||
|
||||
Customer c2 = Ebean.find(Customer.class)
|
||||
.select("status, smallnote")
|
||||
@@ -28,6 +31,7 @@ public class TestUpdatePartial extends BaseTestCase {
|
||||
c2.setSmallnote("2nd note");
|
||||
|
||||
Ebean.save(c2);
|
||||
checkDbStatusValue(c.getId(), "I");
|
||||
|
||||
Customer c3 = Ebean.find(Customer.class)
|
||||
.select("status")
|
||||
@@ -38,7 +42,15 @@ public class TestUpdatePartial extends BaseTestCase {
|
||||
c3.setSmallnote("3rd note");
|
||||
|
||||
Ebean.save(c3);
|
||||
checkDbStatusValue(c.getId(), "N");
|
||||
}
|
||||
|
||||
private void checkDbStatusValue(Integer custId, String dbStatus) {
|
||||
SqlQuery sqlQuery = Ebean.createSqlQuery("select id, status from o_customer where id = ?");
|
||||
sqlQuery.setParameter(1, custId);
|
||||
SqlRow sqlRow = sqlQuery.findUnique();
|
||||
String status = sqlRow.getString("status");
|
||||
assertEquals(dbStatus, status);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,6 +69,6 @@ public class TestUpdatePartial extends BaseTestCase {
|
||||
Ebean.save(customerWithoutChanges);
|
||||
|
||||
// assert
|
||||
Assert.assertEquals(customer.getUpdtime().getTime(), customerWithoutChanges.getUpdtime().getTime());
|
||||
assertEquals(customer.getUpdtime().getTime(), customerWithoutChanges.getUpdtime().getTime());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
datasource.h2.username=sa
|
||||
Reference in New Issue
Block a user