Compare commits

..
Author SHA1 Message Date
rob bygrave 53a80c7dd1 [maven-release-plugin] prepare release ebean-11.15.7 2018-05-14 22:10:18 +12:00
rob bygrave 89789f3347 #1372 - Missing @OneToOne mapping yields unusable error messages.
Restore mapping.
2018-05-14 21:33:11 +12:00
rob bygrave 9dc5ca602c #1372 - Missing @OneToOne mapping yields unusable error messages. 2018-05-14 21:32:48 +12:00
rob bygrave 95737cd3c5 #1374 - Remove io.ebeaninternal.server.core.ServletContextListener - should be unused 2018-05-14 21:23:13 +12:00
Roland PramlandRob Bygrave 0a49fc9b62 FIX: NPE when fetching inherited beans from cache (#1373) 2018-05-14 21:12:42 +12:00
rob bygrave f8c80c7fd2 [maven-release-plugin] prepare for next development iteration 2018-05-10 17:22:18 +12:00
rob bygrave fc837b6472 [maven-release-plugin] prepare release ebean-11.15.6 2018-05-10 17:22:04 +12:00
rob bygrave cfaedcdbc5 #1371 - Regression in 11.15.5 with @Inheritance on abstract class with discriminatorType = INTEGER 2018-05-10 17:20:09 +12:00
rob bygrave 111bac7514 [maven-release-plugin] prepare for next development iteration 2018-05-06 20:50:56 +12:00
rob bygrave 1a2ca63d65 [maven-release-plugin] prepare release ebean-11.15.5 2018-05-06 20:50:40 +12:00
rob bygrave 33031d1c69 #1369 - BeanListener and/or BeanPersistAdapter not fired on delete of children when cascaded 2018-05-06 20:21:10 +12:00
Roland PramlandRob Bygrave 49d9d88fbe FIX: load all props from cache when inheritance is involved (#1366) 2018-04-27 14:44:18 +12:00
Rob Bygrave 756f867ed2 #1365 - Regression: property ebean.namingConvention=io.ebean.config.MatchingNamingConvention ... no longer works 2018-04-18 21:24:37 +12:00
Rob Bygrave 12108ada38 #1363 - Update ebean-migration to 11.5.4 2018-04-18 20:47:59 +12:00
rob bygrave fb2fdc9b53 Merge branch 'master' of github.com:ebean-orm/ebean 2018-04-12 20:44:57 +12:00
rob bygrave d570971fd1 #1363 - Update ebean-migration to 11.5.2 2018-04-12 20:44:39 +12:00
André CamiloandRob Bygrave a8434f22ff Inheritance - Add support for @DiscriminatorValue to be optional (#1361) 2018-04-12 20:38:09 +12:00
rob bygrave b0d3921264 [maven-release-plugin] prepare for next development iteration 2018-04-05 22:23:18 +12:00
rob bygrave 4167dfc4af [maven-release-plugin] prepare release ebean-11.15.4 2018-04-05 22:23:03 +12:00
rob bygrave e49a0c9ffb #1360 - ENH: Add transaction.setNestedUseSavepoint() ... such that nested transactions are actually Savepoints
Additional tests
2018-04-05 21:48:54 +12:00
rob bygrave dd6f223ae1 #1360 - ENH: Add transaction.setNestedUseSavepoint() ... such that nested transactions are actually Savepoints 2018-04-05 21:35:29 +12:00
Roman ParshikovandRob Bygrave 05aff53241 Fix Maven links in README (#1358) 2018-04-03 21:17:46 +12:00
Rob Bygrave 9a3362e842 [maven-release-plugin] prepare for next development iteration 2018-03-29 14:35:40 +13:00
Rob Bygrave 04e0069eb3 [maven-release-plugin] prepare release ebean-11.15.3 2018-03-29 14:35:25 +13:00
Rob Bygrave b8c4e1c3d3 #1357 - Regression in 11.15.2 - for nested Ebean.beginTransaction(TxScope.requiresNew()); ... where closed via ebeanServer.endTransaction(); rather than transaction.end() 2018-03-29 10:22:12 +13:00
Rob Bygrave 6b136248fa [maven-release-plugin] prepare for next development iteration 2018-03-29 01:49:43 +13:00
36 changed files with 991 additions and 79 deletions
+3 -3
View File
@@ -11,7 +11,7 @@ Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Web
[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cebean "maven central all related projects")
## Current versions
* [![Maven Central : ebean](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean) - ebean
* [![Maven Central : ebean-agent](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-agent) - ebean-agent
* [![Maven Central : ebean-maven-plugin](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-maven-plugin) - ebean-maven-plugin
* [![Maven Central : ebean](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean) - ebean
* [![Maven Central : ebean-agent](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent
* [![Maven Central : ebean-maven-plugin](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin
+3 -10
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.15.2</version>
<version>11.15.7</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -17,7 +17,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.15.2</tag>
<tag>ebean-11.15.7</tag>
</scm>
<profiles>
@@ -130,7 +130,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>11.5.1</version>
<version>11.5.4</version>
</dependency>
<dependency>
@@ -169,13 +169,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
+15 -3
View File
@@ -23,6 +23,7 @@ public interface Transaction extends AutoCloseable {
* This returns the current transaction for the 'default server'. If you are using
* multiple EbeanServer's then use {@link EbeanServer#currentTransaction()}.
* </p>
*
* @see Ebean#currentTransaction()
* @see EbeanServer#currentTransaction()
*/
@@ -136,6 +137,18 @@ public interface Transaction extends AutoCloseable {
*/
void rollback(Throwable e) throws PersistenceException;
/**
* Set when we want nested transactions to use Savepoint's.
* <p>
* This means that for a nested transaction:
* <ul>
* <li>begin transaction maps to creating a savepoint</li>
* <li>commit transaction maps to releasing a savepoint</li>
* <li>rollback transaction maps to rollback a savepoint</li>
* </ul>
*/
void setNestedUseSavepoint();
/**
* Mark the transaction for rollback only.
*/
@@ -151,7 +164,6 @@ public interface Transaction extends AutoCloseable {
*/
void end();
/**
* Synonym for end() to support AutoClosable.
*/
@@ -227,7 +239,7 @@ public interface Transaction extends AutoCloseable {
* Refer to {@link ServerConfig#setSkipCacheAfterWrite(boolean)} for configuring the default behavior
* for using the L2 bean cache in transactions spanning multiple query/persist requests.
* </p>
* <p>
*
* <pre>{@code
*
* // assume Customer has L2 bean caching enabled ...
@@ -309,7 +321,7 @@ public interface Transaction extends AutoCloseable {
* <p>
* Example: batch processing executing every 3 rows
* </p>
* <p>
*
* <pre>{@code
*
* String data = "This is a simple test of the batch processing"
@@ -2871,7 +2871,7 @@ public class ServerConfig {
}
private NamingConvention createNamingConvention(PropertiesWrapper properties, NamingConvention namingConvention) {
NamingConvention nc = properties.createInstance(NamingConvention.class, "namingconvention", null);
NamingConvention nc = properties.createInstance(NamingConvention.class, "namingConvention", null);
return (nc != null) ? nc : namingConvention;
}
@@ -319,4 +319,9 @@ public interface SpiTransaction extends Transaction {
* Return the profile location for this transaction.
*/
ProfileLocation getProfileLocation();
/**
* Return true when nested transactions should create Savepoints.
*/
boolean isNestedUseSavepoint();
}
@@ -21,7 +21,7 @@ import java.sql.SQLException;
/**
* Proxy for an underlying SpiTransaction (most of the API).
*/
abstract class SpiTransactionProxy implements SpiTransaction {
public abstract class SpiTransactionProxy implements SpiTransaction {
protected SpiTransaction transaction;
@@ -50,6 +50,16 @@ abstract class SpiTransactionProxy implements SpiTransaction {
return transaction.isRollbackOnly();
}
@Override
public void setNestedUseSavepoint() {
transaction.setNestedUseSavepoint();
}
@Override
public boolean isNestedUseSavepoint() {
return transaction.isNestedUseSavepoint();
}
@Override
public long profileOffset() {
return transaction.profileOffset();
@@ -15,6 +15,10 @@ public class CachedBeanDataToBean {
EntityBeanIntercept ebi = bean._ebean_getIntercept();
BeanProperty idProperty = desc.getIdProperty();
if (desc.getInheritInfo() != null) {
desc = desc.getInheritInfo().readType(bean.getClass()).desc();
}
if (idProperty != null) {
// load the id property
loadProperty(bean, cacheBeanData, ebi, idProperty, context);
@@ -843,7 +843,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
*/
@Override
public void endTransaction() {
Transaction transaction = currentTransaction();
Transaction transaction = transactionManager.getInScope();
if (transaction != null) {
transaction.end();
}
@@ -1,32 +0,0 @@
package io.ebeaninternal.server.core;
import io.ebeaninternal.server.lib.ShutdownManager;
import javax.servlet.ServletContextEvent;
/**
* Listens for webserver server starting and stopping events.
* <p>
* Register this listener in the web.xml configuration file. This will listen
* for startup and shutdown events.
* </p>
*/
public class ServletContextListener implements javax.servlet.ServletContextListener {
/**
* The servlet container is stopping.
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
ShutdownManager.shutdown();
}
/**
* Do nothing on startup.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
}
}
@@ -71,6 +71,7 @@ import io.ebeaninternal.server.query.STreePropertyAssocMany;
import io.ebeaninternal.server.query.STreePropertyAssocOne;
import io.ebeaninternal.server.query.STreeType;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.querydefn.DefaultOrmQuery;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebeaninternal.server.type.DataBind;
@@ -1723,7 +1724,15 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* associated L2 bean caching.
*/
public boolean isDeleteByStatement() {
return deleteRecurseSkippable && !isBeanCaching();
return persistListener == null
&& persistController == null
&& deleteRecurseSkippable && !isBeanCaching();
}
public boolean isDeleteByBulk() {
return persistListener == null
&& persistController == null
&& propertiesManyToMany.length == 0;
}
/**
@@ -1870,6 +1879,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* on first access (lazy load) or immediately (eager load)
*/
public EntityBean createEntityBean(boolean isNew) {
if (prototypeEntityBean == null) {
throw new UnsupportedOperationException("cannot create entity bean for abstract entity " + getName());
}
try {
EntityBean bean = (EntityBean) prototypeEntityBean._ebean_newInstance();
@@ -1961,6 +1973,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
public T createReference(Object id, PersistenceContext pc) {
try {
if (inheritInfo != null && !inheritInfo.isConcrete()) {
// we actually need to do a query because we don't know the type without the discriminator
// value, just select the id property and discriminator column (auto added)
DefaultOrmQuery<T> query = new DefaultOrmQuery<>(this, ebeanServer, ebeanServer.getExpressionFactory());
return query.select(getIdProperty().getName()).setId(id).findOne();
}
EntityBean eb = createEntityBean();
id = convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
@@ -310,6 +310,10 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
String discProperty = prefix + "." + discriminatorColumn;
selectChain.add(discProperty);
}
if (targetIdBinder == null) {
throw new IllegalStateException("No Id binding property for " + getFullBeanName()
+ ". Probably a missing @OneToOne mapping annotation on this relationship?");
}
targetIdBinder.buildRawSqlSelectChain(prefix, selectChain);
} else {
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.deploy.id.IdBinder;
import io.ebeaninternal.server.deploy.parse.DeployInheritInfo;
import io.ebeaninternal.server.query.SqlTreeProperties;
import java.lang.reflect.Modifier;
import javax.persistence.PersistenceException;
import java.sql.SQLException;
import java.util.ArrayList;
@@ -291,7 +292,7 @@ public class InheritInfo {
* Return true if this is considered a concrete type in the inheritance hierarchy.
*/
public boolean isConcrete() {
return discriminatorValue != null;
return !Modifier.isAbstract(type.getModifiers());
}
/**
@@ -123,11 +123,14 @@ public class DeployInherit {
info.setColumnDefn(da.columnDefinition());
}
DiscriminatorValue dv = AnnotationUtil.findAnnotation(cls, DiscriminatorValue.class); // do not search recursive
if (dv != null) {
info.setDiscriminatorValue(dv.value());
if (!info.isAbstract()) {
DiscriminatorValue dv = AnnotationUtil.findAnnotation(cls, DiscriminatorValue.class); // do not search recursive
if (dv != null) {
info.setDiscriminatorValue(dv.value());
} else {
info.setDiscriminatorValue(cls.getSimpleName());
}
}
return info;
}
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy.parse;
import io.ebeaninternal.server.deploy.InheritInfo;
import java.lang.reflect.Modifier;
import javax.persistence.DiscriminatorType;
import java.sql.Types;
import java.util.ArrayList;
@@ -64,7 +65,7 @@ public class DeployInheritInfo {
* Return true if this is abstract node.
*/
public boolean isAbstract() {
return (discriminatorObjectValue == null);
return Modifier.isAbstract(type.getModifiers());
}
/**
@@ -205,7 +206,7 @@ public class DeployInheritInfo {
}
private void appendDiscriminator(List<Object> list) {
if (discriminatorObjectValue != null) {
if (!isAbstract()) {
list.add(discriminatorObjectValue);
}
for (DeployInheritInfo child : children) {
@@ -588,10 +588,18 @@ public final class DefaultPersister implements Persister {
}
}
private void deleteList(List<?> beanList, Transaction t, boolean softDelete) {
private void deleteList(List<?> beanList, SpiTransaction t, boolean softDelete, boolean children) {
if (children) {
t.depth(-1);
t.checkBatchEscalationOnCollection();
}
for (Object aBeanList : beanList) {
deleteRecurse((EntityBean) aBeanList, t, softDelete);
}
if (children) {
t.flushBatchOnCollection();
t.depth(+1);
}
}
/**
@@ -675,7 +683,7 @@ public final class DefaultPersister implements Persister {
t.logSummary("-- DeleteById of " + descriptor.getName() + " ids[" + idList + "] requires fetch of foreign key values");
}
List<?> beanList = server.findList(q, t);
deleteList(beanList, t, softDelete);
deleteList(beanList, t, softDelete, false);
return beanList.size();
} else {
@@ -1012,7 +1020,7 @@ public final class DefaultPersister implements Persister {
// cascade delete the beans in the collection
BeanDescriptor<?> targetDesc = many.getTargetDescriptor();
if (!softDelete || targetDesc.isSoftDelete()) {
if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isBeanCaching()) {
if (targetDesc.isDeleteByStatement()) {
// Just delete all the children with one statement
IntersectionRow intRow = many.buildManyDeleteChildren(parentBean, excludeDetailIds);
SqlUpdate sqlDelete = intRow.createDelete(server, softDelete);
@@ -1037,13 +1045,13 @@ public final class DefaultPersister implements Persister {
*/
private void deleteChildrenById(SpiTransaction t, BeanDescriptor<?> targetDesc, List<Object> childIds, boolean softDelete) {
if (targetDesc.propertiesManyToMany().length > 0) {
if (!targetDesc.isDeleteByBulk()) {
// convert into a list of reference objects and perform delete by object
List<Object> refList = new ArrayList<>(childIds.size());
for (Object id : childIds) {
refList.add(targetDesc.createReference(id, null));
}
deleteList(refList, t, softDelete);
deleteList(refList, t, softDelete, true);
} else {
// perform delete by statement if possible
@@ -231,6 +231,16 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
public void markNotQueryOnly() {
}
@Override
public boolean isNestedUseSavepoint() {
return false;
}
@Override
public void setNestedUseSavepoint() {
}
@Override
public boolean isReadOnly() {
if (!isActive()) {
@@ -77,6 +77,8 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
protected boolean rollbackOnly;
protected boolean nestedUseSavepoint;
/**
* The underlying Connection.
*/
@@ -1117,6 +1119,16 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
this.rollbackOnly = true;
}
@Override
public boolean isNestedUseSavepoint() {
return nestedUseSavepoint;
}
@Override
public void setNestedUseSavepoint() {
this.nestedUseSavepoint = true;
}
/**
* Rollback the transaction.
*/
@@ -37,6 +37,16 @@ class NoTransaction implements SpiTransaction {
return null;
}
@Override
public boolean isNestedUseSavepoint() {
return false;
}
@Override
public void setNestedUseSavepoint() {
}
@Override
public boolean isActive() {
// always false
@@ -0,0 +1,107 @@
package io.ebeaninternal.server.transaction;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.SpiTransactionProxy;
import io.ebeaninternal.server.lib.util.Str;
import javax.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Savepoint;
/**
* A nested transaction that represents a Savepoint.
*/
class SavepointTransaction extends SpiTransactionProxy {
private static final int STATE_COMMITTED = 1;
private static final int STATE_ROLLED_BACK = 2;
private final TransactionManager manager;
private final Savepoint savepoint;
private final Connection connection;
private final String logPrefix;
private boolean rollbackOnly;
private int state;
SavepointTransaction(SpiTransaction transaction, TransactionManager manager) throws SQLException {
this.manager = manager;
this.transaction = transaction;
this.connection = transaction.getInternalConnection();
this.savepoint = connection.setSavepoint();
int savepointId = savepoint.getSavepointId();
this.logPrefix = "txn[" + transaction.getId() + "-sp" + savepointId + "] ";
}
@Override
public String getLogPrefix() {
return logPrefix;
}
@Override
public void logSql(String msg) {
TransactionManager.SQL_LOGGER.debug(Str.add(logPrefix, msg));
}
@Override
public void logSummary(String msg) {
TransactionManager.SUM_LOGGER.debug(Str.add(logPrefix, msg));
}
@Override
public void setRollbackOnly() {
this.rollbackOnly = true;
}
@Override
public void commit() {
if (rollbackOnly) {
rollbackSavepoint(null);
} else {
commitSavepoint();
}
}
@Override
public void rollback() throws PersistenceException {
rollbackSavepoint(null);
}
@Override
public void rollback(Throwable e) throws PersistenceException {
rollbackSavepoint(e);
}
private void commitSavepoint() {
try {
connection.releaseSavepoint(savepoint);
state = STATE_COMMITTED;
manager.notifyOfCommit(this);
} catch (SQLException e) {
throw new PersistenceException("Error trying to commit/release Savepoint", e);
}
}
private void rollbackSavepoint(Throwable cause) throws PersistenceException {
try {
connection.rollback(savepoint);
state = STATE_ROLLED_BACK;
manager.notifyOfRollback(this, cause);
} catch (SQLException e) {
throw new PersistenceException("Error trying to rollback Savepoint", e);
}
}
@Override
public void end() {
if (state == 0) {
rollback();
}
}
@Override
public void close() {
end();
}
}
@@ -582,27 +582,42 @@ public class TransactionManager implements SpiTransactionManager {
txScope = initTxScope(txScope);
boolean setToScope = false;
ScopedTransaction txnContainer = getActiveScoped();
boolean setToScope;
boolean nestedSavepoint;
SpiTransaction transaction;
if (txnContainer == null) {
setToScope = true;
txnContainer = createScopedTransaction();
setToScope = true;
transaction = null;
nestedSavepoint = false;
} else {
setToScope = false;
transaction = txnContainer.current();
nestedSavepoint = transaction.isNestedUseSavepoint();
}
SpiTransaction transaction = txnContainer.current();
TxType type = txScope.getType();
boolean createTransaction = isCreateNewTransaction(transaction, type);
if (createTransaction) {
switch (type) {
case SUPPORTS:
case NOT_SUPPORTED:
case NEVER:
transaction = NoTransaction.INSTANCE;
break;
default:
transaction = createTransaction(true, txScope.getIsolationLevel());
initNewTransaction(transaction, txScope);
boolean createTransaction;
if (nestedSavepoint && (type == TxType.REQUIRED || type == TxType.REQUIRES_NEW)) {
createTransaction = true;
transaction = createSavepoint(transaction, this);
} else {
createTransaction = isCreateNewTransaction(transaction, type);
if (createTransaction) {
switch (type) {
case SUPPORTS:
case NOT_SUPPORTED:
case NEVER:
transaction = NoTransaction.INSTANCE;
break;
default:
transaction = createTransaction(true, txScope.getIsolationLevel());
initNewTransaction(transaction, txScope);
}
}
}
@@ -613,6 +628,14 @@ public class TransactionManager implements SpiTransactionManager {
return txnContainer;
}
private SpiTransaction createSavepoint(SpiTransaction transaction, TransactionManager manager) {
try {
return new SavepointTransaction(transaction, manager);
} catch (SQLException e) {
throw new PersistenceException("Error creating nested Savepoint Transaction", e);
}
}
private void initNewTransaction(SpiTransaction transaction, TxScope txScope) {
if (txScope.isSkipCache()) {
@@ -65,6 +65,8 @@ public class ServerConfigTest {
props.setProperty("notifyL2CacheInForeground", "true");
props.setProperty("idType", "SEQUENCE");
props.setProperty("mappingLocations", "classpath:/foo;bar");
props.setProperty("namingConvention", "io.ebean.config.MatchingNamingConvention");
serverConfig.loadFromProperties(props);
@@ -72,6 +74,7 @@ public class ServerConfigTest {
assertTrue(serverConfig.isNotifyL2CacheInForeground());
assertTrue(serverConfig.isDbOffline());
assertTrue(serverConfig.isAutoReadOnlyDataSource());
assertThat(serverConfig.getNamingConvention()).isInstanceOf(MatchingNamingConvention.class);
assertEquals(IdType.SEQUENCE, serverConfig.getIdType());
assertEquals(PersistBatch.INSERT, serverConfig.getPersistBatch());
+39
View File
@@ -0,0 +1,39 @@
package org.tests.cache;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.tests.model.basic.OCachedInhChildA;
import org.tests.model.basic.OCachedInhRoot;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull;
/**
* Test class testing deleting/invalidating of cached beans
*/
public class TestCacheManyToOne extends BaseTestCase {
@Test
public void crudInvoice() {
OCachedInhChildA bean = new OCachedInhChildA();
bean.setName("Roland");
bean.setChildAData("ChildData");
Ebean.save(bean);
Ebean.find(OCachedInhRoot.class).select("id").where().idIn(bean.getId()).findEach(bean0 -> {
assertNotNull(bean0);
assertThat(bean0.getName()).isEqualTo("Roland");
assertThat(((OCachedInhChildA)bean0).getChildAData()).isEqualTo("ChildData");
});
Ebean.find(OCachedInhRoot.class).select("id").where().idIn(bean.getId()).findEach(bean1 -> {
assertNotNull(bean1);
assertThat(bean1.getName()).isEqualTo("Roland");
// this will fail if the bean is loaded from cache and the wrong discriminator is used.
assertThat(((OCachedInhChildA)bean1).getChildAData()).isEqualTo("ChildData");
});
}
}
@@ -0,0 +1,51 @@
package org.tests.delete;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class DcDetail {
@Id
long id;
@ManyToOne
DcMaster master;
String description;
long version;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public DcMaster getMaster() {
return master;
}
public void setMaster(DcMaster master) {
this.master = master;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,28 @@
package org.tests.delete;
import io.ebean.event.AbstractBeanPersistListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DcListener extends AbstractBeanPersistListener {
static List<Object> deleted = Collections.synchronizedList(new ArrayList<>());
@Override
public boolean isRegisterFor(Class<?> cls) {
return cls.equals(DcDetail.class);
}
@Override
public void deleted(Object bean) {
deleted.add(bean);
}
static List<Object> deletedBeans() {
List<Object> copy = new ArrayList<>(deleted);
deleted.clear();
return copy;
}
}
@@ -0,0 +1,59 @@
package org.tests.delete;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import java.util.List;
@Entity
public class DcMaster {
@Id
long id;
String name;
@OneToMany(mappedBy = "master", cascade = CascadeType.ALL)
List<DcDetail> details;
@Version
long version;
public DcMaster(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 List<DcDetail> getDetails() {
return details;
}
public void setDetails(List<DcDetail> details) {
this.details = details;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,51 @@
package org.tests.delete;
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 TestDeleteCascadeWithListener extends BaseTestCase {
@Test
public void test() {
DcMaster m0 = new DcMaster("m0");
m0.getDetails().add(new DcDetail());
m0.getDetails().add(new DcDetail());
m0.getDetails().add(new DcDetail());
Ebean.save(m0);
DcMaster found = Ebean.find(DcMaster.class, m0.getId());
LoggedSqlCollector.start();
Ebean.delete(found);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(5);
assertThat(sql.get(0)).contains("select t0.id from dc_detail t0 where master_id=?");
assertThat(sql.get(1)).contains("delete from dc_detail where id=?");
assertThat(sql.get(3)).contains("delete from dc_detail where id=?");
assertThat(sql.get(4)).contains("delete from dc_master where id=? and version=?");
awaitListenerPropagation();
List<Object> beans = DcListener.deletedBeans();
assertThat(beans).hasSize(3);
}
private void awaitListenerPropagation() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,48 @@
package org.tests.inheritance.abstrakt;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.Table;
import javax.persistence.Version;
@Entity
@Table(name = "block")
@Inheritance
@DiscriminatorColumn(name = "case_type", discriminatorType = DiscriminatorType.INTEGER)
public abstract class AbstractBaseBlock {
@Id
long id;
String name;
@Version
long version;
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;
}
}
@@ -0,0 +1,19 @@
package org.tests.inheritance.abstrakt;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@Entity
@DiscriminatorValue(value = "2")
public class Block extends AbstractBaseBlock {
String notes;
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
}
@@ -0,0 +1,18 @@
package org.tests.inheritance.abstrakt;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.junit.Test;
public class TestInheritanceNum extends BaseTestCase {
@Test
public void basicIUD() {
Block block = new Block();
block.setName("ibe");
block.setNotes("try it");
Ebean.save(block);
}
}
@@ -0,0 +1,64 @@
package org.tests.inheritance.cache;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.tests.model.basic.cache.CInhOne;
import org.tests.model.basic.cache.CInhRef;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import org.ebeantest.LoggedSqlCollector;
public class TestInheritanceRefCache extends BaseTestCase {
@Test
public void test() {
CInhOne one = new CInhOne();
one.setLicenseNumber("O12");
one.setDriver("Jimmy");
one.setNotes("Hello");
Ebean.save(one);
CInhRef ref = new CInhRef();
ref.setRef(one);
Ebean.save(ref);
Integer id = ref.getId();
LoggedSqlCollector.start();
CInhRef gotRef = Ebean.find(CInhRef.class).setId(id).findOne();
assertThat(gotRef).isInstanceOf(CInhRef.class);
assertThat(gotRef.getRef()).isInstanceOf(CInhOne.class);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("from cinh_ref").contains("left join cinh_root");
// fetch again - from cache (but fetch second bean from cache)
LoggedSqlCollector.start();
gotRef = Ebean.find(CInhRef.class).setId(id).findOne();
assertThat(gotRef).isInstanceOf(CInhRef.class);
assertThat(gotRef.getRef()).isInstanceOf(CInhOne.class);
sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("from cinh_root");
// fetch again - both from cache
LoggedSqlCollector.start();
gotRef = Ebean.find(CInhRef.class).setId(id).findOne();
assertThat(gotRef).isInstanceOf(CInhRef.class);
assertThat(gotRef.getRef()).isInstanceOf(CInhOne.class);
sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(0);
}
}
@@ -0,0 +1,30 @@
package org.tests.model.basic;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
/**
* Child bean A
*/
@Entity
@DiscriminatorValue("childA")
public class OCachedInhChildA extends OCachedInhRoot {
String childAData;
/**
* @return the childAData
*/
public String getChildAData() {
return childAData;
}
/**
* @param childAData
* the childAData to set
*/
public void setChildAData(String childAData) {
this.childAData = childAData;
}
}
@@ -0,0 +1,29 @@
package org.tests.model.basic;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
/**
* Child bean B
*/
@Entity
@DiscriminatorValue("childB")
public class OCachedInhChildB extends OCachedInhRoot {
String childBData;
/**
* @return the childBData
*/
public String getChildBData() {
return childBData;
}
/**
* @param childBData
* the childBData to set
*/
public void setChildBData(String childBData) {
this.childBData = childBData;
}
}
@@ -0,0 +1,44 @@
package org.tests.model.basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.Table;
import io.ebean.annotation.Cache;
/**
* Cached entity for inheritance.
*
* @author Roland Praml, FOCONIS AG
*/
@Entity
@Table(name = "o_cached_inherit")
@Inheritance
@Cache
public abstract class OCachedInhRoot {
@Id
Long id;
String 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;
}
}
+25
View File
@@ -0,0 +1,25 @@
package org.tests.model.basic.cache;
import org.tests.model.basic.BasicDomain;
import io.ebean.annotation.Cache;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
@Entity
@Cache(enableQueryCache = true)
public class CInhRef extends BasicDomain {
private static final long serialVersionUID = -4673953370819311120L;
@ManyToOne(cascade = {})
private CInhRoot ref;
public CInhRoot getRef() {
return ref;
}
public void setRef(CInhRoot ref) {
this.ref = ref;
}
}
@@ -5,11 +5,13 @@ import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.PersistenceContextScope;
import io.ebean.Transaction;
import io.ebean.TxScope;
import org.junit.Test;
import org.tests.model.basic.EBasic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
public class TestNestedSubTransaction extends BaseTestCase {
@@ -56,6 +58,156 @@ public class TestNestedSubTransaction extends BaseTestCase {
}
}
@Test
public void nestedUseSavepoint_doubleNested_rollbackCommit() {
EbeanServer server = server();
EBasic bean = new EBasic("start2");
try (Transaction txn0 = server.beginTransaction()) {
txn0.setRollbackOnly();
txn0.setNestedUseSavepoint();
server.save(bean);
try (Transaction txn1 = server.beginTransaction()) {
bean.setName("updateNested");
server.save(bean);
try (Transaction txn2 = server.beginTransaction()) {
bean.setName("barney");
Ebean.save(bean);
txn2.commit();
}
txn1.rollback();
}
EBasic fresh = server.find(EBasic.class)
.setId(bean.getId())
.setPersistenceContextScope(PersistenceContextScope.QUERY)
.findOne();
assertNotNull(fresh);
assertThat(fresh.getName()).isEqualTo("start2");
}
}
@Test
public void nestedUseSavepoint_doubleNested_commitRollback() {
EbeanServer server = server();
EBasic bean = new EBasic("start3");
try (Transaction txn0 = server.beginTransaction()) {
txn0.setRollbackOnly();
txn0.setNestedUseSavepoint();
server.save(bean);
try (Transaction txn1 = server.beginTransaction()) {
bean.setName("updateNested3");
server.save(bean);
try (Transaction txn2 = server.beginTransaction()) {
bean.setName("barney3");
Ebean.save(bean);
txn2.rollback();
}
txn1.commit();
}
EBasic fresh = server.find(EBasic.class)
.setId(bean.getId())
.setPersistenceContextScope(PersistenceContextScope.QUERY)
.findOne();
assertNotNull(fresh);
assertThat(fresh.getName()).isEqualTo("updateNested3");
}
}
@Test
public void nestedUseSavepoint_nested_RequiresNew() {
EbeanServer server = server();
EBasic bean = new EBasic("start4");
try (Transaction txn0 = server.beginTransaction()) {
txn0.setRollbackOnly();
txn0.setNestedUseSavepoint();
server.save(bean);
try (Transaction txn1 = server.beginTransaction(TxScope.requiresNew())) {
bean.setName("updateNested4");
server.save(bean);
txn1.commit();
}
EBasic fresh = server.find(EBasic.class)
.setId(bean.getId())
.setPersistenceContextScope(PersistenceContextScope.QUERY)
.findOne();
assertNotNull(fresh);
assertThat(fresh.getName()).isEqualTo("updateNested4");
}
EBasic after = server.find(EBasic.class)
.setId(bean.getId())
.findOne();
assertNull(after);
}
@Test
public void nestedUseSavepoint() {
EbeanServer server = server();
EBasic bean = new EBasic("start1");
try (Transaction txn0 = server.beginTransaction()) {
txn0.setRollbackOnly();
txn0.setNestedUseSavepoint();
server.save(bean);
try (Transaction txn1 = server.beginTransaction()) {
bean.setName("updateNested");
server.save(bean);
txn1.commit();
}
EBasic fresh = server.find(EBasic.class, bean.getId());
assertNotNull(fresh);
assertThat(fresh.getName()).isEqualTo("updateNested");
try (Transaction txn2 = server.beginTransaction()) {
bean.setName("barney");
Ebean.save(bean);
txn2.rollback();
}
fresh = server.find(EBasic.class)
.setId(bean.getId())
.setPersistenceContextScope(PersistenceContextScope.QUERY)
.findOne();
assertNotNull(fresh);
assertThat(fresh.getName()).isEqualTo("updateNested");
} finally {
cleanup(bean);
}
}
private void cleanup(EBasic bean) {
Ebean.delete(EBasic.class, bean.getId());
}
@@ -2,7 +2,9 @@ package org.tests.transaction;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.Transaction;
import io.ebean.TxScope;
import io.ebean.annotation.Transactional;
import io.ebean.annotation.TxType;
import org.junit.Test;
@@ -11,6 +13,7 @@ import org.slf4j.LoggerFactory;
import java.sql.Connection;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
@@ -66,5 +69,64 @@ public class TestTransactionalRequiresNew extends BaseTestCase {
}
}
@Test
public void testDifferentCreation1() {
EbeanServer server = server();
try(Transaction txn = server.beginTransaction()) {
String txnName = txn.toString();
Ebean.beginTransaction(TxScope.requiresNew());
try {
Ebean.commitTransaction();
} finally {
Ebean.endTransaction();
}
assertThat(txn.toString()).isEqualTo(txnName);
}
}
@Test
public void testDifferentCreation2() {
EbeanServer server = server();
try (Transaction txn = server.beginTransaction()) {
String txnName = txn.toString();
server.beginTransaction(TxScope.requiresNew());
try {
server.commitTransaction();
} finally {
server.endTransaction();
}
assertThat(txn.toString()).isEqualTo(txnName);
}
}
@Test
public void testDifferentCreation3() {
EbeanServer server = server();
try (Transaction txn = server.beginTransaction()) {
String txnName = txn.toString();
Transaction txn2 = Ebean.beginTransaction(TxScope.requiresNew());
try {
txn2.commit();
} finally {
txn2.end();
}
assertThat(txn.toString()).isEqualTo(txnName);
}
}
@Test
public void testDifferentCreation4() {
EbeanServer server = server();
try (Transaction txn = server.beginTransaction()) {
String txnName = txn.toString();
server.beginTransaction(TxScope.requiresNew());
try {
Transaction.current().commitAndContinue();
} finally {
Transaction.current().end();
}
assertThat(txn.toString()).isEqualTo(txnName);
}
}
}