/*
* (c) Copyright 2010-2011 by Volker Bergmann. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is permitted under the terms of the
* GNU General Public License (GPL).
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
* REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
* HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.databene.platform.db;
import java.io.Closeable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.databene.benerator.GeneratorContext;
import org.databene.benerator.GeneratorState;
import org.databene.benerator.InvalidGeneratorSetupException;
import org.databene.benerator.engine.BeneratorContext;
import org.databene.benerator.util.UnsafeNonNullGenerator;
import org.databene.commons.IOUtil;
import org.databene.jdbacl.DBUtil;
import org.databene.script.ScriptUtil;
import org.databene.webdecs.DataContainer;
import org.databene.webdecs.DataIterator;
import org.databene.webdecs.DataSource;
/**
* Uses a database table to fetch and increment values like a database sequence.<br/><br/>
* Created: 09.08.2010 14:44:06
* @since 0.6.4
* @author Volker Bergmann
*/
public class SequenceTableGenerator<E extends Number> extends UnsafeNonNullGenerator<E> {
private String table;
private String column;
private DBSystem database;
private String selector;
protected Long increment = 1L;
private String query;
private IncrementorStrategy incrementorStrategy;
private PreparedStatement parameterizedAccessorStatement;
public SequenceTableGenerator() {
this(null, null, null);
}
public SequenceTableGenerator(String table, String column, DBSystem db) {
this(table, column, db, null);
}
public SequenceTableGenerator(String table, String column, DBSystem db, String selector) {
this.table = table;
this.column = column;
this.database = db;
this.selector = selector;
}
public void setTable(String table) {
this.table = table;
}
public void setColumn(String column) {
this.column = column;
}
public void setDatabase(DBSystem db) {
this.database = db;
}
public void setSelector(String selector) {
this.selector = selector;
}
// Generator interface implementation ------------------------------------------------------------------------------
@SuppressWarnings("unchecked")
public Class<E> getGeneratedType() {
return (Class<E>) Number.class;
}
@Override
public void init(GeneratorContext context) throws InvalidGeneratorSetupException {
// check preconditions
assertNotInitialized();
if (database == null)
throw new InvalidGeneratorSetupException("db is null");
// initialize
query = "select " + column + " from " + table;
if (selector != null)
query = ScriptUtil.combineScriptableParts(query, " where ", selector);
incrementorStrategy = createIncrementor();
super.init(context);
}
private IncrementorStrategy createIncrementor() {
if (increment == null)
return null;
String incrementorSql = "update " + table + " set " + column + " = ?";
if (selector != null)
incrementorSql = ScriptUtil.combineScriptableParts(incrementorSql, " where ", selector);
if (selector == null || !ScriptUtil.isScript(selector))
return new PreparedStatementStrategy(incrementorSql, database);
else
return new StatementStrategy(incrementorSql, database);
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public E generate() {
if (this.state == GeneratorState.CLOSED)
return null;
assertInitialized();
DataSource<?> iterable = database.query(query, true, context);
DataIterator<?> iterator = null;
E result;
try {
iterator = iterable.iterator();
DataContainer<?> container = iterator.next(new DataContainer());
if (container == null) {
close();
return null;
}
result = (E) container.getData();
incrementorStrategy.run(result.longValue(), (BeneratorContext) context);
} finally {
IOUtil.close(iterator);
}
return result;
}
@SuppressWarnings({ "unchecked", "cast" })
public E generateWithParams(Object... params) {
if (this.state == GeneratorState.CLOSED)
return null;
ResultSet resultSet = null;
E result = null;
try {
if (parameterizedAccessorStatement == null) {
String queryText = String.valueOf(ScriptUtil.parseUnspecificText(query).evaluate(context));
parameterizedAccessorStatement = database.getConnection().prepareStatement(queryText);
}
for (int i = 0; i < params.length; i++)
parameterizedAccessorStatement.setObject(i + 1, params[i]);
resultSet = parameterizedAccessorStatement.executeQuery();
if (!resultSet.next()) {
close();
return null;
}
result = (E) resultSet.getObject(1);
incrementorStrategy.run(result.longValue(), (BeneratorContext) context, params);
} catch (SQLException e) {
throw new RuntimeException("Error fetching value in " + getClass().getSimpleName(), e);
} finally {
DBUtil.close(resultSet);
}
return (E) result;
}
@Override
public void close() {
IOUtil.close(incrementorStrategy);
DBUtil.close(parameterizedAccessorStatement);
super.close();
}
@Override
public String toString() {
return getClass().getSimpleName() + "[" + selector + "]";
}
// IncrementorStrategy ---------------------------------------------------------------------------------------------
interface IncrementorStrategy extends Closeable {
void run(long currentValue, BeneratorContext context, Object... params);
void close();
}
class PreparedStatementStrategy implements IncrementorStrategy {
private PreparedStatement statement;
public PreparedStatementStrategy(String incrementorSql, DBSystem db) {
try {
statement = db.getConnection().prepareStatement(incrementorSql);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void run(long currentValue, BeneratorContext context, Object... params) {
try {
statement.setLong(1, currentValue + increment);
for (int i = 0; i < params.length; i++)
statement.setObject(2 + i, params[i]);
statement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void close() {
DBUtil.close(statement);
}
}
class StatementStrategy implements IncrementorStrategy {
private Statement statement;
private String sql;
public StatementStrategy(String sql, DBSystem db) {
try {
this.statement = db.getConnection().createStatement();
this.sql = sql;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void run(long currentValue, BeneratorContext context, Object... params) {
try {
String cmd = sql.replace("?", String.valueOf(currentValue + increment));
cmd = ScriptUtil.parseUnspecificText(cmd).evaluate(context).toString();
statement.executeUpdate(cmd);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void close() {
DBUtil.close(statement);
}
}
}