mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Merge pull request #3289 from FOCONIS/fix-nested-user-objects
Change semantic of userObjects in nested transaction
This commit is contained in:
@@ -3,6 +3,8 @@ package io.ebeaninternal.api;
|
||||
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.
|
||||
@@ -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;
|
||||
@@ -218,4 +225,17 @@ public final class ScopeTrans {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.transaction.TransactionScopeManager;
|
||||
import io.ebeaninternal.server.util.ArrayStack;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
|
||||
/**
|
||||
@@ -173,4 +172,31 @@ public final class ScopedTransaction extends SpiTransactionProxy {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ package io.ebeaninternal.server.util;
|
||||
|
||||
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;
|
||||
|
||||
@@ -89,4 +90,9 @@ public class ArrayStack<E> {
|
||||
public boolean contains(E o) {
|
||||
return list.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return list.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -43,7 +46,7 @@ public class TestExecuteComplete extends BaseTestCase {
|
||||
|
||||
try {
|
||||
DB.execute(TxScope.required().setBatch(PersistBatch.ALL), () ->
|
||||
DB.execute(() -> {
|
||||
DB.execute(() -> {
|
||||
|
||||
Customer customer = DB.reference(Customer.class, 42424242L);
|
||||
Order order = new Order();
|
||||
@@ -158,4 +161,26 @@ public class TestExecuteComplete extends BaseTestCase {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user