Merge branch 'master' of github.com:ebean-orm/ebean

This commit is contained in:
Rob Bygrave
2022-11-02 15:18:22 +13:00
12 changed files with 99 additions and 29 deletions
@@ -6,6 +6,10 @@ import java.util.concurrent.Callable;
* BackgroundExecutorWrapper that can be used to wrap tasks that are sent to background (i.e. another thread).
* It should copy all necessary thread-local variables. See {@link MdcBackgroundExecutorWrapper} for implementation details.
*
* Note: only tasks that are executed immediately (submit, execute) are wrapped. Periodic or scheduled tasks are not wrapped,
* as these may keep copied variables in memory either forever or until the scheduled task is finished.
* The caller is responsible to handle these cases.
*
* @author Roland Praml, FOCONIS AG
*/
public interface BackgroundExecutorWrapper {
@@ -121,7 +121,7 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequ
<K> Map<K, T> findMap();
/**
* Execute the findSingleAttributeList query.
* Execute the findSingleAttributeCollection query.
*/
<A extends Collection<?>> A findSingleAttributeCollection(A collection);
@@ -106,23 +106,23 @@ public final class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) {
return schedulePool.scheduleWithFixedDelay(wrap(logExceptions(task)), initialDelay, delay, unit);
return schedulePool.scheduleWithFixedDelay(logExceptions(task), initialDelay, delay, unit);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long delay, TimeUnit unit) {
return schedulePool.scheduleAtFixedRate(wrap(logExceptions(task)), initialDelay, delay, unit);
return schedulePool.scheduleAtFixedRate(logExceptions(task), initialDelay, delay, unit);
}
@Override
public ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit) {
return schedulePool.schedule(wrap(logExceptions(task)), delay, unit);
return schedulePool.schedule(logExceptions(task), delay, unit);
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
// Note: No "logExceptions" as we expect Future.get() by the invoker
return schedulePool.schedule(wrap(task), delay, unit);
return schedulePool.schedule(task, delay, unit);
}
@Override
@@ -106,7 +106,7 @@ public class UuidV1RndIdGenerator implements PlatformIdGenerator {
delta = current - last;
if (delta < -10000 * 20000) {
log.log(INFO, "Clock skew of {} ms detected", delta / -10000);
log.log(INFO, "Clock skew of {0} ms detected", delta / -10000);
// The clock was adjusted back about 2 seconds, or we were generating a lot of ids too fast
// if so, we try to set the current as last and also increment the clockSeq.
lock.lock();
@@ -885,6 +885,9 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
public final void preCommit() {
internalBatchFlush();
firePreCommit();
// we must flush the batch queue again, because the callback can
// modify current transaction
internalBatchFlush();
}
/**
@@ -61,7 +61,7 @@ final class ScalarTypeCalendar extends ScalarTypeBaseDateTime<Calendar> {
@Override
protected String toJsonNanos(Calendar value) {
return String.valueOf(value.getTime());
return String.valueOf(value.getTime().getTime());
}
@Override
@@ -11,6 +11,7 @@ import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeParseException;
/**
* ScalarType for java.sql.Timestamp.
@@ -48,7 +49,7 @@ final class ScalarTypeLocalDateTime extends ScalarTypeBaseDateTime<LocalDateTime
@Override
public LocalDateTime jsonRead(JsonParser parser) throws IOException {
return LocalDateTime.parse(parser.getText());
return parse(parser.getText());
}
@Override
@@ -63,7 +64,11 @@ final class ScalarTypeLocalDateTime extends ScalarTypeBaseDateTime<LocalDateTime
@Override
public LocalDateTime parse(String value) {
return LocalDateTime.parse(value);
try {
return LocalDateTime.parse(value);
} catch (DateTimeParseException pe) {
return super.parse(value);
}
}
@Override
@@ -2,15 +2,20 @@ package io.ebeaninternal.server.type;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import io.ebean.config.JsonConfig;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.StringWriter;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.TimeZone;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ScalarTypeLocalDateTimeTest {
@@ -123,4 +128,35 @@ public class ScalarTypeLocalDateTimeTest {
LocalDateTime value = typeIso.fromJsonISO8601(asJson);
assertThat(localDateTime).isEqualToIgnoringNanos(value);
}
@Test
public void testParseEbean11() throws IOException {
ScalarTypeLocalDateTime type = new ScalarTypeLocalDateTime(JsonConfig.DateTime.ISO8601);
JsonFactory factory = new JsonFactory();
JsonParser parser11 = factory.createParser("1517627106000"); // its a number!
JsonParser parser13 = factory.createParser("\"2022-01-01T01:00:00\"");
// test parsing an ebean 11/13 timestamp, we do not expect an exception
LocalDateTime p = type.parse("1517627106000");
parser11.nextToken();
LocalDateTime q = type.jsonRead(parser11);
assertThat(p).isEqualTo(q);
p = type.parse("2022-01-01T01:00:00");
parser13.nextToken();
q = type.jsonRead(parser13);
assertThat(p).isEqualTo(q);
TimeZone tz = TimeZone.getDefault();
try {
// Adjust Timezone to run on build server
// Note Millis and Iso have a time offset according to TZ
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
LocalDateTime fromMillis = type.parse("0");
LocalDateTime fromIso = type.parse("1970-01-01T01:00");
assertThat(fromMillis).isEqualToIgnoringNanos(fromIso);
} finally {
TimeZone.setDefault(tz);
}
}
}
@@ -11,10 +11,11 @@ import java.io.Reader;
import java.net.URL;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
class CsvReaderTest {
final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy");
final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy", Locale.ENGLISH);
@Test
void test() throws Exception {
@@ -42,7 +42,7 @@ public class ExtraDdlXmlReader {
StringBuilder sb = new StringBuilder(300);
for (DdlScript script : read.getDdlScript()) {
if (script.isDrop() == drops && matchPlatform(platform, script.getPlatforms())) {
logger.log(DEBUG, "include script {}", script.getName());
logger.log(DEBUG, "include script {0}", script.getName());
String value = script.getValue();
sb.append(value);
if (value.lastIndexOf(';') == -1) {
@@ -105,4 +105,25 @@ public class JdbcTransactionTest {
assertThat(postCommitCallCount.get()).isEqualTo(2); // postcommit executed twice
}
}
@Test
public void testFlushInCallback() {
try (Transaction transaction = DB.beginTransaction()) {
transaction.setBatchMode(true);
DB.currentTransaction().register(
new TransactionCallbackAdapter() {
@Override
public void preCommit() {
EBasic basic = new EBasic("binner1");
DB.save(basic);
}
}
);
EBasic basic = new EBasic("bouter1");
DB.save(basic);
transaction.commit(); // transaction will fail if recursive post-commit is failing
}
assertThat(DB.find(EBasic.class).where().eq("name", "binner1").exists()).isTrue();
}
}
+17 -17
View File
@@ -91,12 +91,12 @@ public class TestQueryCache extends BaseTestCase {
// ensure that findCount & findSingleAttribute use different
// slots in cache. If not a "Cannot cast List to int" should happen.
int count = DB
.find(EColAB.class)
.setUseQueryCache(true)
.select("columnA")
.where()
.eq("columnB", "SingleAttribute")
.findCount();
.find(EColAB.class)
.setUseQueryCache(true)
.select("columnA")
.where()
.eq("columnB", "SingleAttribute")
.findCount();
assertThat(count).isEqualTo(2);
}
@@ -305,7 +305,7 @@ public class TestQueryCache extends BaseTestCase {
assertSame(list, list2B);
List<Customer> list3 = DB.find(Customer.class).setUseQueryCache(true).setReadOnly(false).where()
.ilike("name", "Rob").findList();
.ilike("name", "Rob").findList();
assertNotSame(list, list3);
BeanCollection<Customer> bc3 = (BeanCollection<Customer>) list3;
@@ -349,10 +349,10 @@ public class TestQueryCache extends BaseTestCase {
// and now, ensure that we hit the database
LoggedSql.start();
colA_second = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.PUT)
.where()
.eq("columnB", "someId")
.findIds();
.setUseQueryCache(CacheMode.PUT)
.where()
.eq("columnB", "someId")
.findIds();
sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
@@ -361,15 +361,15 @@ public class TestQueryCache extends BaseTestCase {
@Test
public void findCountDifferentQueriesBit() {
DB.getDefault().pluginApi().cacheManager().clearAll();
differentFindCount(q->q.bitwiseAny("id",1), q->q.bitwiseAny("id",0));
differentFindCount(q->q.bitwiseAll("id",1), q->q.bitwiseAll("id",0));
differentFindCount(q -> q.bitwiseAny("id", 1), q -> q.bitwiseAny("id", 0));
differentFindCount(q -> q.bitwiseAll("id", 1), q -> q.bitwiseAll("id", 0));
// differentFindCount(q->q.bitwiseNot("id",1), q->q.bitwiseNot("id",0)); NOT 1 == AND 1 = 0
differentFindCount(q->q.bitwiseAnd("id",1, 0), q->q.bitwiseAnd("id",1, 1));
differentFindCount(q -> q.bitwiseAnd("id", 1, 0), q -> q.bitwiseAnd("id", 1, 1));
differentFindCount(q->q.bitwiseAnd("id",2, 0), q->q.bitwiseAnd("id",4, 0));
differentFindCount(q->q.bitwiseAnd("id",2, 1), q->q.bitwiseAnd("id",4, 1));
differentFindCount(q -> q.bitwiseAnd("id", 2, 0), q -> q.bitwiseAnd("id", 4, 0));
differentFindCount(q -> q.bitwiseAnd("id", 2, 1), q -> q.bitwiseAnd("id", 4, 1));
// Will produce hash collision
differentFindCount(q->q.bitwiseAnd("id",10, 0), q->q.bitwiseAnd("id",0, 928210));
differentFindCount(q -> q.bitwiseAnd("id", 10, 0), q -> q.bitwiseAnd("id", 0, 928210));
}