mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53a80c7dd1 | ||
|
|
89789f3347 | ||
|
|
9dc5ca602c | ||
|
|
95737cd3c5 | ||
|
|
0a49fc9b62 | ||
|
|
f8c80c7fd2 | ||
|
|
fc837b6472 | ||
|
|
cfaedcdbc5 | ||
|
|
111bac7514 | ||
|
|
1a2ca63d65 | ||
|
|
33031d1c69 | ||
|
|
49d9d88fbe | ||
|
|
756f867ed2 | ||
|
|
12108ada38 | ||
|
|
fb2fdc9b53 | ||
|
|
d570971fd1 | ||
|
|
a8434f22ff | ||
|
|
b0d3921264 | ||
|
|
4167dfc4af | ||
|
|
e49a0c9ffb | ||
|
|
dd6f223ae1 | ||
|
|
05aff53241 | ||
|
|
9a3362e842 | ||
|
|
04e0069eb3 | ||
|
|
b8c4e1c3d3 | ||
|
|
6b136248fa |
@@ -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
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean) - ebean
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-agent) - ebean-agent
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-maven-plugin) - ebean-maven-plugin
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean) - ebean
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user