com.gdo.sql.slot.SQLSlot.java Source code

Java tutorial

Introduction

Here is the source code for com.gdo.sql.slot.SQLSlot.java

Source

/**
 * Copyright GDO - 2005
 */
package com.gdo.sql.slot;

import java.lang.ref.SoftReference;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.apache.commons.lang3.StringUtils;

import com.gdo.helper.StringHelper;
import com.gdo.project.util.SqlUtils;
import com.gdo.project.util.SqlUtils.SqlAssoc;
import com.gdo.sql.model.SQLContextStcl;
import com.gdo.sql.model.SQLStcl;
import com.gdo.sql.model.SQLStcl.Slot;
import com.gdo.stencils.Result;
import com.gdo.stencils.Stcl;
import com.gdo.stencils.Stcl.Command;
import com.gdo.stencils.StclContext;
import com.gdo.stencils.cond.PathCondition;
import com.gdo.stencils.cond.StencilCondition;
import com.gdo.stencils.iterator.ListIterator;
import com.gdo.stencils.iterator.StencilIterator;
import com.gdo.stencils.key.IKey;
import com.gdo.stencils.key.Key;
import com.gdo.stencils.plug.PSlot;
import com.gdo.stencils.plug.PStcl;
import com.gdo.stencils.slot.MultiSlot;
import com.gdo.stencils.util.PathUtils;
import com.gdo.stencils.util.StencilUtils;

/**
 * <p>
 * A SQL slot contains stencil constructed from database.
 * </p>
 * <p>
 * Temporary stencils may be stored in database using negative Id.
 * </p>
 * 
 * <p>
 * &copy; 2004, 2005 StudioGdo/Guillaume Doumenc. All Rights Reserved. This
 * software is the proprietary information of StudioGdo & Guillaume Doumenc. Use
 * is subject to license terms.
 * </p>
 * 
 * 
 * @author Guillaume Doumenc (<a>
 *         href="mailto:gdoumenc@studiogdo.com">gdoumenc@studiogdo.com</a>)
 */
public abstract class SQLSlot extends MultiSlot<StclContext, PStcl> implements SQLSlotMixin {

    // prefix to retrieve the plugged stencil after insertion
    public static final String PLUGGED_PREFIX = "plugged";

    // initialized flags
    private boolean _initialized = false;

    // SQL context associated to this slot
    protected PStcl _sql_context;

    // cursor associated to this slot
    protected SQLCursor _cursor;

    // optimization
    boolean _load_all;
    int _stencil_context_uid; // context id

    // calculated map stored in each context id
    SoftReference<StencilIterator<StclContext, PStcl>> _stencil_context_map;

    /**
     * Simple constructor.
     * 
     * @param stclContext
     *            the stencil context.
     * @param in
     *            the stencil container.
     * @param name
     *            the slot name.
     * @param size
     *            the number of stencils stored in memory (when more place is
     *            needed then remove previous ones)
     */
    public SQLSlot(StclContext stclContext, Stcl in, String name, int size) {
        super(stclContext, in, name, PSlot.ANY, true);
        _cursor = new SQLCursor(name, size);
    }

    // creator for sub slot
    // the cursor should be defined later
    protected SQLSlot(StclContext stclContext, Stcl in, String name) {
        super(stclContext, in, name, PSlot.ANY, true);
    }

    public void loadAllAtStart() {
        _load_all = true;
    }

    /* (non-Javadoc)
     * @see com.gdo.stencils.slot._Slot#isCursorBased(com.gdo.stencils._StencilContext)
     */
    @Override
    public boolean isCursorBased(StclContext stclContext) {
        return true;
    }

    /**
     * Retrieves the slot cursor. This method should be redefined in case of
     * subslot.
     * 
     * @param stclContext
     *            the stencil context.
     * @param self
     *            this stencil as a plugged stencil.
     * @return the slot cursor.
     */
    public SQLCursor getCursor(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        return _cursor;
    }

    /**
     * Utility function to retrieve cursor slot from resource slot.
     * 
     * @param stclContext
     *            the stencil context.
     * @param resource
     *            the resource slot name.
     * @param self
     *            this slot as a plugged slot.
     * @return the SQL cursor.
     */
    public SQLCursor getResourceSlotCursor(StclContext stclContext, String resource,
            PSlot<StclContext, PStcl> self) {
        PSlot<StclContext, PStcl> slot = self.getContainer().getResourceSlot(stclContext, resource);
        return ((SQLSlot) slot.getSlot()).getCursor(stclContext, slot);
    }

    @Override
    public String getProperty(StclContext stclContext, IKey key, String prop, PSlot<StclContext, PStcl> self) {
        SQLCursor cursor = getCursor(stclContext, self);
        String value = cursor.getPropertyValue(stclContext, self, key, prop);
        if (value != null) {
            return value;
        }
        return super.getProperty(stclContext, key, prop, self);
    }

    @Override
    public void setProperty(StclContext stclContext, String value, IKey key, String prop,
            PSlot<StclContext, PStcl> self) {
        SQLCursor cursor = getCursor(stclContext, self);
        cursor.addPropertyValue(stclContext, self, self, key, prop, value);
        super.setProperty(stclContext, value, key, prop, self); // only in
                                                                // cursor should
                                                                // be sufficient
    }

    /**
     * Initializes the slot (deleting all negative records).
     * 
     * @param stclContext
     *            the stencil context.
     * @param self
     *            this slot as a plugged slot.
     * @return <tt>true</tt> if the initialization has been done properly.
     * @throws Exception
     */
    protected boolean initialize(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        if (!_initialized) {
            _initialized = true;

            // get SQL context
            PStcl sqlContext = getSQLContext(stclContext, self);
            if (StencilUtils.isNull(sqlContext)) {
                logWarn(stclContext, "No SQL context defined for slot %s for create stencil", self);
                return false;
            }

            // initializes SQL table
            if (!isReadOnly()) {
                SQLContextStcl context = (SQLContextStcl) sqlContext.getReleasedStencil(stclContext);
                String from = getKeysFromWithoutAlias(stclContext, self).toString();
                context.initializeTable(stclContext, from, sqlContext);
            }

            // reads all values at initialization
            if (_load_all) {
                int size = getStencilsList(stclContext, null, self).size();
                _cursor.size(size);
            }
        }
        return true;
    }

    @Override
    public int size(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            PSlot<StclContext, PStcl> self) {

        // should be initialized before used
        if (!initialize(stclContext, self)) {
            return 0;
        }

        // get keys query
        String query = getSizeQuery(stclContext, cond, self);
        if (StringUtils.isBlank(query)) {
            logWarn(stclContext, "Count query not defined for slot %s for size", self);
            return 0;
        }

        // get SQL context
        PStcl sqlContext = getSQLContext(stclContext, self);
        if (StencilUtils.isNull(sqlContext)) {
            logWarn(stclContext, "No SQL context defined for slot %s for size", self);
            return 0;
        }

        // creates list
        SQLContextStcl sql = (SQLContextStcl) sqlContext.getReleasedStencil(stclContext);
        ResultSet rs = sql.selectQuery(stclContext, query, sqlContext);
        if (rs != null) {
            try {
                rs.next();
                return rs.getInt(1);
            } catch (Exception e) {
                logError(stclContext, e.toString());
            } finally {
                SQLContextStcl.closeResultSet(rs);
            }
        }
        return 0;
    }

    @Override
    public void clear() {
        _cursor.clear();
        super.clear();
    }

    /**
     * Returns the stencil context for query. The stencil context is get from
     * stencil container.
     * 
     * @param stclContext
     *            the stencil context.
     * @param self
     *            the plugged slot.
     * @return the stencil context.
     */
    public PStcl getSQLContext(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        if (_sql_context == null) {
            PStcl container = self.getContainer();
            _sql_context = container.getStencil(stclContext, SQLStcl.Slot.SQL_CONTEXT);
        }
        return _sql_context;
    }

    /**
     * Abstract method to retrieve the template class of stencils defined in the
     * slot.
     * 
     * @param stclContext
     *            the stencil context.
     * @param rs
     *            result set containing all data on stencil.
     * @param self
     *            this slot as a plugged slot.
     * @return template used to construct the stencil with value as constructor
     *         parameter.
     */
    public abstract String getStencilTemplate(StclContext stclContext, ResultSet rs,
            PSlot<StclContext, PStcl> self);

    /**
     * Returns the list of parameters needed to create the stencil.
     * 
     * @param stclContext
     *            the stencil context.
     * @param rs
     *            result set containing all data on stencil.
     * @param self
     *            this slot as a plugged slot.
     * @return the parameters array.
     */
    public Object[] getStencilParameters(StclContext stclContext, ResultSet rs, PSlot<StclContext, PStcl> self) {
        return new Object[0];
    }

    //
    // SQL BEHAVIOR DEFINED THROUGH MIXIN
    //

    public String getDatabaseName(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        return databaseName(stclContext, self);
    }

    public String getTableName(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        return tableName(stclContext, self);
    }

    /**
     * Returns the database table alias name for getting stencil property. For
     * example the id is retrieved from
     * <tt>String.format("%s.Id", getTableAliasForProperty)</tt>
     * 
     * @param stclContext
     *            the stencil context.
     * @param self
     *            the plugged slot.
     * @return the database table alias.
     */
    public String getTableAliasForProperty(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        return getTableName(stclContext, self);
    }

    /**
     * Returns a Id field which must be used to the query to retrieve the
     * stencil key. This specific clause is used only if no specific keys query
     * is defined.
     * 
     * @param stclContext
     *            the stencil context.
     * @param self
     *            the plugged slot.
     * @return the specific SQL id field.
     */
    public String getKeysIdField(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        String alias = getTableAliasForProperty(stclContext, self);
        if (StringUtils.isNotBlank(alias)) {
            if (alias.indexOf("`") < 0)
                alias = "`" + alias + "`";
            return String.format("%s.Id", alias);
        }
        return "Id";
    }

    private String getKeysFromWithoutAlias(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        StringBuffer from = new StringBuffer();
        String database = getDatabaseName(stclContext, self);
        if (StringUtils.isNotBlank(database)) {
            if (database.indexOf("`") < 0)
                from.append("`").append(database).append("`");
            else
                from.append(database);
            from.append(".");
        }
        String table = getTableName(stclContext, self);
        if (table.indexOf("`") < 0)
            from.append("`").append(table).append("`");
        else
            from.append(table);
        return from.toString();
    }

    public String getKeysSelect(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        return keysSelect(stclContext, self);
    }

    public String getKeysFrom(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        String from = getKeysFromWithoutAlias(stclContext, self);
        String alias = getTableAliasForProperty(stclContext, self);
        return keysFrom(stclContext, from, alias, self);
    }

    public String getKeysCondition(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            PSlot<StclContext, PStcl> self) {
        String id_field = getKeysIdField(stclContext, self);
        return keysCondition(stclContext, cond, id_field, self);
    }

    public String getKeysOrder(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        String id_field = getKeysIdField(stclContext, self);
        return order(stclContext, id_field, self);
    }

    public String getKeysGroup(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        return group(stclContext, self);
    }

    public String getKeysLimit(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        return limit(stclContext, self);
    }

    public String getKeysHaving(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            PSlot<StclContext, PStcl> self) {
        return (cond == null) ? null
                : cond.toSQL(stclContext, getTableAliasForProperty(stclContext, self), self.getContainer());
    }

    //
    // INTERNAL CODE
    //

    private String getInternalKeysOrder(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            PSlot<StclContext, PStcl> self) {

        // check if key is contains "1 order"
        String key = PathCondition.getKeyCondition(cond);
        if (StringUtils.isNotBlank(key)) {
            String lower = key.toLowerCase();
            int start = lower.indexOf(" order ");
            if (start != -1) {
                int stop = lower.indexOf(" limit ");
                if (stop > 0) {
                    return key.substring(start + 1, stop);
                }
                return key.substring(start + 1);
            }
        }

        // no limit
        return getKeysOrder(stclContext, self);
    }

    private String getInternalKeysLimit(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            PSlot<StclContext, PStcl> self) {

        // check if key is contains " limit"
        String key = PathCondition.getKeyCondition(cond);
        if (StringUtils.isNotBlank(key)) {
            String lower = key.toLowerCase();
            int index = lower.indexOf(" limit ");
            if (index != -1) {
                return key.substring(index + 1);
            }
        }

        // otherwise returns defined limit
        return getKeysLimit(stclContext, self);
    }

    protected String getSizeCounter(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            PSlot<StclContext, PStcl> self) {
        return String.format("Count(*)");
    }

    /**
     * Returns a specific SQL query which must be used to retrieve the slot
     * size.
     * 
     * @param stclContext
     *            the stencil context.
     * @param self
     *            this slot as a plugged slot.
     * @return the query to fetch all keys.
     */
    public String getSizeQuery(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            PSlot<StclContext, PStcl> self) {
        String counter = getSizeCounter(stclContext, cond, self);
        return getInternalKeysQuery(stclContext, cond, counter, null, null, null, self);
        // String alias = getTableAliasForProperty(stclContext, self);
        // return getInternalKeysQuery(stclContext, cond, String.format("%s.*",
        // alias), null, null, self);
        // return String.format("SELECT Count(*) FROM (%s) _internal_counter",
        // keys);
    }

    /**
     * Returns a specific SQL query which must be used to retrieve the stencil
     * keys.
     * 
     * @param stclContext
     *            the stencil context.
     * @param self
     *            this slot as a plugged slot.
     * @return the query to fetch all keys.
     */
    public String getKeysQuery(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            PSlot<StclContext, PStcl> self) {

        // if only searching a stencil from its id
        if (cond != null) {
            try {
                int k = Integer.parseInt(PathCondition.getKeyCondition(cond));
                return getStencilQuery(stclContext, new Key(k), k > 0, self);
            } catch (NumberFormatException e) {
            }
        }

        return getInternalKeysQuery(stclContext, cond, true, true, self);
    }

    public String getKeysQueryWithoutCondition(StclContext stclContext, String key,
            PSlot<StclContext, PStcl> self) {
        return getStencilQuery(stclContext, new Key(key), false, self);
    }

    /**
     * Returns the SQL query constructed for this slot.
     * 
     * @param stclContext
     *            the stencil context.
     * @param cond
     *            the stencil condition.
     * @param withOrder
     *            <tt>true</tt> if order should be used
     * @param withLimit
     *            <tt>true</tt> if limit should be used
     * @param self
     *            this slot as a plugged slot.
     * @return the SQL query
     */
    public String getInternalKeysQuery(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            boolean withOrder, boolean withLimit, PSlot<StclContext, PStcl> self) {
        String select = getKeysSelect(stclContext, self);
        String order = null;
        String limit = null;

        // set order if needed
        if (withOrder) {
            String o = getInternalKeysOrder(stclContext, cond, self);
            if (StringUtils.isNotBlank(o)) {
                order = o;
            }
        }

        // sets limit if needed
        if (withLimit) {
            String l = getInternalKeysLimit(stclContext, cond, self);
            if (StringUtils.isNotBlank(l)) {
                limit = l;
            }
        }

        // add group
        String group = getKeysGroup(stclContext, self);

        return getInternalKeysQuery(stclContext, cond, select, order, limit, group, self);
    }

    /**
     * Returns the SQL query constructed for this slot.
     * 
     * @param stclContext
     *            the stencil context.
     * @param cond
     *            the stencil condition.
     * @param select
     *            the select part used.
     * @param order
     *            the order part used.
     * @param limit
     *            the limit part used.
     * @param self
     *            this slot as a plugged slot.
     * @return the SQL query
     */
    public String getInternalKeysQuery(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            String select, String order, String limit, String group, PSlot<StclContext, PStcl> self) {

        // gets query components
        String from = getKeysFrom(stclContext, self);
        String where = getKeysCondition(stclContext, cond, self);

        // creates query
        String query = String.format("SELECT %s FROM %s ", select, from);

        // adds having (expression condition)
        String having = getKeysHaving(stclContext, cond, self);

        // adds where, group, having, order and limit
        if (StringUtils.isNotBlank(where)) {
            query = String.format("%s WHERE %s", query, where);
        }
        if (StringUtils.isNotBlank(group)) {
            query = String.format("%s %s", query, group);
        }
        if (StringUtils.isNotBlank(having)) {
            query = String.format("%s HAVING %s", query, having);
        }
        if (StringUtils.isNotBlank(order)) {
            query = String.format("%s %s", query, order);
        }
        if (StringUtils.isNotBlank(limit)) {
            query = String.format("%s %s", query, limit);
        }

        // returns created query
        return query;
    }

    /**
     * Return the properties values stored for optimization (this map is updated
     * each time the keys are calculated). <b>Beware</b>Think to update the map
     * when you save values.
     * 
     * @param stclContext
     *            the stencil context.
     * @param rs
     *            the result set of values fetch from keys query.
     * @param self
     *            this slot as a plugged slot.
     * @return a map with property path as key and value associated
     *         <tt>null if not optimization/tt>.
     */
    public Map<String, String> getPropertiesValuesFromKeyResults(StclContext stclContext, ResultSet rs,
            PSlot<StclContext, PStcl> self) throws SQLException {
        Map<String, String> map = new HashMap<String, String>();
        map.put(SQLStcl.Slot.ID, rs.getString(SQLStcl.Slot.ID));
        return map;
        /*
         * Map<String, String> map =
         * super.getPropertiesValuesFromKeyResults(stclContext, rs, self);
         * map.put(Stcl.Slot.ID, rs.getString(Stcl.Slot.ID)); return map;
         */
    }

    /**
     * Returns a specific SQL selection clause which must be used to the query
     * to retrieve the stencil. This specific clause is used only if no specific
     * keys query is defined.
     * 
     * @param stclContext
     *            the stencil context.
     * @param self
     *            this slot as a plugged slot.
     * @return the specific SQL selection.
     */
    public String getStencilSelect(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        return getKeysSelect(stclContext, self);
    }

    /**
     * Returns a Id field which must be used to the query to retrieve the
     * stencil. This specific clause is used only if no specific keys query is
     * defined.
     * 
     * @param stclContext
     *            the stencil context.
     * @param self
     *            this slot as a plugged slot.
     * @return the specific SQL id field.
     */
    public String getStencilIdField(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        return getKeysIdField(stclContext, self);
    }

    /**
     * Returns a specific SQL from clause which must be used to the query to
     * retrieve the stencil.
     * 
     * @param stclContext
     *            the stencil context.
     * @param self
     *            this slot as a plugged slot.
     * @return the specific SQL selection.
     */
    public String getStencilFrom(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        return getKeysFrom(stclContext, self);
    }

    /**
     * Returns a specific SQL group clause which must be used to the query to
     * retrieve the stencil.
     * 
     * @param stclContext
     *            the stencil context.
     * @param self
     *            this slot as a plugged slot.
     * @return the specific SQL selection.
     */
    public String getStencilGroup(StclContext stclContext, PSlot<StclContext, PStcl> self) {
        return getKeysGroup(stclContext, self);
    }

    /**
     * Abstract method to retrieve the query string which will return the result
     * set used to complete the stencil.
     * 
     * @param stclContext
     *            the stencil context.
     * @param key
     *            the plugged key.
     * @param self
     *            this slot as a plugged slot.
     * @return the query to fetch the stencil values.
     */
    public String getStencilQuery(StclContext stclContext, IKey key, boolean withWhere,
            PSlot<StclContext, PStcl> self) {
        String select = getStencilSelect(stclContext, self);
        String from = getStencilFrom(stclContext, self);
        String group = getStencilGroup(stclContext, self);
        String id = getStencilIdField(stclContext, self);

        // creates query
        String query = null;
        if (withWhere) {
            String where = getKeysCondition(stclContext, null, self);
            query = String.format("SELECT %s FROM %s WHERE %s='%s' AND %s", select, from, id, key, where);
        } else
            query = String.format("SELECT %s FROM %s WHERE %s='%s'", select, from, id, key);

        // adds group, having, order and limit
        if (StringUtils.isNotBlank(group)) {
            query = String.format("%s %s", query, group);
        }

        return query;
    }

    /**
     * Method to complete the stencil from SQL values (by default all stencils
     * must have a sql context, so it will be plugged).
     * 
     * @param stclContext
     *            the stencil context.
     * @param stencil
     *            the created stencil.
     * @param rs
     *            the result set of values.
     * @param self
     *            this slot as a plugged slot.
     * @return the completion result.
     */
    public Result completeStencil(StclContext stclContext, PStcl stencil, ResultSet rs,
            PSlot<StclContext, PStcl> self) {

        // completes at least id
        try {
            stencil.setString(stclContext, Slot.ID, rs.getString(Slot.ID));
        } catch (SQLException e) {
            logWarn(stclContext, e.toString());
        }

        // plugs SQL context
        PStcl sqlContext = self.getContainer().getStencil(stclContext, SQLStcl.Slot.SQL_CONTEXT);
        stencil.plug(stclContext, sqlContext, SQLStcl.Slot.SQL_CONTEXT);

        return Result.success();
    }

    /**
     * Calls the insertion query to the database (this method is called by plug
     * order. By default all stencils must have a sql context, so it will be
     * plugged in its SQL slot and the last inserted index is returned. If you
     * override it don't forget to call afterInsertStencilQuery (or doing
     * content in your code). <b>BEWARE</b>: do not do another update query (or
     * last inserted index will be wrong)
     * 
     * @param stclContext
     *            the stencil context.
     * @param stencil
     *            the stencil to be inserted.
     * @param sqlContext
     *            the sql context stencil.
     * @param self
     *            this slot as a plugged slot.
     * @return the key of the plugged stencil.
     */
    public synchronized Result insertStencilQuery(StclContext stclContext, PStcl stencil, PStcl sqlContext,
            PSlot<StclContext, PStcl> self) {
        try {

            // verifies if insert can be done
            Result result = beforeInsertStencilQuery(stclContext, stencil, sqlContext, self);
            if (result.isNotSuccess())
                return result;

            // creates the query
            SqlUtils.SqlAssoc assoc = getSqlAssoc(stclContext, stencil, self);
            String query = assoc.getInsertQuery();

            // does the query
            SQLContextStcl stcl = (SQLContextStcl) sqlContext.getReleasedStencil(stclContext);
            result = stcl.updateQuery(stclContext, query, sqlContext);
            if (result.isNotSuccess())
                return result;

            // does after insertion
            return afterInsertStencilQuery(stclContext, stencil, sqlContext, self);
        } catch (Exception e) {
            logError(stclContext, e.toString());
            return Result.error(e);
        }
    }

    protected Result beforeInsertStencilQuery(StclContext stclContext, PStcl stencil, PStcl sqlContext,
            PSlot<StclContext, PStcl> self) {
        return Result.success();
    }

    protected Result afterInsertStencilQuery(StclContext stclContext, PStcl stencil, PStcl sqlContext,
            PSlot<StclContext, PStcl> self) {
        PStcl plugged = stencil;

        // returns key created if was not defined
        String id = stencil.getString(stclContext, SQLStcl.Slot.ID, "");
        if (StringUtils.isBlank(id)) {
            SQLContextStcl stcl = (SQLContextStcl) sqlContext.getReleasedStencil(stclContext);
            int last_inserted_id = stcl.queryLastInsertID(stclContext, sqlContext);
            plugged.setInt(stclContext, SQLStcl.Slot.ID, last_inserted_id);
            plugged.call(stclContext, Command.UPDATE);
            plugged.plug(stclContext, sqlContext, SQLStcl.Slot.SQL_CONTEXT);
            plugged.setCursorBased(stclContext, self, _cursor, new Key(last_inserted_id));
        }

        return Result.success(PLUGGED_PREFIX, plugged);
    }

    /**
     * Calls the update query to the database.
     * 
     * @param stclContext
     *            the stencil context.
     * @param stencil
     *            the stencil to be updated.
     * @param sqlContext
     *            the sql context stencil.
     * @param self
     *            this slot as a plugged slot.
     * @return the key of the plugged stencil.
     */
    public Result updateStencilQuery(StclContext stclContext, PStcl stencil, PStcl sqlContext,
            PSlot<StclContext, PStcl> self) {
        try {

            // verifies if update can be done
            Result result = beforeUpdateStencilQuery(stclContext, stencil, sqlContext, self);
            if (result.isNotSuccess())
                return result;

            // creates the query
            SqlUtils.SqlAssoc assoc = getSqlAssoc(stclContext, stencil, self);
            SqlUtils.SqlCondition sql_where = newSqlCondition(stclContext, stencil, self);
            sql_where.pushString(stclContext, SQLStcl.Slot.ID, SQLStcl.Slot.ID);
            String query = assoc.getUpdateQuery(sql_where);

            // does the query
            SQLContextStcl stcl = (SQLContextStcl) sqlContext.getReleasedStencil(stclContext);
            result = stcl.updateQuery(stclContext, query, sqlContext);
            if (result.isNotSuccess()) {
                getCursor(stclContext, self).clear();
                return result;
            }

            // does after updatation
            return afterUpdateStencilQuery(stclContext, stencil, sqlContext, self);
        } catch (Exception e) {
            logError(stclContext, e.toString());
            return Result.error(e);
        }
    }

    public Result beforeUpdateStencilQuery(StclContext stclContext, PStcl stencil, PStcl sqlContext,
            PSlot<StclContext, PStcl> self) {
        return Result.success();
    }

    public Result afterUpdateStencilQuery(StclContext stclContext, PStcl stencil, PStcl sqlContext,
            PSlot<StclContext, PStcl> self) {
        return Result.success();
    }

    /**
     * Calls the deletion query to the database (this method is called by unplug
     * order).
     * 
     * @param stclContext
     *            the stencil context.
     * @param key
     *            the plugged key (may not be <tt>null</tt>).
     * @param stencil
     *            the stencil to be deleted.
     * @param sqlContext
     *            the sql context stencil.
     * @param container
     *            the container stencil.
     */
    protected Result deleteStencilQuery(StclContext stclContext, IKey key, PStcl stencil, PStcl sqlContext,
            PSlot<StclContext, PStcl> self) {
        try {

            // verifies if delete can be done
            Result result = beforeDeleteStencilQuery(stclContext, key, stencil, sqlContext, self);
            if (result.isNotSuccess())
                return result;

            String from = getKeysFrom(stclContext, self);
            String table = getTableName(stclContext, self);
            String alias = getTableAliasForProperty(stclContext, self);
            SqlUtils.SqlCondition sql_where = SqlUtils.newSqlCondition(from, table, alias, stencil);

            // creates the query
            sql_where.put(SQLStcl.Slot.ID, "'" + key.toString() + "'");
            String query = sql_where.getDeleteQuery();

            // does the query
            SQLContextStcl stcl = (SQLContextStcl) sqlContext.getReleasedStencil(stclContext);
            result = stcl.updateQuery(stclContext, query, sqlContext);
            if (result.isNotSuccess())
                return result;

            // does after updatation
            return afterDeleteStencilQuery(stclContext, key, stencil, sqlContext, self);
        } catch (Exception e) {
            logError(stclContext, e.toString());
            return Result.error(e);
        }
    }

    protected Result beforeDeleteStencilQuery(StclContext stclContext, IKey key, PStcl stencil, PStcl sqlContext,
            PSlot<StclContext, PStcl> self) {
        return Result.success();
    }

    protected Result afterDeleteStencilQuery(StclContext stclContext, IKey key, PStcl stencil, PStcl sqlContext,
            PSlot<StclContext, PStcl> self) {
        return Result.success();
    }

    /**
     * Retrieves the key used in slot for the stencil. When the stencil is
     * unplugged without a key, this key must be retrieved from the stencil
     * content.
     * 
     * @param stclContext
     *            the stencil context.
     * @param stencil
     *            the stencil to be deleted.
     * @param sqlContext
     *            the sql context stencil.
     * @param container
     *            the container stencil.
     * @return the key from database information.
     */
    public IKey retrieveKeyFromStencil(StclContext stclContext, PStcl stencil, PStcl sqlContext, PStcl container) {
        String id = SqlUtils.getStringFromStencil(stclContext, stencil, SQLStcl.Slot.ID);
        return new Key(id);
    }

    public SqlAssoc getSqlAssoc(StclContext stclContext, PStcl stencil, PSlot<StclContext, PStcl> self) {
        SqlAssoc assoc = newSqlAssoc(stclContext, stencil, self);

        // add stored fields in sql assoc
        Stcl stcl = stencil.getReleasedStencil(stclContext);
        if (stcl instanceof SQLStcl) {
            ((SQLStcl) stcl).addInSqlAssoc(stclContext, assoc, stencil, self.getContainer());
        } else {
            logWarn(stclContext, "The stencil %s is not a SQL stencil", stencil);
        }

        return assoc;
    }

    public ResultSet getKeysResultSet(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            PSlot<StclContext, PStcl> self) {

        // get keys query
        String query = getKeysQuery(stclContext, cond, self);
        if (StringUtils.isBlank(query)) {
            logWarn(stclContext, "Keys query not defined for slot %s for key result set", self);
            return null;
        }

        // get sql context
        PStcl sqlContext = getSQLContext(stclContext, self);
        if (StencilUtils.isNull(sqlContext)) {
            logWarn(stclContext, "No SQL context defined for slot %s for key result set", self);
            return null;
        }

        // creates list
        SQLContextStcl sql = (SQLContextStcl) sqlContext.getReleasedStencil(stclContext);
        return sql.selectQuery(stclContext, query, sqlContext);
    }

    // TO BE REMOVED (getStencil should be used)
    public ResultSet getKeysResultSet(StclContext stclContext, IKey key, PSlot<StclContext, PStcl> self) {

        // get keys query
        PathCondition<StclContext, PStcl> cond = PathCondition.newKeyCondition(stclContext, key,
                self.getContainer());
        String query = getKeysQuery(stclContext, cond, self);
        if (StringUtils.isBlank(query)) {
            logWarn(stclContext, "Keys query not defined for slot %s for key result set", self);
            return null;
        }

        // get sql context
        PStcl sqlContext = getSQLContext(stclContext, self);
        if (StencilUtils.isNull(sqlContext)) {
            logWarn(stclContext, "No SQL context defined for slot %s for key result set", self);
            return null;
        }

        // creates list
        SQLContextStcl sql = (SQLContextStcl) sqlContext.getReleasedStencil(stclContext);
        return sql.selectQuery(stclContext, query, sqlContext);
    }

    @Override
    protected StencilIterator<StclContext, PStcl> getStencilsList(StclContext stclContext,
            StencilCondition<StclContext, PStcl> cond, PSlot<StclContext, PStcl> self) {

        // should be initialized before used
        if (!initialize(stclContext, self)) {
            String msg = logWarn(stclContext, "Cannot initialize slot %s", self);
            return StencilUtils.<StclContext, PStcl>iterator(Result.error(msg));
        }

        // if the list was already created for the same stencil context and
        // without any condition
        //synchronized (this) {
        //    if ((_stencil_context_uid == stclContext.getId() || _read_only) && _stencil_context_map != null) {
        //        return StencilUtils.<StclContext, PStcl> iterator(stclContext, _stencil_context_map.get().clone(), cond, self);
        //    }
        //}

        // creates the stencil list
        SQLCursor cursor = getCursor(stclContext, self);
        List<IKey> keys = getKeys(stclContext, cond, self);
        List<PStcl> stencils = new Vector<PStcl>(keys.size());
        for (IKey key : keys) {
            PStcl stencil = new PStcl(stclContext, self, key, cursor);
            stencils.add(stencil);
        }

        // returns the iterator (save for optimization on complete list)
        StencilIterator<StclContext, PStcl> map = new ListIterator<StclContext, PStcl>(stencils);
        if (cond == null) {
            _stencil_context_uid = stclContext.getId();
            _stencil_context_map = new SoftReference<>(map);
        }
        return map;
    }

    // not using other condition than ID...
    protected PStcl getStencil(StclContext stclContext, int key, PSlot<StclContext, PStcl> self) {
        SQLCursor cursor = getCursor(stclContext, self);
        return new PStcl(stclContext, self, new Key(key), cursor);
    }

    /**
     * Returns the list of plugged stencils with cursor.
     * 
     * @param stclContext
     *            the stencil context.
     * @param cond
     * @param self
     *            this slot as a plugged slot.
     * @return
     */
    protected List<IKey> getKeys(StclContext stclContext, StencilCondition<StclContext, PStcl> cond,
            PSlot<StclContext, PStcl> self) {

        // get keys query
        ResultSet rs = getKeysResultSet(stclContext, cond, self);
        if (rs != null) {
            try {
                SQLCursor cursor = getCursor(stclContext, self);
                List<IKey> keys = new ArrayList<>();
                while (rs.next()) {

                    // keys are id
                    String id = rs.getString(SQLStcl.Slot.ID);

                    // for some jointure the id may be null (such result should
                    // not be taken in account)
                    if (StringUtils.isBlank(id))
                        continue;

                    // adds slot value attributes to get string optimization
                    // only if not currently already modified
                    // (or properties are more uptodate that from request)
                    IKey key = new Key(id);
                    Boolean modified = cursor._modified.get(key);
                    if (modified == null || !modified) {
                        try {
                            Map<String, String> attributes = getPropertiesValuesFromKeyResults(stclContext, rs,
                                    self);
                            cursor.setPropertiesValues(stclContext, self, key, attributes);
                            keys.add(key);
                        } catch (Exception e) {
                            // don't add key in list if cannot set property
                            // values
                            logError(stclContext, e.toString());
                        }
                    } else {
                        keys.add(key);
                    }
                }
                return keys;
            } catch (Exception e) {
                logError(stclContext, e.toString());
            } finally {
                SQLContextStcl.closeResultSet(rs);
            }
        }
        return new ArrayList<IKey>();
    }

    @Override
    protected PStcl doPlug(StclContext stclContext, PStcl stencil, IKey key, PSlot<StclContext, PStcl> self) {

        // should be initialized before used
        if (!initialize(stclContext, self)) {
            String msg = logWarn(stclContext, "Cannot initialize slot %s", self);
            return Stcl.nullPStencil(stclContext, Result.error(msg));
        }

        // creates new plugged stencil
        // StencilFactory<StclContext, PStcl> factory =
        // (StencilFactory<StclContext,
        // PStcl>) stclContext.getStencilFactory();
        // PStcl toBePlugged = factory.createPStencil(stclContext, self, key,
        // order.getStencil());

        // cheks the stencil plugged is a SQL stencil
        if (!(stencil.getReleasedStencil(stclContext) instanceof SQLStcl)) {
            String msg = String.format("Cannot plug a non SQLStcl %s in a SQLSlot %s", stencil, self);
            return Stcl.nullPStencil(stclContext, Result.error(msg));
        }

        // gets sql context from slot
        PStcl sqlContext = getSQLContext(stclContext, self);
        if (StencilUtils.isNull(sqlContext)) {

            // may be defined on stencil plugged
            sqlContext = stencil.getStencil(stclContext, SQLStcl.Slot.SQL_CONTEXT);
            if (StencilUtils.isNull(sqlContext)) {
                String msg = logWarn(stclContext, "No SQL context defined for doPlug in stencil %s", self);
                return Stcl.nullPStencil(stclContext, Result.error(msg));
            }
        }

        // the stencil context map is then obsolete
        _stencil_context_uid = 0;

        // does insert query
        Result result = insertStencilQuery(stclContext, stencil, sqlContext, self);
        if (result.isNotSuccess()) {
            PStcl container = self.getContainer();
            return container.nullPStencil(stclContext, result);
        }

        // returns the stencil plugged
        PStcl plugged = result.getSuccessValue(PLUGGED_PREFIX);
        if (StencilUtils.isNull(plugged)) {
            PStcl container = self.getContainer();
            String msg = logWarn(stclContext, "was not able to perform the insertion of %s at key %s in database",
                    stencil, key);
            return container.nullPStencil(stclContext, Result.error(msg));
        }

        // PathCondition<StclContext, PStcl> cond =
        // PathCondition.newKeyCondition(plugged.getKey());
        // PStcl p = self.getStencil(stclContext, cond);

        // add containing slot
        SQLStcl sql = (SQLStcl) plugged.getReleasedStencil(stclContext);
        sql.setSQLContext(sqlContext);
        sql.setSQLContainerSlot(self);

        // return plugged;
        return plugged;
    }

    @Override
    protected void doUnplug(StclContext stclContext, PStcl stencil, IKey key, PSlot<StclContext, PStcl> self) {
        SQLCursor cursor = getCursor(stclContext, self);
        PStcl container = self.getContainer();

        // should be initialized before used
        if (!initialize(stclContext, self)) {
            logWarn(stclContext, "Cannot initialize slot %s", self);
            return;
        }

        // gets SQL context
        PStcl sqlContext = getSQLContext(stclContext, self);
        if (StencilUtils.isNull(sqlContext)) {
            logWarn(stclContext, "No SQL context defined for doUnplug in stencil %s", self);
            return;
        }

        // get key
        if (key.isEmpty()) {
            key = retrieveKeyFromStencil(stclContext, stencil, sqlContext, container);
        }
        if (key.isEmpty()) {
            return;
        }

        // removes the stencil from this slot
        stencil.removeThisReferenceFromStencil(stclContext);

        // remove stencil from cursor (if not in cursor (negative id for ex)
        // then get order stencil)
        int id = key.toInt();
        if (id >= 0)
            cursor.remove(stclContext, self, self, key);

        // does deletion query (if key can be found)
        Result result = deleteStencilQuery(stclContext, key, stencil, sqlContext, self);
        if (result.isNotSuccess()) {
            logWarn(stclContext, result.getMessage());
            return;
        }

        // the stencil context map is then obsolete
        _stencil_context_uid = 0;
    }

    @Override
    protected void doUnplugAll(StclContext stclContext, PSlot<StclContext, PStcl> self) {

        // should be initialized before used
        if (!initialize(stclContext, self)) {
            logWarn(stclContext, "Cannot initialize slot %s", self);
            return;
        }

        // get SQL context
        PStcl sqlContext = getSQLContext(stclContext, self);
        if (StencilUtils.isNull(sqlContext)) {
            logWarn(stclContext, "No SQL context defined for doUnplugAll in stencil %s", self);
            return;
        }

        // create the query
        String table = getTableAliasForProperty(stclContext, self);
        if (StringUtils.isBlank(table)) {
            table = getTableName(stclContext, self);
        }
        String from = getKeysFrom(stclContext, self);
        String condition = getKeysCondition(stclContext, null, self);
        String query = String.format("DELETE %s FROM %s WHERE %s", table, from, condition);

        // does the query
        SQLContextStcl stcl = (SQLContextStcl) sqlContext.getReleasedStencil(stclContext);
        stcl.updateQuery(stclContext, query, sqlContext);

        // the stencil context map is then obsolete
        _stencil_context_uid = 0;
    }

    @Override
    public Result doMultiUnplug(StclContext stclContext, String keys, PSlot<StclContext, PStcl> self) {
        return doMultiUnplug(stclContext, StringHelper.splitShortStringAndTrim(keys, PathUtils.MULTI), self);
    }

    /**
     * Removes several stencils in one query.
     * 
     * @param stclContext
     *            the stencil context.
     * @param keys
     *            the array of keys to be removed.
     * @param self
     *            this slot as a plugged slot.
     * @return
     */
    private Result doMultiUnplug(StclContext stclContext, String[] keys, PSlot<StclContext, PStcl> self) {

        // should be initialized before used
        if (!initialize(stclContext, self)) {
            String msg = logWarn(stclContext, "Cannot initialize slot %s", self);
            return Result.error(msg);
        }

        // get SQL context
        PStcl sqlContext = getSQLContext(stclContext, self);
        if (StencilUtils.isNull(sqlContext)) {
            String msg = logWarn(stclContext, "no SQL context defined for doMultiUnplug instencil %s", self);
            return Result.error(msg);
        }

        // create the query
        String table = getTableName(stclContext, self);
        String sql_where = null;
        for (String key : keys) {
            String c = String.format("%s='%s'", SQLStcl.Slot.ID, key);
            if (sql_where != null) {
                sql_where = String.format("%s OR %s", sql_where, c);
            } else {
                sql_where = c;
            }
        }
        String query = String.format("DELETE FROM %s WHERE %s", table, sql_where);

        // does the query
        SQLContextStcl stcl = (SQLContextStcl) sqlContext.getReleasedStencil(stclContext);
        Result result = stcl.updateQuery(stclContext, query, sqlContext);

        // the stencil context map is then obsolete
        _stencil_context_uid = 0;

        return result;
    }

    public SqlUtils.SqlAssoc newSqlAssoc(StclContext stclContext, PStcl stencil, PSlot<StclContext, PStcl> self) {
        String from = getKeysFrom(stclContext, self);
        String table = getTableName(stclContext, self);
        String alias = getTableAliasForProperty(stclContext, self);
        return SqlUtils.newSqlAssoc(from, table, alias, stencil);
    }

    public SqlUtils.SqlCondition newSqlCondition(StclContext stclContext, PStcl stencil,
            PSlot<StclContext, PStcl> self) {
        String from = getKeysFrom(stclContext, self);
        String table = getTableName(stclContext, self);
        String alias = getTableAliasForProperty(stclContext, self);
        return SqlUtils.newSqlCondition(from, table, alias, stencil);
    }
}