package com.github.davidmoten.rx.jdbc;
import static com.github.davidmoten.rx.Functions.constant;
import static com.github.davidmoten.rx.RxUtil.log;
import static com.github.davidmoten.rx.RxUtil.toEmpty;
import static com.github.davidmoten.rx.jdbc.DatabaseCreator.connectionProvider;
import static com.github.davidmoten.rx.jdbc.DatabaseCreator.createDatabase;
import static com.github.davidmoten.rx.jdbc.DatabaseCreator.nextUrl;
import static com.github.davidmoten.rx.jdbc.TestingUtil.countDown;
import static java.util.Arrays.asList;
import static org.easymock.EasyMock.createMock;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static rx.Observable.just;
import java.io.IOException;
import java.io.Reader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.easymock.EasyMock;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.davidmoten.rx.RxUtil;
import com.github.davidmoten.rx.jdbc.annotations.Column;
import com.github.davidmoten.rx.jdbc.annotations.Index;
import com.github.davidmoten.rx.jdbc.annotations.Query;
import com.github.davidmoten.rx.jdbc.exceptions.TransactionAlreadyOpenException;
import com.github.davidmoten.rx.jdbc.tuple.Tuple2;
import com.github.davidmoten.rx.jdbc.tuple.Tuple3;
import com.github.davidmoten.rx.jdbc.tuple.Tuple4;
import com.github.davidmoten.rx.jdbc.tuple.Tuple5;
import com.github.davidmoten.rx.jdbc.tuple.Tuple6;
import com.github.davidmoten.rx.jdbc.tuple.Tuple7;
import com.github.davidmoten.rx.jdbc.tuple.TupleN;
import com.zaxxer.hikari.HikariDataSource;
import rx.Observable;
import rx.Observable.Transformer;
import rx.Observer;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.observables.MathObservable;
import rx.observers.TestSubscriber;
public abstract class DatabaseTestBase {
/**
* <p>
* Timeout used for latch.await() and similar. If too short then short
* machine lockups on build server (which is known to happen on CloudBees
* Jenkins infrastructure) will cause undesired test failures.
* </p>
*
* <p>
* While your test is still failing use a lower value of course so that you
* get rapid turnaround. However, once passing switch the timeout to this
* standard timeout value.
* </p>
*/
private static final int TIMEOUT_SECONDS = 3;
private static final Logger log = LoggerFactory.getLogger(DatabaseTestBase.class);
private final boolean async;
public DatabaseTestBase(boolean async) {
this.async = async;
}
Database db() {
if (async)
return DatabaseCreator.db().asynchronous();
else
return DatabaseCreator.db();
}
@Test
public void testSimpleExample() {
Observable<String> names = db().select("select name from person order by name")
.getAs(String.class);
// convert the names to a list for unit test
List<String> list = names.toList().toBlocking().single();
log.debug("list=" + list);
assertEquals(asList("FRED", "JOSEPH", "MARMADUKE"), list);
}
@Test
public void testCountQuery() {
int count = db()
// select names
.select("select name from person where name >?")
// set name parameter
.parameter("ALEX")
// count results
.count()
// get count
.first()
// block till finished
.toBlocking().single();
assertEquals(3, count);
}
@Test
public void testTransactionUsingCount() {
Database db = db();
Func1<? super Integer, Boolean> isZero = new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer t1) {
return t1 == 0;
}
};
Observable<Integer> existingRows = db
// select names
.select("select name from person where name=?")
// set name parameter
.parameter("FRED")
// is part of transaction
.dependsOn(db.beginTransaction())
// get result count
.count()
// return empty if count = 0
.filter(isZero);
Observable<Integer> update = db
// insert record if does not exist
.update("insert into person(name,score) values(?,0)")
// get parameters from last query
.parameters(existingRows.map(constant("FRED")))
// return num rows affected
.count();
boolean committed = db.commit(update).toBlocking().single();
assertTrue(committed);
}
@Test
public void testTransactionOnCommit() {
Database db = db();
Observable<Boolean> begin = db.beginTransaction();
Observable<Integer> updateCount = db
// set everyones score to 99
.update("update person set score=?")
// is within transaction
.dependsOn(begin)
// new score
.parameter(99)
// execute
.count();
Observable<Boolean> commit = db.commit(updateCount);
long count = db.select("select count(*) from person where score=?")
// set score
.parameter(99)
// depends on
.dependsOn(commit)
// return as Long
.getAs(Long.class)
// log
.doOnEach(RxUtil.log())
// get answer
.toBlocking().single();
assertEquals(3, count);
}
@Test
public void testSelectErrorResetsTransactionContextInDatabaseClass() {
Database db = db();
Observable<Integer> select = db
// select names
.select("select namez from person where name=?")
// set name parameter
.parameter("FRED")
// is part of transaction
.dependsOn(db.beginTransaction())
// get result count
.count();
final AtomicBoolean transactionClosed = new AtomicBoolean(true);
db.commit(select).doOnError(new Action1<Throwable>() {
@Override
public void call(Throwable t) {
System.out.println(t.getMessage());
if (t instanceof TransactionAlreadyOpenException)
transactionClosed.set(false);
}
}).retry(1).subscribe(ignore());
assertTrue(transactionClosed.get());
}
@Test
public void testUpdateErrorResetsTransactionContextInDatabaseClass() {
Database db = db();
Observable<Integer> update = db
// select names
.update("zzz")
// set name parameter
.parameter("FRED")
// is part of transaction
.dependsOn(db.beginTransaction())
// get result count
.count();
final AtomicBoolean transactionClosed = new AtomicBoolean(true);
db.commit(update).doOnError(new Action1<Throwable>() {
@Override
public void call(Throwable t) {
System.out.println(t.getMessage());
if (t instanceof TransactionAlreadyOpenException)
transactionClosed.set(false);
}
}).retry(1).subscribe(ignore());
assertTrue(transactionClosed.get());
}
private static <T> Observer<T> ignore() {
return new Observer<T>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(T t) {
}
};
}
@Test
public void testPushEmptyList() {
Database db = db();
Observable<Integer> rowsAffected = Observable
// generate two integers
.range(1, 2)
// replace the integers with empty lists
.map(toEmpty())
// execute the update
.compose(
db.update("update person set score = score + 1").parameterListTransformer())
// flatten
.compose(RxUtil.<Integer> flatten())
// total the affected records
.compose(SUM_INTEGER);
assertIs(6, rowsAffected);
}
@Test
public void testRunScript() {
Observable<String> commands = just("create table temp1(id integer)", "drop table temp1");
db().run(commands).count().toBlocking().single();
}
@Test
public void testTransactionOnCommitDoesntOccurUnlessSubscribedTo() {
Database db = db();
Observable<Boolean> begin = db.beginTransaction();
Observable<Integer> u = db.update("update person set score=?").dependsOn(begin)
.parameter(99).count();
db.commit(u);
// note that last transaction was not listed as a dependency of the next
// query
long count = db.select("select count(*) from person where score=?").parameter(99)
.getAs(Long.class).toBlocking().single();
assertEquals(0, count);
}
@Test
public void testTransactionOnRollback() {
Database db = db();
Observable<Boolean> begin = db.beginTransaction();
Observable<Integer> updateCount = db.update("update person set score=?").dependsOn(begin)
.parameter(99).count();
db.rollback(updateCount);
long count = db.select("select count(*) from person where score=?").parameter(99)
.dependsOnLastTransaction().getAs(Long.class).toBlocking().single();
assertEquals(0, count);
}
@Test
public void testUpdateAndSelectWithTransaction() {
Database db = db();
Observable<Boolean> begin = db.beginTransaction();
Observable<Integer> updateCount = db
// update everyone's score to 99
.update("update person set score=?")
// in transaction
.dependsOn(begin)
// new score
.parameter(99)
// execute
.count();
long count = db.select("select count(*) from person where score=?")
// where score = 99
.parameter(99)
// depends on
.dependsOn(updateCount)
// as long value
.getAs(Long.class).toBlocking().single();
assertEquals(3, count);
}
@Test
public void testUseParameterObservable() {
int count = db().select("select name from person where name >?")
.parameters(Observable.just("ALEX")).count().toBlocking().single();
assertEquals(3, count);
}
@Test
public void testTwoParameters() {
List<String> list = db().select("select name from person where name > ? and name < ?")
.parameter("ALEX").parameter("LOUIS").getAs(String.class).toList().toBlocking()
.single();
assertEquals(asList("FRED", "JOSEPH"), list);
}
@Test
public void testTakeFewerThanAvailable() {
int count = db().select("select name from person where name >?").parameter("ALEX")
.get(new ResultSetMapper<Integer>() {
@Override
public Integer call(ResultSet rs) throws SQLException {
return 1;
}
}).take(2).count().first().toBlocking().single();
assertEquals(2, count);
}
@Test
public void testJdbcObservableCountLettersInAllNames() {
int count = MathObservable
.sumInteger(db()
// select
.select("select name from person where name >?")
// set name
.parameter("ALEX")
// count letters
.get(COUNT_LETTERS_IN_NAME))
// first result
.first()
// block and get result
.toBlocking().single();
assertEquals(19, count);
}
private static final ResultSetMapper<Integer> COUNT_LETTERS_IN_NAME = new ResultSetMapper<Integer>() {
@Override
public Integer call(ResultSet rs) throws SQLException {
return rs.getString("name").length();
}
};
@Test
public void testTransformToTuple2AndTestActionsPrintln() {
Tuple2<String, Integer> tuple = db()
.select("select name,score from person where name >? order by name")
.parameter("ALEX").getAs(String.class, Integer.class).last().toBlocking().single();
assertEquals("MARMADUKE", tuple.value1());
assertEquals(25, (int) tuple.value2());
}
@Test
public void testTransformToTupleN() {
TupleN<String> tuple = db().select("select name, lower(name) from person order by name")
.getTupleN(String.class).first().toBlocking().single();
assertEquals("FRED", tuple.values().get(0));
assertEquals("fred", tuple.values().get(1));
}
@Test
public void testMultipleSetsOfParameters() {
List<Integer> list = db().select("select score from person where name=?")
// first param
.parameter("FRED")
// second param
.parameter("JOSEPH")
// score as integer
.getAs(Integer.class)
// log
.doOnEach(log())
// sort
.toSortedList()
// block and get
.toBlocking().single();
assertEquals(asList(21, 34), list);
}
@Test
public void testNoParams() {
List<Tuple2<String, Integer>> tuples = db()
.select("select name, score from person where name=? order by name")
.getAs(String.class, Integer.class).toList().toBlocking().single();
assertEquals(0, tuples.size());
}
@Test
public void testCreateFromScript() {
Database db = Database.from(DatabaseCreator.nextUrl());
Observable<Integer> create = db
.run(DatabaseTestBase.class.getResourceAsStream("/db-creation-script.sql"), ";");
Observable<Integer> count = db.select("select name from person").dependsOn(create)
.getAs(String.class).count();
assertIs(3, count);
}
@Test
public void testComposition2() {
log.debug("running testComposition2");
Func1<Integer, Boolean> isZero = new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer count) {
return count == 0;
}
};
Database db = db();
Observable<Integer> existingRows = db.select("select name from person where name=?")
.parameter("FRED").getAs(String.class).count().filter(isZero);
List<Integer> counts = db.update("insert into person(name,score) values(?,?)")
.parameters(existingRows).count().toList().toBlocking().single();
assertEquals(0, counts.size());
}
@Test
public void testEmptyResultSet() {
int count = db().select("select name from person where name >?")
.parameters(Observable.just("ZZTOP")).count().first().toBlocking().single();
assertEquals(0, count);
}
@Test
public void testMixingExplicitAndObservableParameters() {
String name = db()
.select("select name from person where name > ? and score < ? order by name")
.parameter("BARRY").parameters(Observable.just(100)).getAs(String.class).first()
.toBlocking().single();
assertEquals("FRED", name);
}
@Test
public void testInstantiateDatabaseWithUrl() throws SQLException {
Database db = Database.from("jdbc:h2:mem:testa1");
Connection con = db.queryContext().connectionProvider().get();
con.close();
}
@Test
public void testComposition() {
// use composition to find the first person alphabetically with
// a score less than the person with the last name alphabetically
// whose name is not XAVIER. Two threads and connections will be used.
Database db = db();
Observable<Integer> score = db
.select("select score from person where name <> ? order by name")
.parameter("XAVIER").getAs(Integer.class).last();
Observable<String> name = db.select("select name from person where score < ? order by name")
.parameters(score).getAs(String.class).first();
assertIs("FRED", name);
}
@Test
public void testCompositionUsingCompose() {
// use composition to find the first person alphabetically with
// a score less than the person with the last name alphabetically
// whose name is not XAVIER. Two threads and connections will be used.
Database db = db();
Observable<String> name = db
.select("select score from person where name <> ? order by name")
.parameter("XAVIER").getAs(Integer.class).last()
.compose(db.select("select name from person where score < ? order by name")
.parameterTransformer().getAs(String.class))
.first();
assertIs("FRED", name);
}
@Test
public void testCompositionTwoLevels() {
Database db = db();
Observable<String> names = db.select("select name from person order by name")
.getAs(String.class);
Observable<String> names2 = db.select("select name from person where name<>? order by name")
.parameters(names).parameters(names).getAs(String.class);
List<String> list = db.select("select name from person where name>?").parameters(names2)
.getAs(String.class).toList().toBlocking().single();
System.out.println(list);
assertEquals(12, list.size());
}
@Test(expected = RuntimeException.class)
public void testSqlProblem() {
String name = db().select("select name from pperson where name >?").parameter("ALEX")
.getAs(String.class).first().toBlocking().single();
log.debug(name);
}
@Test(expected = ClassCastException.class)
public void testException() {
Integer name = db().select("select name from person where name >?").parameter("ALEX")
.getAs(Integer.class).first().toBlocking().single();
log.debug("name=" + name);
}
@Test
public void testAutoMapWillMapStringToStringAndIntToDouble() {
Person person = db().select("select name,score,dob,registered from person order by name")
.autoMap(Person.class).first().toBlocking().single();
assertEquals("FRED", person.getName());
assertEquals(21, person.getScore(), 0.001);
assertNull(person.getDateOfBirth());
}
@Test(expected = RuntimeException.class)
public void testAutoMapCannotFindConstructorWithEnoughParameters() {
db().select("select name,score,dob,registered,name from person order by name")
.autoMap(Person.class).first().toBlocking().single();
}
@Test
public void testGetTimestamp() {
Database db = db();
java.sql.Timestamp registered = new java.sql.Timestamp(100);
Observable<Integer> u = db.update("update person set registered=? where name=?")
.parameter(registered).parameter("FRED").count();
Date regTime = db.select("select registered from person order by name").dependsOn(u)
.getAs(Date.class).first().toBlocking().single();
assertEquals(100, regTime.getTime());
}
@Test
public void insertClobAndReadAsString() throws SQLException {
Database db = db();
insertClob(db);
// read clob as string
String text = db.select("select document from person_clob").getAs(String.class).first()
.toBlocking().single();
assertTrue(text.contains("about Fred"));
}
@Test
public void insertNullClobAndReadAsString() throws SQLException {
Database db = db();
insertClob(db, null);
// read clob as string
String text = db.select("select document from person_clob").getAs(String.class).first()
.toBlocking().single();
assertNull(text);
}
@Test
public void insertNullBlobAndReadAsByteArray() throws SQLException {
Database db = db();
insertBlob(db, null);
// read clob as string
byte[] bytes = db.select("select document from person_blob").getAs(byte[].class).first()
.toBlocking().single();
assertNull(bytes);
}
private static void insertClob(Database db) {
insertClob(db, "A description about Fred that is rather long and needs a Clob to store it");
}
private static void insertClob(Database db, String value) {
Observable<Integer> count = db.update("insert into person_clob(name,document) values(?,?)")
.parameter("FRED").parameterClob(value).count();
assertIs(1, count);
}
private static void insertBlob(Database db, byte[] bytes) {
Observable<Integer> count = db.update("insert into person_blob(name,document) values(?,?)")
.parameter("FRED").parameterBlob(bytes).count();
assertIs(1, count);
}
@Test
public void insertClobAndReadAsReader() throws SQLException, IOException {
Database db = db();
insertClob(db);
// read clob as Reader
String text = db.select("select document from person_clob").getAs(Reader.class)
.map(Util.READER_TO_STRING).first().toBlocking().single();
assertTrue(text.contains("about Fred"));
}
@Test
public void insertBlobAndReadAsByteArray() throws SQLException {
Database db = db();
insertBlob(db);
// read clob as string
byte[] bytes = db.select("select document from person_blob").getAs(byte[].class).first()
.toBlocking().single();
assertTrue(new String(bytes).contains("about Fred"));
}
@Test
public void testInsertNull() {
Observable<Integer> count = db().update("insert into person(name,score,dob) values(?,?,?)")
.parameters("JACK", 42, null).count();
assertIs(1, count);
}
@Test
public void testRC4() {
Observable.<Object> empty().concatWith(just(10, 20, 30)).buffer(3)
.concatMap(new Func1<List<Object>, Observable<Object>>() {
@Override
public Observable<Object> call(List<Object> list) {
return Observable.from(list);
}
}).count().toBlocking().single();
// Observable<Integer> count = db()
// .update("insert into person(name,score,dob) values(?,?,?)")
// .parameters("JACK", 42, null).count();
// assertIs(1, count);
}
@Test
public void testAutoMap() {
TimeZone current = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("AEST"));
Database db = db();
Date dob = new Date(100);
long now = System.currentTimeMillis();
java.sql.Timestamp registered = new java.sql.Timestamp(now);
Observable<Integer> u = db.update("update person set dob=?, registered=? where name=?")
.parameter(dob).parameter(registered).parameter("FRED").count();
Person person = db.select("select name,score,dob,registered from person order by name")
.dependsOn(u).autoMap(Person.class).first().toBlocking().single();
assertEquals("FRED", person.getName());
assertEquals(21, person.getScore(), 0.001);
// Dates are truncated to start of day
assertEquals(0, (long) person.getDateOfBirth());
assertEquals(now, (long) person.getRegistered());
} finally {
TimeZone.setDefault(current);
}
}
@Test
public void testLastTransactionWithoutTransaction() {
assertIs(0, db().lastTransactionResult().count());
}
@Test
public void testTuple3() {
Tuple3<String, Integer, String> tuple = db()
.select("select name,1,lower(name) from person order by name")
.getAs(String.class, Integer.class, String.class).first().toBlocking().single();
assertEquals("FRED", tuple.value1());
assertEquals(1, (int) tuple.value2());
assertEquals("fred", tuple.value3());
}
@Test
public void testTuple4() {
Tuple4<String, Integer, String, Integer> tuple = db()
.select("select name,1,lower(name),2 from person order by name")
.getAs(String.class, Integer.class, String.class, Integer.class).first()
.toBlocking().single();
assertEquals("FRED", tuple.value1());
assertEquals(1, (int) tuple.value2());
assertEquals("fred", tuple.value3());
assertEquals(2, (int) tuple.value4());
}
@Test
public void testTuple5() {
Tuple5<String, Integer, String, Integer, String> tuple = db()
.select("select name,1,lower(name),2,name from person order by name")
.getAs(String.class, Integer.class, String.class, Integer.class, String.class)
.first().toBlocking().single();
assertEquals("FRED", tuple.value1());
assertEquals(1, (int) tuple.value2());
assertEquals("fred", tuple.value3());
assertEquals(2, (int) tuple.value4());
assertEquals("FRED", tuple.value5());
}
@Test
public void testTuple6() {
Tuple6<String, Integer, String, Integer, String, Integer> tuple = db()
.select("select name,1,lower(name),2,name,3 from person order by name")
.getAs(String.class, Integer.class, String.class, Integer.class, String.class,
Integer.class)
.first().toBlocking().single();
assertEquals("FRED", tuple.value1());
assertEquals(1, (int) tuple.value2());
assertEquals("fred", tuple.value3());
assertEquals(2, (int) tuple.value4());
assertEquals("FRED", tuple.value5());
assertEquals(3, (int) tuple.value6());
}
@Test
public void testTuple7() {
Tuple7<String, Integer, String, Integer, String, Integer, Integer> tuple = db()
.select("select name,1,lower(name),2,name,3,4 from person order by name")
.getAs(String.class, Integer.class, String.class, Integer.class, String.class,
Integer.class, Integer.class)
.first().toBlocking().single();
assertEquals("FRED", tuple.value1());
assertEquals(1, (int) tuple.value2());
assertEquals("fred", tuple.value3());
assertEquals(2, (int) tuple.value4());
assertEquals("FRED", tuple.value5());
assertEquals(3, (int) tuple.value6());
assertEquals(4, (int) tuple.value7());
}
@Test
public void testAutoMapClob() {
Database db = db();
insertClob(db);
List<PersonClob> list = db.select("select name, document from person_clob")
.autoMap(PersonClob.class).toList().toBlocking().single();
assertEquals(1, list.size());
assertEquals("FRED", list.get(0).getName());
assertTrue(list.get(0).getDocument().contains("rather long"));
}
@Test
public void testAutoMapBlob() {
Database db = db();
insertBlob(db);
List<PersonBlob> list = db.select("select name, document from person_blob")
.autoMap(PersonBlob.class).toList().toBlocking().single();
assertEquals(1, list.size());
assertEquals("FRED", list.get(0).getName());
assertTrue(new String(list.get(0).getDocument()).contains("rather long"));
}
private void insertBlob(Database db) {
insertBlob(db, "A description about Fred that is rather long and needs a Clob to store it"
.getBytes());
}
@Test
public void testCalendarParameter() throws SQLException {
Database db = db();
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(0);
Observable<Integer> update = db.update("update person set registered=? where name=?")
.parameters(cal, "FRED").count();
Timestamp t = db.select("select registered from person where name=?").parameter("FRED")
.dependsOn(update).getAs(Timestamp.class).first().toBlocking().single();
assertEquals(0, t.getTime());
}
@Test
public void testDatabaseBuilder() {
Database.builder().connectionProvider(connectionProvider())
.nonTransactionalSchedulerOnCurrentThread().build();
}
@Test
public void testConnectionPool() {
ConnectionProviderPooled cp = new ConnectionProviderPooled(nextUrl(), 0, 10);
Database db = createDatabase(cp);
int count = db.select("select name from person order by name").count().toBlocking()
.single();
assertEquals(3, count);
cp.close();
// and again to test idempotentcy
cp.close();
}
@Test(expected = RuntimeException.class)
public void testConnectionPoolWhenExceptionThrown() throws SQLException {
HikariDataSource pool = new HikariDataSource();
pool.setJdbcUrl("invalid");
new ConnectionProviderPooled(pool).get();
}
@Test
public void testConnectionPoolDoesNotRunOutOfConnectionsWhenQueryRunRepeatedly()
throws SQLException {
ConnectionProviderPooled cp = new ConnectionProviderPooled(nextUrl(), 0, 1);
Database db = new Database(cp);
Connection con = cp.get();
DatabaseCreator.createDatabase(con);
con.close();
assertIs(100, db.select("select name from person where name=?")
.parameters(Observable.range(0, 100).map(constant("FRED"))).count());
}
// TODO add unit test to check that resources closed (connection etc) before
// onComplete or onError called on either select or update
@Test
public void testDatabaseBuilderWithPool() {
Database.builder().url(nextUrl()).pool(0, 5).build().close();
}
@Test
public void testDatabaseBuilderWithPoolAndConfigured() {
Database.builder().url(nextUrl()).pool(0, 5).configure(new Action1<HikariDataSource>() {
@Override
public void call(HikariDataSource ds) {
ds.setConnectionTimeout(10000);
}}).build().close();
}
@Test(expected=IllegalArgumentException.class)
public void testDatabaseBuilderWithPoolAndIllegallyConfiguredThrows() {
Database.builder().url(nextUrl()).pool(0, 5).configure(new Action1<HikariDataSource>() {
@Override
public void call(HikariDataSource ds) {
//too short!
ds.setConnectionTimeout(1);
}}).build().close();
}
@Test
public void testOneConnectionOpenAndClosedAfterOneSelect() throws InterruptedException {
CountDownConnectionProvider cp = new CountDownConnectionProvider(1, 1);
Database db = new Database(cp);
db.select("select name from person").count().toBlocking().single();
cp.closesLatch().await();
cp.getsLatch().await();
}
@Test
public void testOneConnectionOpenAndClosedAfterOneUpdate() throws InterruptedException {
CountDownConnectionProvider cp = new CountDownConnectionProvider(1, 1);
Database db = new Database(cp);
db.update("update person set score=? where name=?").parameters(23, "FRED").count()
.toBlocking().single();
cp.closesLatch().await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
cp.getsLatch().await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void testComposeWithParameters() {
int score = just("FRED").compose(db().select("select score from person where name=?")
.parameterTransformer().getAs(Integer.class)).toBlocking().single();
assertEquals(21, score);
}
@Test
public void testcomposeWithManyParameters() {
int score = Observable
// range
.range(1, 3)
// log
.doOnEach(log())
// to parameter
.map(constant("FRED"))
.compose(db()
// select
.select("select score from person where name=?")
// push parameters
.parameterTransformer()
// get score as integer
.getAs(Integer.class))
// sum values
.compose(SUM_INTEGER)
// block and get
.toBlocking().single();
assertEquals(3 * 21, score);
}
private final Transformer<Integer, Integer> SUM_INTEGER = new Transformer<Integer, Integer>() {
@Override
public Observable<Integer> call(Observable<Integer> source) {
return MathObservable.sumInteger(source);
}
};
@Test
public void testDetector() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
Observable.range(1, 10).doOnUnsubscribe(countDown(latch)).take(1).toBlocking().single();
assertTrue(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));
}
@Test
public void testUnsubscribeOfBufferAndFlatMap() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
Observable.interval(10, TimeUnit.MILLISECONDS).doOnUnsubscribe(countDown(latch)).buffer(2)
.flatMap(constant(just(1L))).take(6).toList().toBlocking().single();
assertTrue(latch.await(3, TimeUnit.SECONDS));
}
@Test
public void testParametersAreUnsubscribedIfUnsubscribedPostParameterOperatorcompose()
throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
Observable.interval(100, TimeUnit.MILLISECONDS).doOnEach(log()).map(constant("FRED"))
.doOnUnsubscribe(countDown(latch))
.compose(db().select("select score from person where name=?").parameterTransformer()
.getAs(Integer.class))
.take(1).subscribe(log());
assertTrue(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));
}
@Test
public void testParametersAreUnsubscribed() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
Observable<String> params = Observable.interval(100, TimeUnit.MILLISECONDS).doOnEach(log())
.map(constant("FRED")).doOnUnsubscribe(countDown(latch));
db().select("select score from person where name=?").parameters(params).getAs(Integer.class)
.take(1).subscribe(log());
assertTrue(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));
}
@Test
public void testTakeShouldNotHang() {
assertEquals(1, (int) Observable.<Integer> empty().concatWith(Observable.just(1)).take(1)
.toBlocking().single());
}
@Test
public void testcomposeSelectWithDependencies() {
Database db = db();
Observable<Integer> count = db.update("update person set score=? where name=?")
.parameters(4, "FRED").count()
.compose(db.select("select score from person where name=?").parameters("FRED")
.dependsOnTransformer().getAs(Integer.class));
assertIs(4, count);
}
@Test
public void testcomposeUpdateWithParameters() {
Database db = db();
Observable<Integer> count = just(4, "FRED").compose(
db.update("update person set score=? where name=?").parameterTransformer());
assertIs(1, count);
}
@Test
public void testcomposeUpdateWithDependencies() {
Database db = db();
Observable<Integer> score = Observable
// parameters for coming update
.just(4, "FRED")
// update Fred's score to 4
.compose(db.update("update person set score=? where name=?").parameterTransformer())
// update everyone with score of 4 to 14
.compose(db.update("update person set score=? where score=?").parameters(14, 4)
.dependsOnTransformer())
// get Fred's score
.compose(db.select("select score from person where name=?").parameters("FRED")
.dependsOnTransformer().getAs(Integer.class));
assertIs(14, score);
}
static <T> void assertIs(T t, Observable<T> observable) {
assertEquals(t, observable.toBlocking().single());
}
@Test
public void testTwoConnectionsOpenedAndClosedAfterTwoSelects() throws InterruptedException {
CountDownConnectionProvider cp = new CountDownConnectionProvider(2, 2);
Database db = new Database(cp);
db.select("select name from person").count().toBlocking().single();
db.select("select name from person").count().toBlocking().single();
assertTrue(cp.getsLatch().await(60, TimeUnit.SECONDS));
assertTrue(cp.closesLatch().await(60, TimeUnit.SECONDS));
}
@Test
public void testTwoConnectionsOpenedAndClosedAfterTwoUpdates() throws InterruptedException {
CountDownConnectionProvider cp = new CountDownConnectionProvider(2, 2);
Database db = new Database(cp);
db.update("update person set score=? where name=?").parameters(23, "FRED").count()
.toBlocking().single();
db.update("update person set score=? where name=?").parameters(25, "JOHN").count()
.toBlocking().single();
assertTrue(cp.getsLatch().await(60, TimeUnit.SECONDS));
assertTrue(cp.closesLatch().await(60, TimeUnit.SECONDS));
}
@Test
public void testOneConnectionOpenedAndClosedAfterTwoSelectsWithinTransaction()
throws InterruptedException {
CountDownConnectionProvider cp = new CountDownConnectionProvider(1, 1);
Database db = new Database(cp);
Observable<Boolean> begin = db.beginTransaction();
Observable<Integer> count = db.select("select name from person").dependsOn(begin).count();
Observable<Integer> count2 = db.select("select name from person").dependsOn(count).count();
int result = db.commit(count2).count().toBlocking().single();
log.info("committed " + result);
cp.getsLatch().await();
log.info("gets ok");
cp.closesLatch().await();
log.info("closes ok");
}
@Test
public void testOneConnectionOpenedAndClosedAfterTwoUpdatesWithinTransaction()
throws InterruptedException {
CountDownConnectionProvider cp = new CountDownConnectionProvider(1, 1);
Database db = new Database(cp);
Observable<Boolean> begin = db.beginTransaction();
Observable<Integer> count = db.update("update person set score=? where name=?")
.dependsOn(begin).parameters(23, "FRED").count();
Observable<Integer> count2 = db.update("update person set score=? where name=?")
.dependsOn(count).parameters(25, "JOHN").count();
int result = db.commit(count2).count().toBlocking().single();
log.info("committed " + result);
cp.getsLatch().await();
log.info("gets ok");
cp.closesLatch().await();
log.info("closes ok");
}
@Test
public void testCloseDatabaseClosesConnectionProvider() {
ConnectionProvider cp = createMock(ConnectionProvider.class);
cp.close();
EasyMock.expectLastCall().once();
EasyMock.replay(cp);
new Database(cp).close();
EasyMock.verify(cp);
}
@Test
public void testCloseAutoCommittingConnectionProviderClosesInternalConnectionProvider() {
ConnectionProvider cp = createMock(ConnectionProvider.class);
cp.close();
EasyMock.expectLastCall().once();
EasyMock.replay(cp);
new ConnectionProviderAutoCommitting(cp).close();
EasyMock.verify(cp);
}
@Test
public void testCloseSingletonManualCommitConnectionProviderClosesInternalConnectionProvider() {
ConnectionProvider cp = createMock(ConnectionProvider.class);
cp.close();
EasyMock.expectLastCall().once();
EasyMock.replay(cp);
new ConnectionProviderSingletonManualCommit(cp).close();
EasyMock.verify(cp);
}
@Test
public void testCloseConnectionProviderFromUrlClosesInternalConnectionProvider() {
db().close();
}
@Test(expected = RuntimeException.class)
public void testCannotPassObservableAsSingleParameter() {
db().select("anything").parameter(Observable.just(123));
}
@Test
public void testConnectionsReleasedByUpdateStatementBeforeOnNext() throws InterruptedException {
final CountDownConnectionProvider cp = new CountDownConnectionProvider(1, 1);
Database db = new Database(cp);
Observable<Integer> result = db.update("update person set score = 1 where name=?")
.parameter("FRED").count();
checkConnectionsReleased(cp, result);
}
@Test
public void testConnectionsReleasedByCommitBeforeOnNext() throws InterruptedException {
final CountDownConnectionProvider cp = new CountDownConnectionProvider(1, 1);
Database db = new Database(cp);
Observable<Boolean> begin = db.beginTransaction();
Observable<Integer> result = db.update("update person set score = 1 where name=?")
.dependsOn(begin).parameter("FRED").count();
checkConnectionsReleased(cp, db.commit(result));
}
@Test
public void testConnectionsReleasedByRollbackBeforeOnNext() throws InterruptedException {
final CountDownConnectionProvider cp = new CountDownConnectionProvider(1, 1);
Database db = new Database(cp);
Observable<Boolean> begin = db.beginTransaction();
Observable<Integer> result = db.update("update person set score = 1 where name=?")
.dependsOn(begin).parameter("FRED").count();
checkConnectionsReleased(cp, db.rollback(result));
}
private void checkConnectionsReleased(final CountDownConnectionProvider cp,
Observable<?> result) throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
result.subscribe(new Action1<Object>() {
@Override
public void call(Object obj) {
try {
if (cp.closesLatch().await(TIMEOUT_SECONDS, TimeUnit.SECONDS))
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
assertTrue(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));
}
@Test
public void testCanChainUpdateStatementsWithinTransaction() {
Database db = db();
Observable<Boolean> begin = db.beginTransaction();
Observable<Integer> updates = Observable
// set name parameter
.just("FRED")
// push into update
.compose(db.update("update person set score=1 where name=?").dependsOn(begin)
.parameterTransformer())
// map num rows affected to JOHN
.map(constant("JOHN"))
// push into second update
.compose(
db.update("update person set score=2 where name=?").parameterTransformer());
db.commit(updates).toBlocking().single();
}
@Test
public void testCommitOperator() {
Database db = db();
Observable<Boolean> begin = db.beginTransaction();
String name = Observable
// set name parameter
.just("FRED")
// push into update
.compose(db.update("update person set score=1 where name=?") //
.dependsOn(begin) //
.parameterTransformer())
// map num rows affected to JOHN
.compose(db.commit_())
// select query
.compose(db.select("select name from person where score=1")
// depends on commit
.dependsOnTransformer()
// return names
.getAs(String.class))
// return first name
.first()
// block to get make everything run
.toBlocking().single();
assertEquals("FRED", name);
}
@Test
public void testTryCatch() {
try (Connection con = DatabaseCreator.nextConnection();
PreparedStatement ps = con.prepareStatement(
"select name from person where name > ? order by name");) {
ps.setObject(1, "ALEX");
List<String> list = new ArrayList<String>();
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
list.add(rs.getString(1));
}
}
System.out.println(list);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Test
public void testChainSelectUsingOperators() {
Database db = db();
List<Integer> scores = db.select("select name from person")
// get name
.getAs(String.class)
// push name as parameter to next select
.compose(db
// select scores
.select("select score from person where name=?")
// parameters are pushed
.parameterTransformer()
// get score as integer
.getAs(Integer.class))
// sort scores
.toSortedList()
// block to get result
.toBlocking().single();
assertEquals(asList(21, 25, 34), scores);
}
@Test
public void testBeginTransactionEmitsOneItem() {
Database db = db();
Boolean value = db.beginTransaction().toBlocking().single();
assertTrue(value);
}
@Test
public void testCommitOnLastOperator() {
Database db = db();
long count = db
// start transaction
.beginTransaction()
// push parameters
.concatMap(constant(just(99, 88)))
// log
.doOnEach(log())
// update twice
.compose(db.update("update person set score=?")
// push parameters
.parameterTransformer())
// commit on last
.compose(db.commitOnComplete_())
// get count of 88s
.compose(db.select("select count(*) from person where score=88")
// depends on previous
.dependsOnTransformer()
// count as Long
.getAs(Long.class))
// block and get result
.toBlocking().single();
assertEquals(3, count);
}
@Test
public void testRollbackOnLastOperator() {
Database db = db();
long count = db
// start transaction
.beginTransaction()
// push parameters
.concatMap(constant(just(99, 88)))
// log
.doOnEach(log())
// update twice
.compose(db.update("update person set score=?")
// push parameters
.parameterTransformer())
// commit on last
.compose(db.rollbackOnComplete_())
// get count of 88s
.compose(db.select("select count(*) from person where score=88")
// depends on previous
.dependsOnTransformer()
// count as Long
.getAs(Long.class))
// block and get result
.toBlocking().single();
assertEquals(0, count);
}
@Test
public void testBeginTransactionOnNextForThreePasses() {
Database db = db();
Observable<Integer> min = Observable
// do 3 times
.just(11, 12, 13)
// begin transaction for each item
.compose(db.beginTransactionOnNext_())
// update all scores to the item
.compose(db.update("update person set score=?").parameterTransformer())
// to empty parameter list
.map(toEmpty())
// increase score
.compose(db.update("update person set score=score + 5").parameterListTransformer())
// only expect one result so can flatten
.compose(RxUtil.<Integer> flatten())
// commit transaction
.compose(db.commitOnNext_())
// to empty lists
.map(toEmpty())
// return count
.compose(db.select("select min(score) from person").dependsOnTransformer()
.getAs(Integer.class));
assertIs(18, min);
}
@Test
public void testParameterListOperator() {
Database db = db();
@SuppressWarnings("unchecked")
int count =
// parameters grouped in lists
objects(objects(1), objects(2))
// log
.doOnEach(log())
// begin trans
.compose(db.<Observable<Object>> beginTransactionOnNext_())
// log
.doOnEach(log())
// update
.compose(db.update("update person set score = ?")
// push lists of parameters
.parameterListTransformer())
// log
.doOnEach(log())
// commit
.compose(db.<Integer> commitOnNextList_())
// total rows affected
.count()
// block and get result
.toBlocking().single();
assertEquals(2, count);
}
@Test
public void testParameterListOperatorWhenQueryNeedsTwoParameters() {
Database db = db();
@SuppressWarnings("unchecked")
int count =
// parameters grouped in lists
objects(objects(1, "FRED", 3, "JOHN"), objects(2, "JOSEPH"))
// log
.doOnEach(log())
// begin trans
.compose(db.<Observable<Object>> beginTransactionOnNext_())
// log
.doOnEach(log())
// update
.compose(db.update("update person set score = ? where name=?")
// push lists of parameters
.parameterListTransformer())
// log
.doOnEach(log())
// commit
.compose(db.<Integer> commitOnNextList_())
// total rows affected
.count()
// block and get result
.toBlocking().single();
assertEquals(2, count);
}
@Test
public void testCanExecuteCreateSchema() {
Database db = db();
int count = db.update("create schema if not exists special_user").count().toBlocking()
.single();
assertEquals(0, count);
}
@Test
public void testCanExecuteCreateTable() {
Database db = db();
int count = db.update("create table mytemp(name varchar2(100) primary key)").count()
.toBlocking().single();
assertEquals(0, count);
}
private static Observable<Object> objects(Object... objects) {
return Observable.from(objects);
}
private static Observable<Observable<Object>> objects(
@SuppressWarnings("unchecked") Observable<Object>... objects) {
return Observable.from(objects);
}
@Test
public void testDatabaseFromConnectionCanUseConnectionTwiceWithoutItBeingClosedInReality()
throws SQLException {
ConnectionProvider cp = DatabaseCreator.connectionProvider();
DatabaseCreator.createDatabase(cp);
Connection con = cp.get();
Database db = Database.from(con);
Observable<Integer> count = db
// get names
.select("select name from person")
// as string
.getAs(String.class)
// count names
.count()
// do something else
.compose(db
// get max score
.select("select max(score) from person")
// run the previous statement first
.dependsOnTransformer()
// as integer
.getAs(Integer.class));
assertIs(34, count);
con.close();
}
@Test
public void testNonTransactionalMultipleQueries() {
// get a synchronous database
Database db = DatabaseCreator.db();
final Set<String> set = Collections.newSetFromMap(new HashMap<String, Boolean>());
Observable<Integer> count = Observable.just(1, 2, 3, 4, 5)
// select
.compose(db.select("select name from person where score >?")
// push parameters to this query
.parameterTransformer()
// get name as string
.getAs(String.class))
// record thread name
.doOnNext(new Action1<String>() {
@Override
public void call(String name) {
set.add(Thread.currentThread().getName());
}
})
// count
.count();
assertIs(5 * 3, count);
System.out.println("threads=" + set);
assertEquals(1, set.size());
}
@Test
public void testTransaction() {
Database db = db();
Observable<Boolean> begin = db.beginTransaction();
Observable<Integer> count = Observable
// generate 1,2,3
.just(1, 2, 3)
// update score with that value
.compose(db.update("update person set score = ?")
// participates in a transaction
.dependsOn(begin)
// parameters are pushed to this update statement
.parameterTransformer())
// commit transaction
.compose(db.commitOnComplete_())
// count names with score 3
.compose(db.select("select count(name) from person where score=3")
// must commit first
.dependsOnTransformer().getAs(Integer.class));
assertIs(3, count);
}
@Test
public void testTwoConnectionsOpenedAndClosedWhenTakeOneUsedWithSelectThatReturnsOneRow()
throws InterruptedException {
Action0 completed = new Action0() {
@Override
public void call() {
System.out.println("completed");
}
};
CountDownConnectionProvider cp = new CountDownConnectionProvider(1, 1);
Database db = new Database(cp);
db.select("select count(*) from person").getAs(Long.class).doOnCompleted(completed).take(1)
.toBlocking().single();
assertTrue(cp.getsLatch().await(6, TimeUnit.SECONDS));
assertTrue(cp.closesLatch().await(6, TimeUnit.SECONDS));
}
@Test
public void testAutoMapInterface() {
// test dynamic proxying
List<NameScore> list = db().select("select name, score from person order by name")
.autoMap(NameScore.class).toList().toBlocking().single();
assertEquals(3, list.size());
assertEquals("FRED", list.get(0).name());
assertEquals(21, list.get(0).score());
assertEquals("JOSEPH", list.get(1).name());
assertEquals(34, list.get(1).score());
}
static interface NameScore {
@Index(1)
String name();
@Column("score")
int score();
}
@Test
public void testAutoMapConvertsCamelCaseToUnderscoreColumnNames() {
// test dynamic proxying
List<Address> list = db().select("select address_id, full_address from address")
.autoMap(Address.class).toList().toBlocking().single();
assertEquals(1, list.size());
assertEquals(1, list.get(0).addressId());
assertTrue(list.get(0).fullAddress().contains("Something"));
}
static interface Address {
@Column
int addressId();
@Column
String fullAddress();
}
@Test
public void testAutoMapWithQueryAnnotation() {
List<NameScore2> list = db().select().autoMap(NameScore2.class).toList().toBlocking()
.single();
assertEquals(3, list.size());
assertEquals("FRED", list.get(0).name());
assertEquals(21, list.get(0).score());
assertEquals("JOSEPH", list.get(1).name());
assertEquals(34, list.get(1).score());
}
@Query("select name, score from person order by name")
static interface NameScore2 {
@Index(1)
String name();
@Column("score")
int score();
}
@Test(expected = RuntimeException.class)
public void testAutoMapThrowsExceptionIfMappedInterfaceColumnMethodHasParameters() {
// test dynamic proxying
db().select("select address_id, full_address from address").autoMap(Address2.class).toList()
.toBlocking().single();
}
static interface Address2 {
@Column
int addressId(String suburb);
@Column
String fullAddress();
}
@Test
public void testCustomMapper() {
String name = db().select("select name from person order by name")
.get(new ResultSetMapper<String>() {
@Override
public String call(ResultSet rs) throws SQLException {
return rs.getString(1);
}
}).first().toBlocking().single();
assertEquals("FRED", name);
}
@Test
public void testReturnGeneratedKeysForOneInsertedValue() {
// h2 only returns the last generated key
List<Integer> list = db()
//
.update("insert into note(text) values(?)")
//
.parameters("something")
//
.returnGeneratedKeys()
//
.getAs(Integer.class)
//
.toList().toBlocking().single();
assertEquals(Arrays.asList(1), list);
}
@Test
public void testReturnGeneratedKeysForMultipleInsertedValuesInOneStatement() {
// h2 only returns the last generated key
List<Integer> list = db()
//
.update("insert into note(text) values(?),(?)")
//
.parameters("something", "again")
//
.returnGeneratedKeys()
//
.getAs(Integer.class)
//
.toList().toBlocking().single();
assertEquals(Arrays.asList(2), list);
}
@Test
public void testReturnGeneratedKeysForMultipleCallsOfInsert() {
// h2 only returns the last generated key
List<Integer> list = db()
//
.update("insert into note(text) values(?)")
//
.parameters("something", "again")
//
.returnGeneratedKeys()
//
.getAs(Integer.class)
//
.toList().toBlocking().single();
assertEquals(Arrays.asList(1, 2), list);
}
@Test
public void testNamedParameters() {
String name = db()
//
.select("select name from person where score >= :min and score <=:max")
//
.parameter("min", 24)
//
.parameter("max", 26)
//
.getAs(String.class).toBlocking().single();
assertEquals("MARMADUKE", name);
}
@Test
public void testNamedParametersWithMapParameter() {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("min", 24);
map.put("max", 26);
String name = db()
//
.select("select name from person where score >= :min and score <=:max")
//
.parameters(map)
//
.getAs(String.class).toBlocking().single();
assertEquals("MARMADUKE", name);
}
@Test
public void testNamedParametersWithMapParameterInObservable() {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("min", 24);
map.put("max", 26);
String name = db()
//
.select("select name from person where score >= :min and score <=:max")
//
.parameters(Observable.just(map))
//
.getAs(String.class).toBlocking().single();
assertEquals("MARMADUKE", name);
}
@Test
public void testNamedParametersWithUpdateStatement() {
int count = db()
//
.update("update person set score = :newScore where score >= :min and score <=:max")
//
.parameter("newScore", 25)
//
.parameter("min", 24)
//
.parameter("max", 26)
//
.count().toBlocking().single();
assertEquals(1, count);
}
@Test(expected = RuntimeException.class)
public void testNamedParametersOneMissingParameterShouldThrowException() {
db().select("select name from person where name = :name and score = :score")
.parameter("name", "FRED").count().toBlocking().single();
}
@Test(expected = RuntimeException.class)
public void testNamedParametersWithMapParameterNoNamesInSql() {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("min", 24);
map.put("max", 26);
db()
//
.select("select name from person where score >= ? and score <= ?")
//
.parameters(Observable.just(map))
//
.getAs(String.class).toBlocking().single();
}
@Test
public void testNoParameters() {
int count = db().select("select name from person").count().toBlocking().single();
assertEquals(3, count);
}
@Test
public void testAutoMapInterfaceWithPrimitives() {
List<NameScorePrimitive> list = db()
.select("select name, score from person where name='FRED'")
.autoMap(NameScorePrimitive.class).toList().toBlocking().single();
assertEquals(21, list.get(0).score());
}
static interface NameScorePrimitive {
@Column
String name();
@Column
int score();
}
@Test
public void testResultSetTransformSetOnDatabase() {
final AtomicInteger count = new AtomicInteger();
Func1<ResultSet, ? extends ResultSet> transform = new Func1<ResultSet, ResultSet>() {
@Override
public ResultSet call(ResultSet rs) {
count.incrementAndGet();
return rs;
}
};
TestSubscriber<Integer> ts = TestSubscriber.create();
Database db = Database.builder().connectionProvider(db().connectionProvider())
.resultSetTransform(transform).build();
db.select("select name from person").count().subscribe(ts);
ts.awaitTerminalEvent();
assertEquals(1, count.get());
}
@Test
public void testResultSetTransformSetOnQuery() {
final AtomicInteger count = new AtomicInteger();
Func1<ResultSet, ? extends ResultSet> transform = new Func1<ResultSet, ResultSet>() {
@Override
public ResultSet call(ResultSet rs) {
count.incrementAndGet();
return rs;
}
};
TestSubscriber<Integer> ts = TestSubscriber.create();
db().select("select name from person").resultSetTransform(transform).count().subscribe(ts);
ts.awaitTerminalEvent(10, TimeUnit.SECONDS);
assertEquals(1, count.get());
}
@Test
public void testResultSetTransformSetOnQueryCompoundsWithDatabaseTransform() {
final List<Integer> list = new CopyOnWriteArrayList<>();
Func1<ResultSet, ? extends ResultSet> transform1 = new Func1<ResultSet, ResultSet>() {
@Override
public ResultSet call(ResultSet rs) {
list.add(1);
return rs;
}
};
Func1<ResultSet, ? extends ResultSet> transform2 = new Func1<ResultSet, ResultSet>() {
@Override
public ResultSet call(ResultSet rs) {
list.add(2);
return rs;
}
};
Database db = Database.builder().connectionProvider(db().connectionProvider())
.resultSetTransform(transform1).build();
TestSubscriber<Integer> ts = TestSubscriber.create();
db.select("select name from person").resultSetTransform(transform2).count().subscribe(ts);
ts.awaitTerminalEvent(10, TimeUnit.SECONDS);
assertEquals(Arrays.asList(1, 2), list);
}
/********************************************************
** Utility classes
********************************************************/
private static class CountDownConnectionProvider implements ConnectionProvider {
private final ConnectionProvider cp;
private final CountDownLatch closesLatch;
private final CountDownLatch getsLatch;
CountDownConnectionProvider(int expectedGets, int expectedCloses) {
this.cp = connectionProvider();
DatabaseCreator.createDatabase(cp.get());
this.closesLatch = new CountDownLatch(expectedCloses);
this.getsLatch = new CountDownLatch(expectedGets);
}
CountDownLatch closesLatch() {
return closesLatch;
}
CountDownLatch getsLatch() {
return getsLatch;
}
@Override
public Connection get() {
getsLatch.countDown();
Connection inner = cp.get();
return new CountingConnection(inner, closesLatch);
}
@Override
public void close() {
cp.close();
}
}
static class PersonClob {
private final String name;
private final String document;
public PersonClob(String name, String document) {
this.name = name;
this.document = document;
}
public String getName() {
return name;
}
public String getDocument() {
return document;
}
}
static class PersonBlob {
private final String name;
private final byte[] document;
public PersonBlob(String name, byte[] document) {
this.name = name;
this.document = document;
}
public String getName() {
return name;
}
public byte[] getDocument() {
return document;
}
}
static class Person {
private final String name;
private final double score;
private final Long dateOfBirthEpochMs;
private final Long registered;
Person(String name, double score, Long dateOfBirthEpochMs, Long registered) {
this.name = name;
this.score = score;
this.dateOfBirthEpochMs = dateOfBirthEpochMs;
this.registered = registered;
}
public String getName() {
return name;
}
public double getScore() {
return score;
}
public Long getDateOfBirth() {
return dateOfBirthEpochMs;
}
public Long getRegistered() {
return registered;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Pair [name=");
builder.append(name);
builder.append(", score=");
builder.append(score);
builder.append("]");
return builder.toString();
}
}
}