Compare commits

...
Author SHA1 Message Date
rob bygrave eac68166d9 [maven-release-plugin] prepare release ebean-11.18.6 2018-07-18 22:04:48 +12:00
rob bygrave cb0023345f Refactor scalar types for Varchar, LongVarchar and Clob - ScalarTypeStringBase 2018-07-18 21:59:11 +12:00
rob bygrave f0f7feb125 No effective change - code format 2018-07-18 21:49:19 +12:00
Roland PramlandRob Bygrave c8e32253a5 FIX: support for ScalarTypeConverter and @Lob (#1454) 2018-07-18 21:41:46 +12:00
rob bygrave 01541bb208 #1457 - Issue with @ManyToOne with @EmbeddedId where only some columns are insertable or updatable 2018-07-18 21:29:08 +12:00
rob bygrave 2d44d06f36 #1456 - io.ebean.annotation.NotNull not honored by @ManyToOne property 2018-07-18 19:36:41 +12:00
rob bygrave fda930a162 No effective change - fix javadoc title to Ebean 11 2018-07-18 17:01:14 +12:00
Roland PramlandRob Bygrave 5a6cdb9a86 FIX: null handling in ScalarType.toBeanType (#1455) 2018-07-18 16:33:36 +12:00
rob bygrave 5ef7fe8504 [maven-release-plugin] prepare for next development iteration 2018-07-15 16:09:15 +12:00
rob bygrave 11586fb635 [maven-release-plugin] prepare release ebean-11.18.5 2018-07-15 16:09:00 +12:00
rob bygrave d555122067 #1453 - @Formula property included in DDL when on @Inheritance bean and without @Transient 2018-07-15 15:36:29 +12:00
rob bygrave b6d3dc3516 Fix test code only - Tune entity bean needs at least 1 non Id property 2018-07-15 15:01:17 +12:00
rob bygrave bbf54d6196 git push origin masterMerge branch 'FOCONIS-cache-bug-delete' 2018-07-15 14:13:48 +12:00
rob bygrave 74c88ce511 #1452 Fix - Deleting a bean does not invalidate the bean cache (Regression from 11.18.1 - introduced by changes in #1427) 2018-07-15 14:13:29 +12:00
rob bygrave 309bf6a11a Merge branch 'cache-bug-delete' of https://github.com/FOCONIS/ebean into FOCONIS-cache-bug-delete 2018-07-15 13:56:37 +12:00
rob bygrave 02b6ab0f74 No effective change, move null check on orderBy to prior to parse 2018-07-15 13:55:51 +12:00
rob bygrave 0c41993ce7 No effective change, whitespace 2018-07-15 13:55:15 +12:00
rob bygrave d949c48245 git push origin masterMerge branch 'tobias--bugfix/lazy_fetch_with_id_column_name' 2018-07-15 13:52:14 +12:00
rob bygrave 93e603ca25 #1449 Fix for failing lazy fetch when logical property name starts with underscore 2018-07-15 13:49:05 +12:00
rob bygrave 117d22fd67 Merge branch 'bugfix/lazy_fetch_with_id_column_name' of https://github.com/tobias-/ebean into tobias--bugfix/lazy_fetch_with_id_column_name 2018-07-15 13:11:33 +12:00
Roland Praml f13de2c8a6 ADD: Testcase for delete with cache 2018-07-13 19:57:56 +02:00
Mitsch164andRob Bygrave 5b8ef8154a Fix Transaction Flushing when ID Property is Loaded (#1450)
* Fix for unintentianal flush when ID is loaded

* fix duplicateKey Exception

* higher id
2018-07-12 15:10:17 +12:00
Tobias df892f4509 Failing testcase with lazy fetch
For some reason, all entities with an id starting with _ fails when
lazy-fetching due to sort not being properly renamed.
2018-07-11 15:52:56 +02:00
rob bygrave 2755e6d1ec [maven-release-plugin] prepare for next development iteration 2018-07-05 23:40:12 +12:00
rob bygrave 2024193da9 [maven-release-plugin] prepare release ebean-11.18.4 2018-07-05 23:39:56 +12:00
rob bygrave 72788c7428 No effective change - tidy test code 2018-07-05 22:41:28 +12:00
rob bygrave 8789709fb8 No effective change - tidy test code 2018-07-05 22:40:09 +12:00
rob bygrave 588aaf9361 #1447 - Delete query ... should honour cascading delete if defined
See also PR #1445 with the failing test case
2018-07-05 22:37:54 +12:00
Michael BenzandRob Bygrave 68649d0e98 Failing unit test to demonstrate the bug that deleting an entity via … (#1445)
* Failing unit test to demonstrate the bug that deleting an entity via an Ebean query does not respect the cascade setting.
(here a mapped optional OneToOne entity is not deleted using an Ebean query even if the CascadeType.ALL was used)

* moved init and cleanup code to @Before and @After JUnit methods and added another test that shows Ebean.delete() works as expected
2018-07-05 19:52:40 +12:00
rob bygrave f6047d3b73 [maven-release-plugin] prepare for next development iteration 2018-07-04 22:09:27 +12:00
66 changed files with 1352 additions and 316 deletions
+3 -3
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.18.3</version>
<version>11.18.6</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.18.3</tag>
<tag>ebean-11.18.6</tag>
</scm>
<profiles>
@@ -360,7 +360,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<doctitle>Ebean 10</doctitle>
<doctitle>Ebean 11</doctitle>
<overview>src/main/java/io/ebean/overview.html</overview>
<source>1.8</source>
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
@@ -246,6 +246,9 @@ public class PlatformDdl {
* Convert the standard type to the platform specific type.
*/
public String convert(String type, boolean identity) {
if (type == null) {
return null;
}
if (type.contains("[]")) {
return convertArrayType(type);
}
@@ -133,8 +133,7 @@ public class VisitAllUsing {
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && inheritInfo.isRoot()) {
// add all properties on the children objects
InheritChildVisitor childVisitor = new InheritChildVisitor(this, pv);
inheritInfo.visitChildren(childVisitor);
inheritInfo.visitChildren(new InheritChildVisitor(this, pv));
}
}
@@ -155,9 +154,10 @@ public class VisitAllUsing {
@Override
public void visit(InheritInfo inheritInfo) {
BeanProperty[] propertiesLocal = inheritInfo.desc().propertiesLocal();
for (BeanProperty aPropertiesLocal : propertiesLocal) {
owner.visit(pv, aPropertiesLocal);
for (BeanProperty beanProperty : inheritInfo.desc().propertiesLocal()) {
if (beanProperty.isDDLColumn()) {
owner.visit(pv, beanProperty);
}
}
}
}
@@ -1384,7 +1384,18 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
try {
request.initTransIfRequired();
request.markNotQueryOnly();
return request.delete();
if (request.isDeleteByStatement()) {
return request.delete();
} else {
// escalate to fetch the ids of the beans to delete due
// to cascading deletes or l2 caching etc
List<Object> ids = request.findIds();
if (ids.isEmpty()) {
return 0;
} else {
return persister.deleteByIds(request.getBeanDescriptor(), ids, request.getTransaction(), false);
}
}
} finally {
request.endTransIfRequired();
}
@@ -116,6 +116,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
}
}
@Override
public boolean isDeleteByStatement() {
return beanDescriptor.isDeleteByStatement();
}
@Override
public boolean isMultiValueIdSupported() {
return beanDescriptor.isMultiValueIdSupported();
@@ -356,7 +356,15 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private boolean flushBatchOnGetter(int propertyIndex) {
// propertyIndex of -1 the Id property, no flush for get Id on UPDATE
return propertyIndex == -1 ? type == Type.INSERT : beanDescriptor.isGeneratedProperty(propertyIndex);
if (propertyIndex == -1) {
if (beanDescriptor.isIdLoaded(intercept)) {
return false;
} else {
return type == Type.INSERT;
}
} else {
return beanDescriptor.isGeneratedProperty(propertyIndex);
}
}
public void setSkipBatchForTopLevel() {
@@ -63,6 +63,11 @@ public interface Persister {
*/
int deleteMany(Class<?> beanType, Collection<?> ids, Transaction transaction, boolean permanent);
/**
* Delete multiple beans when escalated from a delete query.
*/
int deleteByIds(BeanDescriptor<?> descriptor, List<Object> idList, Transaction transaction, boolean permanent);
/**
* Execute the Update.
*/
@@ -171,4 +171,9 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequ
* Set profile location for "find all" if not set.
*/
void profileLocationAll();
/**
* Return true if delete by statement is allowed for this type given cascade rules etc.
*/
boolean isDeleteByStatement();
}
@@ -3160,6 +3160,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
public boolean hasIdPropertyOnly(EntityBeanIntercept ebi) {
return ebi.hasIdOnly(idPropertyIndex);
}
public boolean isIdLoaded(EntityBeanIntercept ebi) {
return ebi.isLoadedProperty(idPropertyIndex);
}
public boolean hasIdValue(EntityBean bean) {
return (idProperty != null && !DmlUtil.isNullOrZero(idProperty.getValue(bean)));
@@ -739,6 +739,7 @@ final class BeanDescriptorCacheHelp<T> {
if (invalidateQueryCache) {
changeSet.addInvalidate(desc);
} else {
queryCacheClear(changeSet);
if (beanCache != null) {
changeSet.addBeanRemoveMany(desc, ids);
}
@@ -357,14 +357,34 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
* Return true if this association is updateable.
*/
public boolean isUpdateable() {
return tableJoin.columns().length <= 0 || tableJoin.columns()[0].isUpdateable();
TableJoinColumn[] columns = tableJoin.columns();
if (columns.length <= 0) {
return true;
}
for (TableJoinColumn column : columns) {
if (column.isUpdateable()) {
// at least 1 is updatable
return true;
}
}
return false;
}
/**
* Return true if this association is insertable.
*/
public boolean isInsertable() {
return tableJoin.columns().length <= 0 || tableJoin.columns()[0].isInsertable();
TableJoinColumn[] columns = tableJoin.columns();
if (columns.length <= 0) {
return true;
}
for (TableJoinColumn column : columns) {
if (column.isInsertable()) {
// at least 1 is insertable
return true;
}
}
return false;
}
/**
@@ -443,16 +463,18 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
String matchColumn = col.getForeignDbColumn();
String localColumn = col.getLocalDbColumn();
String localSqlFormula = col.getLocalSqlFormula();
boolean insertable = col.isInsertable();
boolean updateable = col.isUpdateable();
for (int j = 0; j < props.length; j++) {
if (props[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
return new ImportedIdSimple(owner, localColumn, localSqlFormula, props[j], j);
return new ImportedIdSimple(owner, localColumn, localSqlFormula, props[j], j, insertable, updateable);
}
}
for (int j = 0; j < others.length; j++) {
if (others[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
return new ImportedIdSimple(owner, localColumn, localSqlFormula, others[j], j + props.length);
return new ImportedIdSimple(owner, localColumn, localSqlFormula, others[j], j + props.length, insertable, updateable);
}
}
@@ -187,6 +187,6 @@ public abstract class DeployParser {
}
private boolean isWordStart(char ch) {
return Character.isLetter(ch);
return Character.isLetter(ch) || ch == UNDERSCORE;
}
}
@@ -64,8 +64,11 @@ public class ImportedIdEmbedded implements ImportedId {
@Override
public void dmlAppend(GenerateDmlRequest request) {
boolean update = request.isUpdate();
for (ImportedIdSimple anImported : imported) {
request.appendColumn(anImported.localDbColumn);
if (anImported.isInclude(update)) {
request.appendColumn(anImported.localDbColumn);
}
}
}
@@ -100,30 +103,28 @@ public class ImportedIdEmbedded implements ImportedId {
@Override
public Object bind(BindableRequest request, EntityBean bean) throws SQLException {
Object embeddedId = null;
if (bean != null) {
embeddedId = foreignAssocOne.getValue(bean);
}
Object embeddedId = (bean == null) ? null : foreignAssocOne.getValue(bean);
boolean update = request.isUpdate();
if (embeddedId == null) {
for (ImportedIdSimple anImported : imported) {
if (anImported.owner.isUpdateable()) {
if (anImported.isInclude(update)) {
request.bind(null, anImported.foreignProperty);
}
}
// return anything non-null to skip a derived relationship update
return Object.class;
} else {
EntityBean embedded = (EntityBean) embeddedId;
for (ImportedIdSimple anImported : imported) {
if (anImported.owner.isUpdateable()) {
if (anImported.isInclude(update)) {
Object scalarValue = anImported.foreignProperty.getValue(embedded);
request.bind(scalarValue, anImported.foreignProperty);
}
}
return embedded;
}
// hmmm, not worrying about this just yet
return null;
}
@Override
@@ -47,15 +47,32 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
protected final int position;
public ImportedIdSimple(BeanPropertyAssoc<?> owner, String localDbColumn, String localSqlFormula, BeanProperty foreignProperty, int position) {
/**
* If true include in insert.
*/
private final boolean insertable;
/**
* If true include in update.
*/
private final boolean updateable;
public ImportedIdSimple(BeanPropertyAssoc<?> owner, String localDbColumn, String localSqlFormula, BeanProperty foreignProperty, int position,
boolean insertable, boolean updateable) {
this.owner = owner;
this.localDbColumn = InternString.intern(localDbColumn);
this.localSqlFormula = InternString.intern(localSqlFormula);
this.foreignProperty = foreignProperty;
this.position = position;
this.insertable = insertable;
this.updateable = updateable;
this.logicalName = InternString.intern(owner.getName() + "." + foreignProperty.getName());
}
public ImportedIdSimple(BeanPropertyAssoc<?> owner, String localDbColumn, String localSqlFormula, BeanProperty foreignProperty, int position) {
this(owner, localDbColumn, localSqlFormula, foreignProperty, position, true, true);
}
/**
* Return the list as an array sorted into the same order as the Bean Properties.
*/
@@ -68,6 +85,13 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
return importedIds;
}
/**
* Return true if it should be included in the update (or insert).
*/
public boolean isInclude(boolean update) {
return (update) ? updateable : insertable;
}
@Override
public boolean equals(Object obj) {
// remove FindBugs warning
@@ -116,6 +116,10 @@ public class AnnotationAssocOnes extends AnnotationParser {
prop.setFetchPreference(fetchPreference.value());
}
io.ebean.annotation.NotNull nonNull = get(prop, io.ebean.annotation.NotNull.class);
if (nonNull != null) {
prop.setNullable(false);
}
if (validationAnnotations) {
NotNull notNull = get(prop, NotNull.class);
if (notNull != null && isEbeanValidationGroups(notNull.groups())) {
@@ -18,6 +18,7 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.type.DataEncryptSupport;
import io.ebeaninternal.server.type.ScalarType;
import io.ebeaninternal.server.type.ScalarTypeArray;
import io.ebeaninternal.server.type.ScalarTypeWrapper;
import io.ebeaninternal.server.type.SimpleAesEncryptor;
import io.ebeaninternal.server.type.TypeManager;
import org.slf4j.Logger;
@@ -116,8 +117,8 @@ public class DeployUtil {
throw new IllegalArgumentException("Class [" + enumType + "] is Not a Enum?");
}
try {
Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) enumType;
EnumType type = enumerated != null ? enumerated.value() : null;
Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) enumType;
EnumType type = enumerated != null ? enumerated.value() : null;
ScalarType<?> scalarType = typeManager.createEnumScalarType(enumClass, type);
prop.setScalarType(scalarType);
prop.setDbType(scalarType.getJdbcType());
@@ -278,19 +279,25 @@ public class DeployUtil {
*/
public void setLobType(DeployBeanProperty prop) {
// is String or byte[] ? used to determine if its a CLOB or BLOB
Class<?> type = prop.getPropertyType();
ScalarType<?> scalarType = prop.getScalarType();
// this also sets the lob flag on DeployBeanProperty
int lobType = isClobType(type) ? dbCLOBType : dbBLOBType;
if (scalarType instanceof ScalarTypeWrapper) {
int lobType = scalarType.getJdbcType() == Types.VARCHAR ? dbCLOBType : dbBLOBType;
prop.setDbType(lobType);
} else {
// is String or byte[] ? used to determine if its a CLOB or BLOB
Class<?> type = prop.getPropertyType();
// this also sets the lob flag on DeployBeanProperty
int lobType = isClobType(type) ? dbCLOBType : dbBLOBType;
ScalarType<?> scalarType = typeManager.getScalarType(type, lobType);
if (scalarType == null) {
// this should never occur actually
throw new RuntimeException("No ScalarType for LOB type [" + type + "] [" + lobType + "]");
scalarType = typeManager.getScalarType(type, lobType);
if (scalarType == null) {
// this should never occur actually
throw new RuntimeException("No ScalarType for LOB type [" + type + "] [" + lobType + "]");
}
prop.setDbType(lobType);
prop.setScalarType(scalarType);
}
prop.setDbType(lobType);
prop.setScalarType(scalarType);
}
private boolean isClobType(Class<?> type) {
@@ -680,6 +680,12 @@ public final class DefaultPersister implements Persister {
return delete(descriptor, id, null, transaction, deleteMode);
}
@Override
public int deleteByIds(BeanDescriptor<?> descriptor, List<Object> idList, Transaction transaction, boolean permanent) {
DeleteMode deleteMode = (permanent || !descriptor.isSoftDelete()) ? DeleteMode.HARD : DeleteMode.SOFT;
return delete(descriptor, null, idList, transaction, deleteMode);
}
/**
* Delete by Id or a List of Id's.
*/
@@ -739,18 +745,20 @@ public final class DefaultPersister implements Persister {
// OneToMany's with delete cascade
BeanPropertyAssocMany<?>[] manys = descriptor.propertiesManyDelete();
for (BeanPropertyAssocMany<?> many : manys) {
BeanDescriptor<?> targetDesc = many.getTargetDescriptor();
// only cascade soft deletes when supported by target
if (deleteMode.isHard() || targetDesc.isSoftDelete()) {
if (deleteMode.isHard() && targetDesc.isDeleteByStatement()) {
// we can just delete children with a single statement
SqlUpdate sqlDelete = many.deleteByParentId(id, idList);
executeSqlUpdate(sqlDelete, t);
} else {
// we need to fetch the Id's to delete (recurse or notify L2 cache)
List<Object> childIds = many.findIdsByParentId(id, idList, t, null);
if (!childIds.isEmpty()) {
delete(targetDesc, null, childIds, t, deleteMode);
if (!many.isManyToMany()) {
BeanDescriptor<?> targetDesc = many.getTargetDescriptor();
// only cascade soft deletes when supported by target
if (deleteMode.isHard() || targetDesc.isSoftDelete()) {
if (deleteMode.isHard() && targetDesc.isDeleteByStatement()) {
// we can just delete children with a single statement
SqlUpdate sqlDelete = many.deleteByParentId(id, idList);
executeSqlUpdate(sqlDelete, t);
} else {
// we need to fetch the Id's to delete (recurse or notify L2 cache)
List<Object> childIds = many.findIdsByParentId(id, idList, t, null);
if (!childIds.isEmpty()) {
delete(targetDesc, null, childIds, t, deleteMode);
}
}
}
}
@@ -19,6 +19,11 @@ public class DeleteHandler extends DmlHandler {
this.meta = meta;
}
@Override
public boolean isUpdate() {
return false;
}
/**
* Generate and bind the delete statement.
*/
@@ -29,10 +29,6 @@ public class GenerateDmlRequest {
return this;
}
public void appendColumnIsNull(String column) {
appendColumn(column, IS_NULL);
}
public void appendColumn(String column) {
//String bind = (insertMode > 0) ? "?" : "=?";
appendColumn(column, "?");
@@ -91,4 +87,7 @@ public class GenerateDmlRequest {
this.prefix2 = ", ";
}
public boolean isUpdate() {
return insertMode == 0;
}
}
@@ -50,6 +50,11 @@ public class InsertHandler extends DmlHandler {
this.concatinatedKey = meta.isConcatenatedKey();
}
@Override
public boolean isUpdate() {
return false;
}
/**
* Generate and bind the insert statement.
*/
@@ -22,6 +22,11 @@ public class UpdateHandler extends DmlHandler {
this.meta = meta;
}
@Override
public boolean isUpdate() {
return true;
}
/**
* Generate and bind the update statement.
*/
@@ -59,4 +59,8 @@ public interface BindableRequest {
*/
long now();
/**
* Return true if this is an update request.
*/
boolean isUpdate();
}
@@ -19,9 +19,7 @@ public class FactoryAssocOnes {
*/
public void create(List<Bindable> list, BeanDescriptor<?> desc, DmlMode mode) {
BeanPropertyAssocOne<?>[] ones = desc.propertiesOneImported();
for (BeanPropertyAssocOne<?> one : ones) {
for (BeanPropertyAssocOne<?> one : desc.propertiesOneImported()) {
if (!one.isImportedPrimaryKey()) {
switch (mode) {
case INSERT:
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.query;
import io.ebean.OrderBy;
import io.ebean.OrderBy.Property;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
@@ -18,27 +17,22 @@ class CQueryOrderBy {
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
private final OrderBy<?> orderBy;
/**
* Create the logical order by clause.
*/
public static String parse(BeanDescriptor<?> desc, SpiQuery<?> query) {
return new CQueryOrderBy(desc, query).parseInternal();
public static String parse(BeanDescriptor<?> desc, OrderBy<?> orderBy) {
return new CQueryOrderBy(desc, orderBy).parseInternal();
}
private CQueryOrderBy(BeanDescriptor<?> desc, SpiQuery<?> query) {
private CQueryOrderBy(BeanDescriptor<?> desc, OrderBy<?> orderBy) {
this.desc = desc;
this.query = query;
this.orderBy = orderBy;
}
private String parseInternal() {
OrderBy<?> orderBy = query.getOrderBy();
if (orderBy == null) {
return null;
}
StringBuilder sb = new StringBuilder();
List<Property> properties = orderBy.getProperties();
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.query;
import io.ebean.OrderBy;
import io.ebeaninternal.api.BindParams;
import io.ebeaninternal.api.SpiExpressionList;
import io.ebeaninternal.api.SpiQuery;
@@ -329,7 +330,11 @@ public class CQueryPredicates {
private String parseOrderBy() {
return CQueryOrderBy.parse(request.getBeanDescriptor(), query);
OrderBy<?> orderBy = query.getOrderBy();
if (orderBy == null) {
return null;
}
return CQueryOrderBy.parse(request.getBeanDescriptor(), orderBy);
}
/**
@@ -51,6 +51,7 @@ public class ScalarTypeChar extends ScalarTypeBaseVarchar<Character> {
@Override
public Character toBeanType(Object value) {
if (value == null) return null;
String s = BasicTypeConverter.toString(value);
return s.charAt(0);
}
@@ -54,6 +54,7 @@ public class ScalarTypeCharArray extends ScalarTypeBaseVarchar<char[]> {
@Override
public char[] toBeanType(Object value) {
if (value == null) return null;
String s = BasicTypeConverter.toString(value);
return s.toCharArray();
}
@@ -1,66 +1,24 @@
package io.ebeaninternal.server.type;
import io.ebeaninternal.server.core.BasicTypeConverter;
import java.sql.SQLException;
import java.sql.Types;
/**
* ScalarType for String.
*/
public class ScalarTypeClob extends ScalarTypeBaseVarchar<String> {
public class ScalarTypeClob extends ScalarTypeStringBase {
protected ScalarTypeClob(boolean jdbcNative, int jdbcType) {
super(String.class, jdbcNative, jdbcType);
ScalarTypeClob(boolean jdbcNative, int jdbcType) {
super(jdbcNative, jdbcType);
}
public ScalarTypeClob() {
super(String.class, true, Types.CLOB);
}
@Override
public String convertFromDbString(String dbValue) {
return dbValue;
}
@Override
public String convertToDbString(String beanValue) {
return beanValue;
}
@Override
public void bind(DataBind b, String value) throws SQLException {
if (value == null) {
b.setNull(Types.VARCHAR);
} else {
b.setString(value);
}
super(true, Types.CLOB);
}
@Override
public String read(DataReader dataReader) throws SQLException {
return dataReader.getStringFromStream();
}
@Override
public Object toJdbcType(Object value) {
return BasicTypeConverter.toString(value);
}
@Override
public String toBeanType(Object value) {
return BasicTypeConverter.toString(value);
}
@Override
public String formatValue(String t) {
return t;
}
@Override
public String parse(String value) {
return value;
}
}
@@ -59,6 +59,7 @@ public class ScalarTypeDuration extends ScalarTypeBase<Duration> {
@Override
public Duration toBeanType(Object value) {
if (value instanceof Duration) return (Duration) value;
if (value == null) return null;
return Duration.ofSeconds(BasicTypeConverter.toLong(value));
}
@@ -42,6 +42,7 @@ public class ScalarTypeDurationWithNanos extends ScalarTypeDuration {
@Override
public Duration toBeanType(Object value) {
if (value instanceof Duration) return (Duration) value;
if (value == null) return null;
return convertFromBigDecimal(BasicTypeConverter.toBigDecimal(value));
}
@@ -53,7 +53,7 @@ public class ScalarTypeInstant extends ScalarTypeBaseDateTime<Instant> {
@Override
public Instant toBeanType(Object value) {
if (value instanceof Instant) return (Instant) value;
return convertFromTimestamp((Timestamp) value);
if (value instanceof Timestamp) return convertFromTimestamp((Timestamp) value);
return (Instant) value;
}
}
@@ -54,7 +54,7 @@ public class ScalarTypeLocalDateTime extends ScalarTypeBaseDateTime<LocalDateTim
@Override
public LocalDateTime toBeanType(Object value) {
if (value instanceof LocalDateTime) return (LocalDateTime) value;
return convertFromTimestamp((Timestamp) value);
if (value instanceof Timestamp) return convertFromTimestamp((Timestamp) value);
return (LocalDateTime) value;
}
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.type;
import java.sql.SQLException;
import java.sql.Types;
/**
@@ -12,8 +11,4 @@ public class ScalarTypeLongVarchar extends ScalarTypeClob {
super(true, Types.LONGVARCHAR);
}
@Override
public String read(DataReader dataReader) throws SQLException {
return dataReader.getStringFromStream();
}
}
@@ -55,7 +55,7 @@ public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime<OffsetDateT
@Override
public OffsetDateTime toBeanType(Object value) {
if (value instanceof OffsetDateTime) return (OffsetDateTime) value;
return convertFromTimestamp((Timestamp) value);
if (value instanceof Timestamp) return convertFromTimestamp((Timestamp) value);
return (OffsetDateTime) value;
}
}
@@ -1,103 +1,15 @@
package io.ebeaninternal.server.type;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import io.ebeaninternal.server.core.BasicTypeConverter;
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Types;
/**
* ScalarType for String.
*/
public class ScalarTypeString extends ScalarTypeBase<String> {
public class ScalarTypeString extends ScalarTypeStringBase {
public static final ScalarTypeString INSTANCE = new ScalarTypeString();
private ScalarTypeString() {
super(String.class, true, Types.VARCHAR);
}
@Override
public void bind(DataBind b, String value) throws SQLException {
if (value == null) {
b.setNull(Types.VARCHAR);
} else {
b.setString(value);
}
}
@Override
public String read(DataReader dataReader) throws SQLException {
return dataReader.getString();
}
@Override
public Object toJdbcType(Object value) {
return BasicTypeConverter.toString(value);
}
@Override
public String toBeanType(Object value) {
return BasicTypeConverter.toString(value);
}
@Override
public String formatValue(String value) {
return value;
}
@Override
public String parse(String value) {
return value;
}
@Override
public String convertFromMillis(long systemTimeMillis) {
return String.valueOf(systemTimeMillis);
}
@Override
public boolean isDateTimeCapable() {
return true;
}
@Override
public String readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
return null;
} else {
return dataInput.readUTF();
}
}
@Override
public void writeData(DataOutput dataOutput, String value) throws IOException {
if (value == null) {
dataOutput.writeBoolean(false);
} else {
dataOutput.writeBoolean(true);
dataOutput.writeUTF(value);
}
}
@Override
public String jsonRead(JsonParser parser) throws IOException {
return parser.getValueAsString();
}
@Override
public void jsonWrite(JsonGenerator writer, String value) throws IOException {
writer.writeString(value);
}
@Override
public DocPropertyType getDocType() {
return DocPropertyType.TEXT;
super(true, Types.VARCHAR);
}
}
@@ -0,0 +1,101 @@
package io.ebeaninternal.server.type;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import io.ebeaninternal.server.core.BasicTypeConverter;
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Types;
/**
* Base ScalarType for String type using Varchar, Clob and LongVarchar.
*/
public abstract class ScalarTypeStringBase extends ScalarTypeBase<String> {
ScalarTypeStringBase(boolean jdbcNative, int jdbcType) {
super(String.class, jdbcNative, jdbcType);
}
@Override
public void bind(DataBind b, String value) throws SQLException {
if (value == null) {
b.setNull(Types.VARCHAR);
} else {
b.setString(value);
}
}
@Override
public String read(DataReader dataReader) throws SQLException {
return dataReader.getString();
}
@Override
public Object toJdbcType(Object value) {
return BasicTypeConverter.toString(value);
}
@Override
public String toBeanType(Object value) {
return BasicTypeConverter.toString(value);
}
@Override
public String formatValue(String value) {
return value;
}
@Override
public String parse(String value) {
return value;
}
@Override
public String convertFromMillis(long systemTimeMillis) {
return String.valueOf(systemTimeMillis);
}
@Override
public boolean isDateTimeCapable() {
return true;
}
@Override
public String readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
return null;
} else {
return dataInput.readUTF();
}
}
@Override
public void writeData(DataOutput dataOutput, String value) throws IOException {
if (value == null) {
dataOutput.writeBoolean(false);
} else {
dataOutput.writeBoolean(true);
dataOutput.writeUTF(value);
}
}
@Override
public String jsonRead(JsonParser parser) throws IOException {
return parser.getValueAsString();
}
@Override
public void jsonWrite(JsonGenerator writer, String value) throws IOException {
writer.writeString(value);
}
@Override
public DocPropertyType getDocType() {
return DocPropertyType.TEXT;
}
}
@@ -65,6 +65,7 @@ public class ScalarTypeYear extends ScalarTypeBase<Year> {
@Override
public Year toBeanType(Object value) {
if (value instanceof Year) return (Year) value;
if (value == null) return null;
return Year.of(BasicTypeConverter.toInteger(value));
}
@@ -61,6 +61,7 @@ public class ScalarTypeYearMonthDate extends ScalarTypeBaseDate<YearMonth> {
public YearMonth toBeanType(Object value) {
if (value instanceof YearMonth) return (YearMonth) value;
if (value instanceof LocalDate) return fromLocalDate((LocalDate) value);
if (value == null) return null;
return fromLocalDate(BasicTypeConverter.toDate(value).toLocalDate());
}
@@ -55,7 +55,7 @@ public class ScalarTypeZonedDateTime extends ScalarTypeBaseDateTime<ZonedDateTim
@Override
public ZonedDateTime toBeanType(Object value) {
if (value instanceof ZonedDateTime) return (ZonedDateTime) value;
return convertFromTimestamp((Timestamp) value);
if (value instanceof Timestamp) return convertFromTimestamp((Timestamp) value);
return (ZonedDateTime) value;
}
}
@@ -108,7 +108,6 @@ public class EBasic {
@DbDefault("42")
int newInteger;
@NotNull
@ManyToOne
@DbMigration(preAlter= "insert into migtest_e_user (id) select distinct user_id from migtest_e_basic") // ensure all users exist
EUser user;
@@ -0,0 +1,76 @@
package org.tests.basic.delete;
import static org.assertj.core.api.Assertions.assertThat;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.tests.model.onetoone.OtoUser;
import org.tests.model.onetoone.OtoUserOptional;
public class TestDeleteCascadeByQuery extends BaseTestCase {
private OtoUser testUser;
private OtoUserOptional userOptional;
private Query<OtoUserOptional> userOptionalQuery = Ebean.find(OtoUserOptional.class);
private Query<OtoUser> userQuery = Ebean.find(OtoUser.class);
/**
* Init each test. Delete all existing beans. Then create OtoUser, add OtoUserOptional and save.
*/
@Before
public void init() {
Ebean.deleteAll(userQuery.findList());
Ebean.deleteAll(userOptionalQuery.findList());
userOptional = new OtoUserOptional();
Ebean.save(userOptional);
testUser = new OtoUser();
testUser.setOptional(userOptional);
Ebean.save(testUser);
}
/**
* Test that validates deleting a bean using Ebean.delete() respects the CascadeType.DELETE
* setting.
*/
@Test
public void testDeleteCascadeByEbeanDelete() {
assertThat(Ebean.delete(testUser)).isTrue();
assertThat(userOptionalQuery.findCount())
.overridingErrorMessage("Entity OtoUserOptional found. Ebean.delete() on the user "
+ "did not delete the OneToOne mapped entity as set with CascadeType.ALL")
.isEqualTo(0);
}
/**
* Test that validates deleting a bean with OneToOne mapping with a query respects the
* CascadeType.DELETE setting.
*/
@Test
public void testDeleteCascadeByQuery() {
assertThat(userQuery.delete()).isEqualTo(1);
assertThat(userOptionalQuery.findCount())
.overridingErrorMessage("Entity OtoUserOptional found. Ebean query delete() on the user "
+ "did not delete the OneToOne mapped entity as set with CascadeType.ALL")
.isEqualTo(0);
}
/**
* Cleanup - Delete all existing beans for the next test.
*/
@After
public void cleanup() {
Ebean.deleteAll(userQuery.findList());
Ebean.deleteAll(userOptionalQuery.findList());
}
}
@@ -122,15 +122,20 @@ public class TestBatchInsertFlush extends BaseTestCase {
public void transactional_flushOnGetId() {
EbeanServer server = Ebean.getDefaultServer();
LoggedSqlCollector.start();
EBasicVer b1 = new EBasicVer("b1");
server.save(b1);
EBasicVer b2 = new EBasicVer("b2");
server.save(b2);
//flush here
assertThat(LoggedSqlCollector.current()).isEmpty();
Integer id = b1.getId();
assertNotNull(id);
assertThat(LoggedSqlCollector.current()).hasSize(2);
EBasicVer b3 = new EBasicVer("b3");
server.save(b3);
}
@@ -141,7 +146,8 @@ public class TestBatchInsertFlush extends BaseTestCase {
EbeanServer server = Ebean.getDefaultServer();
Transaction txn = server.beginTransaction();
try {
txn.setBatch(PersistBatch.ALL);
LoggedSqlCollector.start();
txn.setBatchMode(true);
EBasicVer b1 = new EBasicVer("b1");
server.save(b1, txn);
@@ -149,8 +155,11 @@ public class TestBatchInsertFlush extends BaseTestCase {
EBasicVer b2 = new EBasicVer("b2");
server.save(b2, txn);
//flush here
assertThat(LoggedSqlCollector.current()).isEmpty();
Integer id = b1.getId();
assertNotNull(id);
assertThat(LoggedSqlCollector.current()).hasSize(2);
EBasicVer b3 = new EBasicVer("b3");
server.save(b3, txn);
@@ -161,6 +170,67 @@ public class TestBatchInsertFlush extends BaseTestCase {
txn.end();
}
}
@Test
@Transactional(batch = PersistBatch.ALL)
public void transactional_noflushWhenIdIsLoaded() {
EbeanServer server = Ebean.getDefaultServer();
LoggedSqlCollector.start();
EBasicVer b1 = new EBasicVer("b1");
b1.setId(78965);
server.save(b1);
EBasicVer b2 = new EBasicVer("b2");
b2.setId(78645);
server.save(b2);
assertThat(LoggedSqlCollector.current()).isEmpty();
// dont flush here
Integer id = b1.getId();
assertNotNull(id);
assertThat(LoggedSqlCollector.current()).isEmpty();
EBasicVer b3 = new EBasicVer("b3");
server.save(b3);
}
@Test
public void noflushWhenIdIsLoaded() {
EbeanServer server = Ebean.getDefaultServer();
Transaction txn = server.beginTransaction();
try {
LoggedSqlCollector.start();
txn.setBatchMode(true);
EBasicVer b1 = new EBasicVer("b1");
b1.setId(546864);
server.save(b1, txn);
EBasicVer b2 = new EBasicVer("b2");
b2.setId(21354);
server.save(b2, txn);
assertThat(LoggedSqlCollector.current()).isEmpty();
//dont flush here
Integer id = b1.getId();
assertNotNull(id);
assertThat(LoggedSqlCollector.current()).isEmpty();
EBasicVer b3 = new EBasicVer("b3");
server.save(b3, txn);
txn.commit();
assertThat(LoggedSqlCollector.current()).hasSize(3);
} finally {
txn.end();
}
}
@Test
public void testFlushOnGetProperty() {
@@ -7,7 +7,9 @@ import io.ebean.Query;
import io.ebean.annotation.IgnorePlatform;
import io.ebean.annotation.Platform;
import org.tests.model.basic.BBookmarkUser;
import org.tests.model.basic.Contact;
import org.tests.model.basic.Country;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import org.ebeantest.LoggedSqlCollector;
@@ -22,7 +24,46 @@ public class TestDeleteByQuery extends BaseTestCase {
@Test
@IgnorePlatform(Platform.MYSQL)
// FIXME: MySql does not the sub query selecting from the delete table
public void test() {
public void deleteWithSubquery() {
EbeanServer server = Ebean.getDefaultServer();
BBookmarkUser u1 = new BBookmarkUser("u1");
Ebean.save(u1);
Query<BBookmarkUser> query = server.find(BBookmarkUser.class)
.where().eq("org.name", "NahYeahMaybe")
.query();
LoggedSqlCollector.start();
query.delete();
List<String> loggedSql = LoggedSqlCollector.stop();
assertThat(loggedSql).hasSize(1);
assertThat(trimSql(loggedSql.get(0), 1)).contains("delete from bbookmark_user where id in (select t0.id from bbookmark_user t0 left join bbookmark_org t1 on t1.id = t0.org_id where t1.name");
Query<BBookmarkUser> query2 = server.find(BBookmarkUser.class)
.where().eq("name", "NotARealFirstName").query();
LoggedSqlCollector.start();
query2.delete();
loggedSql = LoggedSqlCollector.stop();
assertThat(loggedSql).hasSize(1);
assertThat(loggedSql.get(0)).contains("delete from bbookmark_user where name =");
server.find(BBookmarkUser.class).select("id").where().eq("name", "NotARealFirstName").delete();
server.find(BBookmarkUser.class).select("id").where().eq("name", "TwoAlsoNotRealFirstName").query().delete();
List<BBookmarkUser> list = server.find(BBookmarkUser.class).select("id").where().eq("name", "NotARealFirstName").findList();
assertThat(list).isEmpty();
}
@Test
@IgnorePlatform(Platform.MYSQL)
// FIXME: MySql does not the sub query selecting from the delete table
public void deleteWithSubquery_withEscalation() {
EbeanServer server = Ebean.getDefaultServer();
@@ -33,7 +74,7 @@ public class TestDeleteByQuery extends BaseTestCase {
List<String> loggedSql = LoggedSqlCollector.stop();
assertThat(loggedSql).hasSize(1);
assertThat(trimSql(loggedSql.get(0), 1)).contains("delete from contact where id in (select t0.id from contact t0 left join");
assertThat(trimSql(loggedSql.get(0), 1)).contains("select t0.id from contact t0 left join contact_group t1 on t1.id = t0.group_id where t1.name = ?");
Query<Contact> query2 = server.find(Contact.class).where().eq("firstName", "NotARealFirstName").query();
@@ -42,7 +83,7 @@ public class TestDeleteByQuery extends BaseTestCase {
loggedSql = LoggedSqlCollector.stop();
assertThat(loggedSql).hasSize(1);
assertThat(loggedSql.get(0)).contains("delete from contact where first_name =");
assertThat(loggedSql.get(0)).contains("select t0.id from contact t0 where t0.first_name = ?");
server.find(Contact.class).select("id").where().eq("firstName", "NotARealFirstName").delete();
@@ -57,12 +98,29 @@ public class TestDeleteByQuery extends BaseTestCase {
LoggedSqlCollector.start();
Ebean.find(BBookmarkUser.class).where().eq("id", 7000).delete();
Ebean.find(BBookmarkUser.class).setId(7000).delete();
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql.get(0)).contains("delete from bbookmark_user where id = ?");
assertThat(sql.get(1)).contains("delete from bbookmark_user where id = ?");
// and note this is the easiest option
Ebean.delete(BBookmarkUser.class, 7000);
}
@Test
public void queryByIdDelete_withEscalation() {
LoggedSqlCollector.start();
Ebean.find(Contact.class).where().eq("id", 7000).delete();
Ebean.find(Contact.class).setId(7000).delete();
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql.get(0)).contains("delete from contact where id = ?");
assertThat(sql.get(1)).contains("delete from contact where id = ?");
// escalate to fetch ids then delete ... but no rows found
assertThat(sql.get(0)).contains("select t0.id from contact t0 where t0.id = ?");
assertThat(sql.get(1)).contains("select t0.id from contact t0 where t0.id = ?");
// and note this is the easiest option
Ebean.delete(Contact.class, 7000);
@@ -80,11 +138,23 @@ public class TestDeleteByQuery extends BaseTestCase {
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("delete from o_customer where name = ?");
assertThat(sql.get(0)).contains("select t0.id from o_customer t0 where t0.name = ?");
}
@Test
public void testCommit() {
public void deleteByPredicate() {
BBookmarkUser ud = new BBookmarkUser("deleteQueryByPredicate");
Ebean.save(ud);
Ebean.find(BBookmarkUser.class).where().eq("name", "deleteQueryByPredicate").delete();
BBookmarkUser found = Ebean.find(BBookmarkUser.class, ud.getId());
assertThat(found).isNull();
}
@Test
public void deleteByPredicate_withEscalation() {
ResetBasicData.reset();
@@ -101,4 +171,22 @@ public class TestDeleteByQuery extends BaseTestCase {
Contact contactFind = Ebean.find(Contact.class, contact.getId());
assertThat(contactFind).isNull();
}
@Test
public void deleteByPredicateCached() {
Country country = new Country();
country.setCode("XX");
country.setName("SecretName");
Ebean.save(country);
Query<Country> query = Ebean.find(Country.class).where().eq("name", "SecretName").setUseQueryCache(true);
assertThat(query.findList()).hasSize(1);
assertThat(query.findCount()).isEqualTo(1);
Ebean.find(Country.class).where().eq("name", "SecretName").delete();
//Ebean.getDefaultServer().getPluginApi().getBeanType(Country.class).clearQueryCache();
assertThat(query.findList()).hasSize(0);
assertThat(query.findCount()).isEqualTo(0);
}
}
@@ -1,5 +1,7 @@
package org.tests.merge;
import io.ebean.annotation.NotNull;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
@@ -12,7 +14,8 @@ public class MContactMessage extends MBase {
private String notes;
@ManyToOne(optional = false)
@NotNull
@ManyToOne
private MContact contact;
public MContactMessage(String title, String subject) {
@@ -26,44 +26,26 @@ public class BBookmark {
@Column
private BBookmarkUser user;
/**
* @return the id
*/
public Integer getId() {
return this.id;
}
/**
* @param id the id to set
*/
public void setId(final Integer id) {
this.id = id;
}
/**
* @return the bookmarkReference
*/
public String getBookmarkReference() {
return this.bookmarkReference;
}
/**
* @param bookmarkReference the bookmarkReference to set
*/
public void setBookmarkReference(final String bookmarkReference) {
this.bookmarkReference = bookmarkReference;
}
/**
* @return the user
*/
public BBookmarkUser getUser() {
return this.user;
}
/**
* @param user the user to set
*/
public void setUser(final BBookmarkUser user) {
this.user = user;
}
@@ -0,0 +1,35 @@
package org.tests.model.basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class BBookmarkOrg {
@Id
@GeneratedValue
private int id;
private String name;
public BBookmarkOrg(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -1,13 +1,11 @@
package org.tests.model.basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
/**
* represents a user entity. A user contains a username and password.
*
* @author Chris
*/
@Entity
@@ -17,97 +15,69 @@ public class BBookmarkUser {
@GeneratedValue
private Integer id;
@Column
private String name;
@Column
private String password;
@Column
private String emailAddress;
@Column
private String country;
// @Version
// private Timestamp lastUpdate;
/**
* @return the id
* An optional non-cascading ManyToOne.
*/
@ManyToOne
private BBookmarkOrg org;
public BBookmarkUser(String name) {
this.name = name;
}
public Integer getId() {
return this.id;
}
/**
* @param id the id to set
*/
public void setId(final Integer id) {
this.id = id;
}
/**
* @return the password
*/
public String getPassword() {
return this.password;
}
/**
* @param password the password to set
*/
public void setPassword(final String password) {
this.password = password;
}
/**
* @return the name
*/
public String getName() {
return this.name;
}
/**
* @param name the name to set
*/
public void setName(final String name) {
this.name = name;
}
/**
* @return the emailAddress
*/
public String getEmailAddress() {
return this.emailAddress;
}
/**
* @param emailAddress the emailAddress to set
*/
public void setEmailAddress(final String emailAddress) {
this.emailAddress = emailAddress;
}
/**
* @return the country
*/
public String getCountry() {
return this.country;
}
/**
* @param country the country to set
*/
public void setCountry(final String country) {
this.country = country;
}
// public Timestamp getLastUpdate() {
// return lastUpdate;
// }
//
// public void setLastUpdate(Timestamp lastUpdate) {
// this.lastUpdate = lastUpdate;
// }
public BBookmarkOrg getOrg() {
return org;
}
public void setOrg(BBookmarkOrg org) {
this.org = org;
}
}
@@ -1,5 +1,7 @@
package org.tests.model.basic;
import io.ebean.annotation.Formula;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@@ -9,6 +11,9 @@ public class Cat extends Animal {
String name;
@Formula(select = "${ta}.species")
String catFormula;
public String getName() {
return name;
}
@@ -17,4 +22,11 @@ public class Cat extends Animal {
this.name = name;
}
public String getCatFormula() {
return catFormula;
}
public void setCatFormula(String catFormula) {
this.catFormula = catFormula;
}
}
@@ -0,0 +1,48 @@
package org.tests.model.composite;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.ManyToOne;
@Entity
public class CkeClient {
@EmbeddedId
private CkeClientKey clientPK;
@JoinColumns({
@JoinColumn(name = "username", referencedColumnName = "username"),
@JoinColumn(name = "cod_cpny", referencedColumnName = "cod_cpny", insertable = false, updatable = false)
})
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private CkeUser user;
private String notes;
public CkeUser getUser() {
return user;
}
public void setUser(CkeUser user) {
this.user = user;
}
public CkeClientKey getClientPK() {
return clientPK;
}
public void setClientPK(CkeClientKey clientPK) {
this.clientPK = clientPK;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
}
@@ -0,0 +1,53 @@
package org.tests.model.composite;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.util.Objects;
@Embeddable
public class CkeClientKey {
@Basic(optional = false)
@Column(name = "cod_cpny")
private int codCompany;
@Basic(optional = false)
@Column(name = "cod_client")
private String codClient;
public CkeClientKey(int codCompany, String codClient) {
this.codCompany = codCompany;
this.codClient = codClient;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CkeClientKey that = (CkeClientKey) o;
return codCompany == that.codCompany &&
Objects.equals(codClient, that.codClient);
}
@Override
public int hashCode() {
return Objects.hash(codCompany, codClient);
}
public int getCodCompany() {
return codCompany;
}
public void setCodCompany(int codCompany) {
this.codCompany = codCompany;
}
public String getCodClient() {
return codClient;
}
public void setCodClient(String codClient) {
this.codClient = codClient;
}
}
@@ -0,0 +1,29 @@
package org.tests.model.composite;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
@Entity
public class CkeUser {
@EmbeddedId
private CkeUserKey userPK;
private String name;
public CkeUserKey getUserPK() {
return userPK;
}
public void setUserPK(CkeUserKey userPK) {
this.userPK = userPK;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,53 @@
package org.tests.model.composite;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.util.Objects;
@Embeddable
public class CkeUserKey {
@Basic(optional = false)
@Column(name = "cod_cpny")
private int codCompany;
@Basic(optional = false)
@Column(name = "username")
private String username;
public CkeUserKey(int codCompany, String username) {
this.codCompany = codCompany;
this.username = username;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CkeUserKey that = (CkeUserKey) o;
return codCompany == that.codCompany &&
Objects.equals(username, that.username);
}
@Override
public int hashCode() {
return Objects.hash(codCompany, username);
}
public int getCodCompany() {
return codCompany;
}
public void setCodCompany(int codCompany) {
this.codCompany = codCompany;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
@@ -0,0 +1,50 @@
package org.tests.model.composite;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestCompositeKeyUserClient extends BaseTestCase {
@Test
public void test() {
CkeUser user0 = new CkeUser();
user0.setUserPK(new CkeUserKey(20, "sally"));
user0.setName("sally");
Ebean.save(user0);
CkeUser user1 = new CkeUser();
user1.setUserPK(new CkeUserKey(20, "frank"));
user1.setName("hello");
Ebean.save(user1);
CkeClient client = new CkeClient();
client.setNotes("try it");
client.setClientPK(new CkeClientKey(20, "susan"));
client.setUser(user1);
LoggedSqlCollector.start();
Ebean.save(client);
client.setNotes("update it");
client.setUser(user0);
Ebean.save(client);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("insert into cke_client (cod_cpny, cod_client, notes, username) values (?,?,?,?)");
assertThat(sql.get(1)).contains("update cke_client set notes=?, username=? where cod_cpny=? and cod_client=?");
Ebean.delete(client);
}
}
@@ -0,0 +1,36 @@
package org.tests.model.lazywithid;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class Looney {
@Id
public Long id;
@ManyToOne
private Tune tune;
private String name;
public Looney(final String name) {
this.name = name;
}
public Tune getTune() {
return tune;
}
public void setTune(final Tune tune) {
this.tune = tune;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}
@@ -0,0 +1,26 @@
package org.tests.model.lazywithid;
import static org.junit.Assert.assertEquals;
import java.util.List;
import org.junit.Test;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
public class TestColumnIdName extends BaseTestCase {
@Test
public void test() {
Tune tune = new Tune();
tune.getLoonies().add(new Looney("Taz"));
Ebean.save(tune);
final List<Tune> fetchedCollection = Ebean.find(Tune.class).findList();
assertEquals(1, fetchedCollection.size());
assertEquals(1, fetchedCollection.get(0).getLoonies().size());
assertEquals("Taz", fetchedCollection.get(0).getLoonies().get(0).getName());
}
}
@@ -0,0 +1,40 @@
package org.tests.model.lazywithid;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import io.ebean.common.BeanList;
@Entity
public class Tune {
@Id
@Column(name = "id")
Long _id;
String name;
@OneToMany(cascade = CascadeType.ALL)
private List<Looney> loonies = new BeanList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Looney> getLoonies() {
return loonies;
}
public void setLoonies(final List<Looney> loonies) {
this.loonies = loonies;
}
}
@@ -0,0 +1,31 @@
package org.tests.model.onetoone;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.tests.model.BaseModel;
@Entity
@Table(name = "oto_user_model")
public class OtoUser extends BaseModel {
private String name;
@OneToOne(cascade = CascadeType.ALL)
private OtoUserOptional userOptional;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setOptional(OtoUserOptional userOptional) {
this.userOptional = userOptional;
}
}
@@ -0,0 +1,22 @@
package org.tests.model.onetoone;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.tests.model.BaseModel;
@Entity
@Table(name = "oto_user_model_optional")
public class OtoUserOptional extends BaseModel {
private String optional;
public void setPassword(final String optional) {
this.optional = optional;
}
public String getOptional() {
return optional;
}
}
@@ -14,28 +14,8 @@ public class TestInsertManyAndRef extends BaseTestCase {
@Test
public void testMe() {
// ResetBasicData.reset();
//
// Customer u = new Customer();
// u.setName("Mr Test");
//
// final List<Order> bookmarks = new ArrayList<Order>();
// final Order b1 = new Order();
// b1.setCustomer(u);
// b1.setStatus(Status.NEW);
//
// final Order b2 = new Order();
// b2.setStatus(Status.NEW);
// b2.setCustomer(u);
//
// bookmarks.add(b1);
// bookmarks.add(b2);
//
// Ebean.save(bookmarks);
final BBookmarkUser u = new BBookmarkUser();
final BBookmarkUser u = new BBookmarkUser("Mr Test");
u.setEmailAddress("test@test.com");
u.setName("Mr Test");
u.setPassword("password");
final List<BBookmark> bookmarks = new ArrayList<>();
@@ -0,0 +1,36 @@
package org.tests.types;
/**
* Encrypted string.
*
* @author Roland Praml, FOCONIS AG
*
*/
public class EncryptedBinary {
private final byte[] encryptedData;
EncryptedBinary(byte[] encryptedData) {
this.encryptedData = encryptedData;
}
public byte[] getEncryptedData() {
return encryptedData;
}
public byte[] decrypt() {
return xor(encryptedData);
}
public static EncryptedBinary encrypt(final byte[] s) {
return new EncryptedBinary(xor(s));
}
private static byte[] xor(byte[] s) {
byte[] ret = new byte[s.length];
for (int i = 0; i < s.length; i++) {
ret[i] = (byte) (s[i] ^ i);
}
return ret;
}
}
@@ -0,0 +1,41 @@
package org.tests.types;
/**
* Encrypted string.
*
* @author Roland Praml, FOCONIS AG
*
*/
public class EncryptedString {
private final String encryptedData;
EncryptedString(String encryptedData) {
this.encryptedData = encryptedData;
}
public String getEncryptedData() {
return encryptedData;
}
public String decrypt() {
return rot13(encryptedData);
}
public static EncryptedString encrypt(final String s) {
return new EncryptedString(rot13(s));
}
private static String rot13(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c >= 'a' && c <= 'm') c += 13;
else if (c >= 'A' && c <= 'M') c += 13;
else if (c >= 'n' && c <= 'z') c -= 13;
else if (c >= 'N' && c <= 'Z') c -= 13;
sb.append(c);
}
return sb.toString();
}
}
@@ -0,0 +1,83 @@
package org.tests.types;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.validation.constraints.Size;
import org.tests.model.BaseModel;
@Entity
public class PasswordStoreModel extends BaseModel {
private static final long serialVersionUID = 1L;
// PasswordStoreModel should have the following column definitions in DDL
// enc1 varchar(30),
// enc2 varchar(40),
// enc3 clob,
// enc4 varbinary(30),
// enc5 varbinary(40),
// enc6 blob,
@Size(max = 30)
private EncryptedString enc1;
@Column(length = 40)
private EncryptedString enc2;
@Lob
private EncryptedString enc3;
@Size(max = 30)
private EncryptedBinary enc4;
@Column(length = 40)
private EncryptedBinary enc5;
@Lob
private EncryptedBinary enc6;
public EncryptedString getEnc1() {
return enc1;
}
public void setEnc1(EncryptedString enc1) {
this.enc1 = enc1;
}
public EncryptedString getEnc2() {
return enc2;
}
public void setEnc2(EncryptedString enc2) {
this.enc2 = enc2;
}
public EncryptedString getEnc3() {
return enc3;
}
public void setEnc3(EncryptedString enc3) {
this.enc3 = enc3;
}
public EncryptedBinary getEnc4() {
return enc4;
}
public void setEnc4(EncryptedBinary enc4) {
this.enc4 = enc4;
}
public EncryptedBinary getEnc5() {
return enc5;
}
public void setEnc5(EncryptedBinary enc5) {
this.enc5 = enc5;
}
public EncryptedBinary getEnc6() {
return enc6;
}
public void setEnc6(EncryptedBinary enc6) {
this.enc6 = enc6;
}
}
@@ -0,0 +1,23 @@
package org.tests.types;
import io.ebean.config.ScalarTypeConverter;
public class ScalarTypeEncryptedBinaryConverter implements ScalarTypeConverter<EncryptedBinary, byte[]> {
@Override
public EncryptedBinary getNullValue() {
return null;
}
@Override
public EncryptedBinary wrapValue(final byte[] scalarType) {
return new EncryptedBinary(scalarType);
}
@Override
public byte[] unwrapValue(final EncryptedBinary beanType) {
return beanType.getEncryptedData();
}
}
@@ -0,0 +1,22 @@
package org.tests.types;
import io.ebean.config.ScalarTypeConverter;
public class ScalarTypeEncryptedStringConverter implements ScalarTypeConverter<EncryptedString, String> {
@Override
public EncryptedString getNullValue() {
return null;
}
@Override
public EncryptedString wrapValue(final String scalarType) {
return new EncryptedString(scalarType);
}
@Override
public String unwrapValue(final EncryptedString beanType) {
return beanType.getEncryptedData();
}
}
@@ -0,0 +1,46 @@
package org.tests.types;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import static org.assertj.core.api.Assertions.assertThat;
public class TestEncryptedString extends BaseTestCase {
@Test
public void testName() {
PasswordStoreModel model = new PasswordStoreModel();
model.setEnc1(EncryptedString.encrypt("Hello"));
model.setEnc2(EncryptedString.encrypt("World"));
model.setEnc3(EncryptedString.encrypt("Test"));
model.setEnc4(EncryptedBinary.encrypt("Hello".getBytes(StandardCharsets.UTF_8)));
model.setEnc5(EncryptedBinary.encrypt("World".getBytes(StandardCharsets.UTF_8)));
model.setEnc6(EncryptedBinary.encrypt("Test".getBytes(StandardCharsets.UTF_8)));
model.save();
model = Ebean.find(PasswordStoreModel.class, model.getId());
assertThat(model.getEnc1().getEncryptedData()).isNotEqualTo("Hello");
assertThat(model.getEnc2().getEncryptedData()).isNotEqualTo("World");
assertThat(model.getEnc3().getEncryptedData()).isNotEqualTo("Test");
assertThat(model.getEnc4().getEncryptedData()).isNotEqualTo("Hello".getBytes(StandardCharsets.UTF_8));
assertThat(model.getEnc5().getEncryptedData()).isNotEqualTo("World".getBytes(StandardCharsets.UTF_8));
assertThat(model.getEnc6().getEncryptedData()).isNotEqualTo("Test".getBytes(StandardCharsets.UTF_8));
assertThat(model.getEnc1().decrypt()).isEqualTo("Hello");
assertThat(model.getEnc2().decrypt()).isEqualTo("World");
assertThat(model.getEnc3().decrypt()).isEqualTo("Test");
assertThat(model.getEnc4().decrypt()).isEqualTo("Hello".getBytes(StandardCharsets.UTF_8));
assertThat(model.getEnc5().decrypt()).isEqualTo("World".getBytes(StandardCharsets.UTF_8));
assertThat(model.getEnc6().decrypt()).isEqualTo("Test".getBytes(StandardCharsets.UTF_8));
}
}
@@ -2,6 +2,9 @@ package org.tests.types;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.plugin.BeanType;
import io.ebean.plugin.ExpressionPath;
import org.junit.Test;
import org.tests.model.types.SomeNewTypesBean;
@@ -21,9 +24,7 @@ import java.time.ZonedDateTime;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
public class TestNewTypes extends BaseTestCase {
@@ -145,4 +146,90 @@ public class TestNewTypes extends BaseTestCase {
assertNull(fetched.getPath());
assertNull(fetched.getPeriod());
}
@Test
public void testSetGetPathNonNull() throws Exception {
SomeNewTypesBean refBean = new SomeNewTypesBean();
refBean.setLocalDate(LocalDate.now());
refBean.setLocalDateTime(LocalDateTime.now());
refBean.setOffsetDateTime(OffsetDateTime.now());
refBean.setZonedDateTime(ZonedDateTime.now());
refBean.setInstant(Instant.now());
refBean.setYear(Year.now());
refBean.setMonth(Month.APRIL);
refBean.setDayOfWeek(DayOfWeek.WEDNESDAY);
refBean.setZoneId(ZoneId.systemDefault());
refBean.setZoneOffset(ZonedDateTime.now().getOffset());
refBean.setYearMonth(YearMonth.of(2014, 9));
refBean.setPath(Paths.get(TEMP_PATH));
refBean.setPeriod(Period.of(4,3,2));
testSetGetPath(refBean);
}
@Test
public void testSetGetPathNull() throws Exception {
SomeNewTypesBean refBean = new SomeNewTypesBean();
testSetGetPath(refBean);
}
private void testSetGetPath(SomeNewTypesBean refBean) {
SomeNewTypesBean testBean = new SomeNewTypesBean();
BeanType<SomeNewTypesBean> beanType = Ebean.getDefaultServer().getPluginApi().getBeanType(SomeNewTypesBean.class);
ExpressionPath localDate = beanType.getExpressionPath("localDate");
ExpressionPath localDateTime = beanType.getExpressionPath("localDateTime");
ExpressionPath offsetDateTime = beanType.getExpressionPath("offsetDateTime");
ExpressionPath zonedDateTime = beanType.getExpressionPath("zonedDateTime");
ExpressionPath instant = beanType.getExpressionPath("instant");
ExpressionPath year = beanType.getExpressionPath("year");
ExpressionPath month = beanType.getExpressionPath("month");
ExpressionPath dayOfWeek = beanType.getExpressionPath("dayOfWeek");
ExpressionPath zoneId = beanType.getExpressionPath("zoneId");
ExpressionPath zoneOffset = beanType.getExpressionPath("zoneOffset");
ExpressionPath yearMonth = beanType.getExpressionPath("yearMonth");
ExpressionPath path = beanType.getExpressionPath("path");
ExpressionPath period = beanType.getExpressionPath("period");
localDate.pathSet(testBean, refBean.getLocalDate());
assertThat(localDate.pathGet(testBean)).isEqualTo(refBean.getLocalDate());
localDateTime.pathSet(testBean, refBean.getLocalDateTime());
assertThat(localDateTime.pathGet(testBean)).isEqualTo(refBean.getLocalDateTime());
offsetDateTime.pathSet(testBean, refBean.getOffsetDateTime());
assertThat(offsetDateTime.pathGet(testBean)).isEqualTo(refBean.getOffsetDateTime());
zonedDateTime.pathSet(testBean, refBean.getZonedDateTime());
assertThat(zonedDateTime.pathGet(testBean)).isEqualTo(refBean.getZonedDateTime());
instant.pathSet(testBean, refBean.getInstant());
assertThat(instant.pathGet(testBean)).isEqualTo(refBean.getInstant());
year.pathSet(testBean, refBean.getYear());
assertThat(year.pathGet(testBean)).isEqualTo(refBean.getYear());
month.pathSet(testBean, refBean.getMonth());
assertThat(month.pathGet(testBean)).isEqualTo(refBean.getMonth());
dayOfWeek.pathSet(testBean, refBean.getDayOfWeek());
assertThat(dayOfWeek.pathGet(testBean)).isEqualTo(refBean.getDayOfWeek());
zoneId.pathSet(testBean, refBean.getZoneId());
assertThat(zoneId.pathGet(testBean)).isEqualTo(refBean.getZoneId());
zoneOffset.pathSet(testBean, refBean.getZoneOffset());
assertThat(zoneOffset.pathGet(testBean)).isEqualTo(refBean.getZoneOffset());
yearMonth.pathSet(testBean, refBean.getYearMonth());
assertThat(yearMonth.pathGet(testBean)).isEqualTo(refBean.getYearMonth());
path.pathSet(testBean, refBean.getPath());
assertThat(path.pathGet(testBean)).isEqualTo(refBean.getPath());
period.pathSet(testBean, refBean.getPeriod());
assertThat(period.pathGet(testBean)).isEqualTo(refBean.getPeriod());
Ebean.save(refBean);
Ebean.save(testBean);
}
}