Skip to content

Commit

Permalink
Change semantic of userObjects in nested transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
rPraml committed Dec 8, 2023
1 parent e879a32 commit 839bba9
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 6 deletions.
20 changes: 20 additions & 0 deletions ebean-core/src/main/java/io/ebeaninternal/api/ScopeTrans.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import io.ebean.TxScope;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
* Used internally to handle the scoping of transactions for methods.
Expand Down Expand Up @@ -47,6 +49,11 @@ public final class ScopeTrans {
private boolean nestedCommit;
private boolean nestedUseSavepoint;

/**
* The UserObjects when using in nested transactions.
*/
private Map<String, Object> userObjects;

public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope) {
this.rollbackOnChecked = rollbackOnChecked;
this.created = created;
Expand Down Expand Up @@ -218,4 +225,17 @@ private boolean isRollbackThrowable(Throwable e) {
return e instanceof RuntimeException || rollbackOnChecked;
}

public void putUserObject(String name, Object value) {
if (userObjects == null) {
userObjects = new HashMap<>();
}
userObjects.put(name, value);
}

public Object getUserObject(String name) {
if (userObjects == null) {
return null;
}
return userObjects.get(name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import io.ebeaninternal.server.transaction.TransactionScopeManager;
import io.ebeaninternal.server.util.ArrayStack;

import jakarta.persistence.PersistenceException;

/**
Expand Down Expand Up @@ -173,4 +172,31 @@ public Exception caughtThrowable(Exception e) {
return current.caughtThrowable(e);
}

/**
* New user objects are always written to the current ScopeTrans.
*/
@Override
public void putUserObject(String name, Object value) {
current.putUserObject(name, value);
}

/**
* Returns the userObject in the stack, Herew we search
* the stack and return the first found userObject
*/
@Override
public Object getUserObject(String name) {
Object obj = current.getUserObject(name);
if (obj != null) {
return obj;
}
for (ScopeTrans trans : stack) {
obj = trans.getUserObject(name);
if (obj != null) {
return obj;
}
}
return transaction.getUserObject(name);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.Iterator;
import java.util.List;

/**
* Stack based on ArrayList.
*/
public class ArrayStack<E> {
public class ArrayStack<E> implements Iterable<E> {

private final List<E> list;

Expand Down Expand Up @@ -89,4 +90,9 @@ public int size() {
public boolean contains(E o) {
return list.contains(o);
}

@Override
public Iterator<E> iterator() {
return list.iterator();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package org.tests.transaction;

import io.ebean.*;
import io.ebean.xtest.BaseTestCase;
import io.ebean.xtest.ForPlatform;
import io.ebean.DB;
import io.ebean.DataIntegrityException;
import io.ebean.Transaction;
import io.ebean.TxScope;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.annotation.Transactional;
import io.ebean.xtest.BaseTestCase;
import io.ebean.xtest.ForPlatform;
import io.ebeaninternal.api.SpiTransaction;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.Customer;
Expand Down Expand Up @@ -43,7 +46,7 @@ public void nestedExecute_when_errorOnCommit_threadLocalIsCleared() {

try {
DB.execute(TxScope.required().setBatch(PersistBatch.ALL), () ->
DB.execute(() -> {
DB.execute(() -> {

Customer customer = DB.reference(Customer.class, 42424242L);
Order order = new Order();
Expand Down Expand Up @@ -158,4 +161,26 @@ public void no_transaction_expect_threadScopeCleanup() {
assertThat(getInScopeTransaction()).isNull();
}

@ForPlatform(Platform.H2)
@Test
public void test_nested_userobjects() {

try (Transaction txn1 = DB.beginTransaction()) {
assertThat(getInScopeTransaction()).isNotNull();
getInScopeTransaction().putUserObject("foo", "bar");

try (Transaction txn2 = DB.beginTransaction()) {
assertThat(getInScopeTransaction().getUserObject("foo")).isEqualTo("bar");
getInScopeTransaction().putUserObject("foo", "xxx");
getInScopeTransaction().putUserObject("test", "xxx");
assertThat(getInScopeTransaction().getUserObject("foo")).isEqualTo("xxx");
txn2.commit();
}
// CHECKME: What would we expect here? I would expect "bar" but get "xxx"
// NOTE: with TxScope.requiresNew() - I'll get "bar"
assertThat(getInScopeTransaction().getUserObject("test")).isNull();
assertThat(getInScopeTransaction().getUserObject("foo")).isEqualTo("bar");
}
}

}

0 comments on commit 839bba9

Please sign in to comment.