package org.simpleflatmapper.jdbc.impl; import org.simpleflatmapper.jdbc.Crud; import org.simpleflatmapper.jdbc.CrudDSL; import org.simpleflatmapper.jdbc.SelectQuery; import org.simpleflatmapper.util.CheckedConsumer; import java.lang.reflect.Type; import java.sql.Connection; import java.sql.SQLException; import java.util.Collection; import java.util.concurrent.atomic.AtomicReference; public class LazyCrud<T, K> implements Crud<T, K> { private final CrudDSL<T, K> crudDSL; private final AtomicReference<Crud<T, K>> delegate = new AtomicReference<Crud<T, K>>(); private final String table; public LazyCrud(CrudDSL<T, K> crudDSL, String table) { this.crudDSL = crudDSL; this.table = table; } private Crud<T, K> getDelegate(Connection connection) throws SQLException { Crud<T, K> crud; do { crud = delegate.get(); if (crud != null) break; Crud<T, K> newCrud = instantiateCrud(connection); if (delegate.compareAndSet(null, newCrud)) { crud = newCrud; break; } } while(true); return crud; } private Crud<T, K> instantiateCrud(Connection connection) throws SQLException { if (table == null) { return crudDSL.to(connection); } else { return crudDSL.table(connection, table); } } @Override public void create(Connection connection, T value) throws SQLException { getDelegate(connection).create(connection, value); } @Override public void create(Connection connection, Collection<T> values) throws SQLException { getDelegate(connection).create(connection, values); } @Override public <RH extends CheckedConsumer<? super K>> RH create(Connection connection, T value, RH keyConsumer) throws SQLException { return getDelegate(connection).create(connection, value, keyConsumer); } @Override public <RH extends CheckedConsumer<? super K>> RH create(Connection connection, Collection<T> values, RH keyConsumer) throws SQLException { return getDelegate(connection).create(connection, values, keyConsumer); } @Override public T read(Connection connection, K key) throws SQLException { return getDelegate(connection).read(connection, key); } @Override public <RH extends CheckedConsumer<? super T>> RH read(Connection connection, Collection<K> keys, RH consumer) throws SQLException { return getDelegate(connection).read(connection, keys, consumer); } @Override public void update(Connection connection, T value) throws SQLException { getDelegate(connection).update(connection, value); } @Override public void update(Connection connection, Collection<T> values) throws SQLException { getDelegate(connection).update(connection, values); } @Override public void delete(Connection connection, K key) throws SQLException { getDelegate(connection).delete(connection, key); } @Override public void delete(Connection connection, Collection<K> keys) throws SQLException { getDelegate(connection).delete(connection, keys); } @Override public void createOrUpdate(Connection connection, T value) throws SQLException { getDelegate(connection).createOrUpdate(connection, value); } @Override public void createOrUpdate(Connection connection, Collection<T> values) throws SQLException { getDelegate(connection).createOrUpdate(connection, values); } @Override public <RH extends CheckedConsumer<? super K>> RH createOrUpdate(Connection connection, T value, RH keyConsumer) throws SQLException { return getDelegate(connection).createOrUpdate(connection, value, keyConsumer); } @Override public <RH extends CheckedConsumer<? super K>> RH createOrUpdate(Connection connection, Collection<T> values, RH keyConsumer) throws SQLException { return getDelegate(connection).createOrUpdate(connection, values, keyConsumer); } @Override public <P> SelectQuery<T, P> where(String whereClause, Type paramClass) { Crud<T, K> crud = delegate.get(); if (crud != null) { return crud.where(whereClause, paramClass); } else { return new LazySelectQuery<P>(whereClause, paramClass); } } private class LazySelectQuery<P> implements SelectQuery<T, P> { private final String whereClause; private final Type paramClass; private final AtomicReference<SelectQuery<T, P>> delegate = new AtomicReference<SelectQuery<T, P>>(); public LazySelectQuery(String whereClause, Type paramClass) { this.whereClause = whereClause; this.paramClass = paramClass; } @Override public T readFirst(Connection connection, P p) throws SQLException { return getDelegateQuery(connection).readFirst(connection, p); } @Override public <C extends CheckedConsumer<? super T>> C read(Connection connection, P p, C consumer) throws SQLException { return getDelegateQuery(connection).read(connection, p, consumer); } private SelectQuery<T, P> getDelegateQuery(Connection connection) throws SQLException { SelectQuery<T, P> query; do { query = delegate.get(); if (query != null) break; SelectQuery<T, P> newQuery = getDelegate(connection).where(whereClause, paramClass); if (this.delegate.compareAndSet(null, newQuery)) { query = newQuery; break; } } while (true); return query; } } }