Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/AbstractKeyGenerator.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/AbstractKeyGenerator.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/AbstractKeyGenerator.java (working copy) @@ -13,29 +13,25 @@ * @author Stein M. Hugubakken */ public abstract class AbstractKeyGenerator implements KeyGenerator { - private abstract class AbstractIdentityValue implements IdentityValue { - public AbstractIdentityValue() { } - } - - private class BigDecimalIdentityValue extends AbstractIdentityValue { + private class IntegerSqlTypeHandler implements SqlTypeHandler { public Object getValue(final ResultSet rs) throws SQLException { - return rs.getBigDecimal(1); + return new Integer(rs.getInt(1)); } } - private class IntegerIdentityValue extends AbstractIdentityValue { + private class LongSqlTypeHandler implements SqlTypeHandler { public Object getValue(final ResultSet rs) throws SQLException { - return new Integer(rs.getInt(1)); + return new Long(rs.getLong(1)); } } - private class LongIdentityValue extends AbstractIdentityValue { + private class BigDecimalSqlTypeHandler implements SqlTypeHandler { public Object getValue(final ResultSet rs) throws SQLException { - return new Long(rs.getLong(1)); + return rs.getBigDecimal(1); } } - private class StringIdentityValue extends AbstractIdentityValue { + private class StringSqlTypeHandler implements SqlTypeHandler { public Object getValue(final ResultSet rs) throws SQLException { return rs.getString(1); } @@ -45,9 +41,9 @@ protected String _factoryName; - protected byte _style = AFTER_INSERT; + private SqlTypeHandler _sqlTypeHandler; - protected IdentityValue _identityValue = null; + private byte _style; protected void checkSupportedFactory(final PersistenceFactory factory) throws MappingException { @@ -52,7 +48,7 @@ protected void checkSupportedFactory(final PersistenceFactory factory) throws MappingException { _factoryName = factory.getFactoryName(); - String supportedFactoryNames[] = getSupportedFactoryNames(); + String[] supportedFactoryNames = getSupportedFactoryNames(); boolean supported = false; for (int i = 0; i < supportedFactoryNames.length; i++) { if (_factoryName.equals(supportedFactoryNames[i])) { @@ -61,7 +57,8 @@ } if (!supported) { - String msg = Messages.format("mapping.keyGenNotCompatible", getClass().getName(), _factoryName); + String msg = Messages.format("mapping.keyGenNotCompatible", + getClass().getName(), _factoryName); throw new MappingException(msg); } } @@ -66,6 +63,27 @@ } } + protected abstract String[] getSupportedFactoryNames(); + + protected void initSqlTypeHandler(final int sqlType) { + if (sqlType == Types.INTEGER) { + _sqlTypeHandler = new IntegerSqlTypeHandler(); + } else if (sqlType == Types.BIGINT) { + _sqlTypeHandler = new LongSqlTypeHandler(); + } else if ((sqlType == Types.CHAR) || (sqlType == Types.VARCHAR)) { + _sqlTypeHandler = new StringSqlTypeHandler(); + } else { + _sqlTypeHandler = new BigDecimalSqlTypeHandler(); + } + } + + protected SqlTypeHandler getSqlTypeHandler() { + return _sqlTypeHandler; + } + + /** + * {@inheritDoc} + */ public void supportsSqlType(final int sqlType) throws MappingException { if (sqlType != Types.INTEGER && sqlType != Types.NUMERIC @@ -71,7 +89,8 @@ && sqlType != Types.NUMERIC && sqlType != Types.DECIMAL && sqlType != Types.BIGINT) { - String msg = Messages.format("mapping.keyGenSQLType", getClass().getName(), new Integer(sqlType)); + String msg = Messages.format("mapping.keyGenSQLType", + getClass().getName(), new Integer(sqlType)); throw new MappingException(msg); } } @@ -76,40 +95,29 @@ } } + protected final void setStyle(final byte style) { + _style = style; + } + /** - * Style of key generator: BEFORE_INSERT, DURING_INSERT or AFTER_INSERT ? + * {@inheritDoc} */ - public byte getStyle() { + public final byte getStyle() { return _style; } - public abstract String[] getSupportedFactoryNames(); - - protected void initIdentityValue(final int sqlType) { - if (sqlType == Types.INTEGER) { - _identityValue = new IntegerIdentityValue(); - } else if (sqlType == Types.BIGINT) { - _identityValue = new LongIdentityValue(); - } else if ((sqlType == Types.CHAR) || (sqlType == Types.VARCHAR)) { - _identityValue = new StringIdentityValue(); - } else { - _identityValue = new BigDecimalIdentityValue(); - } - } - /** - * Is key generated in the same connection as INSERT? Default is true. + * {@inheritDoc} */ - public boolean isInSameConnection() { - return true; + public String patchSQL(final String insert, final String primKeyName) + throws MappingException { + return insert; } /** - * Gives a possibility to patch the Castor-generated SQL statement - * for INSERT (makes sense for DURING_INSERT key generators). + * {@inheritDoc} */ - public String patchSQL(final String insert, final String primKeyName) - throws MappingException { - return insert; + public boolean isInSameConnection() { + return true; } } Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/AbstractKeyGenValueHandler.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/AbstractKeyGenValueHandler.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/AbstractKeyGenValueHandler.java (working copy) @@ -11,7 +11,7 @@ public abstract class AbstractKeyGenValueHandler { private KeyGenerator _keyGenerator; - private IdentityValue _identityValue; + private SqlTypeHandler _sqlTypeHandler; AbstractKeyGenValueHandler() { } @@ -18,7 +18,7 @@ public Object getValue(final PreparedStatement stmt) throws PersistenceException, SQLException { ResultSet rs = stmt.executeQuery(); - if (rs.next()) { return _identityValue.getValue(rs); } + if (rs.next()) { return _sqlTypeHandler.getValue(rs); } String msg = Messages.format("persist.keyGenFailed", _keyGenerator.getClass().getName()); throw new PersistenceException(msg); } @@ -48,8 +48,8 @@ _keyGenerator = generator; } - public void setIdentityValue(final IdentityValue identityValue) { - _identityValue = identityValue; + public void setSqlTypeHandler(final SqlTypeHandler sqlTypeHandler) { + _sqlTypeHandler = sqlTypeHandler; } } Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/HighLowKeyGenerator.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/HighLowKeyGenerator.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/HighLowKeyGenerator.java (working copy) @@ -1,46 +1,17 @@ -/** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Intalio, Inc. For written permission, - * please contact info@exolab.org. - * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Intalio, Inc. Exolab is a registered - * trademark of Intalio, Inc. - * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). +/* + * Copyright 2008 Oleg Nitz, Bruce Snyder, Ralf Joachim * - * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * INTALIO, INC. OR ITS 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright 1999 (C) Intalio, Inc. All Rights Reserved. + * http://www.apache.org/licenses/LICENSE-2.0 * - * $Id$ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.castor.cpa.persistence.sql.keygen; @@ -50,7 +21,8 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; -import java.util.Hashtable; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; import org.apache.commons.logging.Log; @@ -64,22 +36,194 @@ import org.exolab.castor.persist.spi.QueryExpression; /** - * The parent abstract class for HIGH-LOW key generators. + * HIGH-LOW key generators. * - * @author Oleg Nitz - * @author Bruce Snyder + * @see HighLowKeyGeneratorFactory + * @author Oleg Nitz + * @author Bruce Snyder + * @author Ralf Joachim * @version $Revision$ $Date: 2006-04-10 16:39:24 -0600 (Mon, 10 Apr 2006) $ - * @see HighLowKeyGeneratorFactory */ -public class HighLowKeyGenerator implements KeyGenerator { - /** - * The Jakarta - * Commons Logging instance used for all logging. - */ - private static Log _log = LogFactory.getFactory().getInstance(HighLowKeyGenerator.class); +public final class HighLowKeyGenerator implements KeyGenerator { + //----------------------------------------------------------------------------------- + + private interface HighLowSqlTypeHandler { + void init(final ResultSet rs) throws SQLException; + boolean hasNext(); + Object next(); + void bindTable(PreparedStatement stmt, int index) throws SQLException; + void bindLast(PreparedStatement stmt, int index) throws SQLException; + void bindMax(PreparedStatement stmt, int index) throws SQLException; + } + + private static class HighLowIntegerSqlTypeHandler implements HighLowSqlTypeHandler { + private final String _table; + private final int _grab; + private int _last = 0; + private int _max = 0; + + public HighLowIntegerSqlTypeHandler(final String table, final int grab) { + _table = table; + _grab = grab; + } + + /** + * {@inheritDoc} + */ + public void init(final ResultSet rs) throws SQLException { + _last = 0; + if (rs != null) { _last = rs.getInt(1); } + _max = _last + _grab; + } + + /** + * {@inheritDoc} + */ + public boolean hasNext() { return _last < _max; } + + /** + * {@inheritDoc} + */ + public Object next() { return new Integer(++_last); } + + /** + * {@inheritDoc} + */ + public void bindTable(final PreparedStatement stmt, final int index) throws SQLException { + stmt.setString(index, _table); + } + + /** + * {@inheritDoc} + */ + public void bindLast(final PreparedStatement stmt, final int index) throws SQLException { + stmt.setInt(index, _last); + } + + /** + * {@inheritDoc} + */ + public void bindMax(final PreparedStatement stmt, final int index) throws SQLException { + stmt.setInt(index, _max); + } + } + + private static class HighLowLongSqlTypeHandler implements HighLowSqlTypeHandler { + private final String _table; + private final long _grab; + private long _last = 0; + private long _max = 0; + + public HighLowLongSqlTypeHandler(final String table, final int grab) { + _table = table; + _grab = grab; + } + + /** + * {@inheritDoc} + */ + public void init(final ResultSet rs) throws SQLException { + _last = 0; + if (rs != null) { _last = rs.getLong(1); } + _max = _last + _grab; + } + + /** + * {@inheritDoc} + */ + public boolean hasNext() { return _last < _max; } + + /** + * {@inheritDoc} + */ + public Object next() { return new Long(++_last); } + + /** + * {@inheritDoc} + */ + public void bindTable(final PreparedStatement stmt, final int index) throws SQLException { + stmt.setString(index, _table); + } + + /** + * {@inheritDoc} + */ + public void bindLast(final PreparedStatement stmt, final int index) throws SQLException { + stmt.setLong(index, _last); + } + + /** + * {@inheritDoc} + */ + public void bindMax(final PreparedStatement stmt, final int index) throws SQLException { + stmt.setLong(index, _max); + } + } + + private static class HighLowBigDecimalSqlTypeHandler implements HighLowSqlTypeHandler { + private static final BigDecimal ZERO = new BigDecimal(0); + private static final BigDecimal ONE = new BigDecimal(1); + private final String _table; + private final BigDecimal _grab; + private BigDecimal _last = ZERO; + private BigDecimal _max = ZERO; + + public HighLowBigDecimalSqlTypeHandler(final String table, final int grab) { + _table = table; + _grab = new BigDecimal(grab); + } + + /** + * {@inheritDoc} + */ + public void init(final ResultSet rs) throws SQLException { + _last = null; + if (rs != null) { _last = rs.getBigDecimal(1); } + if (_last == null) { _last = ZERO; } + _max = _last.add(_grab); + } + + /** + * {@inheritDoc} + */ + public boolean hasNext() { return (_last.compareTo(_max) < 0); } + + /** + * {@inheritDoc} + */ + public Object next() { + _last = _last.add(ONE); + return _last; + } + + /** + * {@inheritDoc} + */ + public void bindTable(final PreparedStatement stmt, final int index) throws SQLException { + stmt.setString(index, _table); + } + + /** + * {@inheritDoc} + */ + public void bindLast(final PreparedStatement stmt, final int index) throws SQLException { + stmt.setBigDecimal(index, _last); + } + + /** + * {@inheritDoc} + */ + public void bindMax(final PreparedStatement stmt, final int index) throws SQLException { + stmt.setBigDecimal(index, _max); + } + } - private static final BigDecimal ONE = new BigDecimal(1); + //----------------------------------------------------------------------------------- + /** The Jakarta + * Commons Logging instance used for all logging. */ + private static final Log LOG = LogFactory.getLog(HighLowKeyGenerator.class); + private static final String SEQ_TABLE = "table"; private static final String SEQ_KEY = "key-column"; @@ -92,6 +236,8 @@ private static final String GLOBAL = "global"; + private final Map _handlers = new HashMap(); + private final PersistenceFactory _factory; private final int _sqlType; @@ -96,34 +242,27 @@ private final int _sqlType; - // Sequence table name - private final String _seqTable; + /** Sequence table name. */ + private String _seqTable; - // Sequence table key column name - private final String _seqKey; + /** Sequence table key column name. */ + private String _seqKey; - // Sequence table value column name - private final String _seqValue; + /** Sequence table value column name. */ + private String _seqValue; - // grab size as int - private int _grabSizeI; + /** Grab size. */ + private int _grabSize; - // flag of use of the same connection - // (less efficient, but in EJB envirinment we have no choice for now) + /** Shell the same connection be used? + *
+ * Note: This is less efficient, but in EJB envirinment we have no choice for now. */ private boolean _sameConnection; - // grab size as BigDecimal - private BigDecimal _grabSizeD; - - // last generated values - private Hashtable _lastValues = new Hashtable(); - - // maximum possible values after which database operation is needed - private Hashtable _maxValues = new Hashtable(); - - // to generate globally unique identities + /** Shell globally unique identities be generated? */ private boolean _global; + //----------------------------------------------------------------------------------- /** * Initialize the HIGH-LOW key generator. @@ -130,12 +269,15 @@ */ public HighLowKeyGenerator(final PersistenceFactory factory, final Properties params, final int sqlType) throws MappingException { - String factorStr; - _factory = factory; _sqlType = sqlType; + supportsSqlType(sqlType); - + + initFromParameters(params); + } + + public void initFromParameters(final Properties params) throws MappingException { _seqTable = params.getProperty(SEQ_TABLE); if (_seqTable == null) { throw new MappingException(Messages.format( @@ -154,17 +296,17 @@ "mapping.KeyGenParamNotSet", SEQ_VALUE, getClass().getName())); } - factorStr = params.getProperty(GRAB_SIZE, "10"); + String grabSize = params.getProperty(GRAB_SIZE, "10"); try { - _grabSizeI = Integer.parseInt(factorStr); + _grabSize = Integer.parseInt(grabSize); } catch (NumberFormatException except) { - _grabSizeI = 0; + _grabSize = 0; } - if (_grabSizeI <= 0) { + if (_grabSize <= 0) { throw new MappingException(Messages.format( - "mapping.wrongKeyGenParam", factorStr, GRAB_SIZE, getClass().getName())); + "mapping.wrongKeyGenParam", grabSize, GRAB_SIZE, getClass().getName())); } - _grabSizeD = new BigDecimal(_grabSizeI); + _sameConnection = "true".equals(params.getProperty(SAME_CONNECTION)); _global = "true".equals(params.getProperty(GLOBAL)); } @@ -169,186 +311,93 @@ _global = "true".equals(params.getProperty(GLOBAL)); } - /** - * Determine if the key generator supports a given sql type. - * - * @param sqlType - * @throws MappingException - */ - public void supportsSqlType(final int sqlType) throws MappingException { - if ((sqlType != Types.INTEGER) && (sqlType != Types.NUMERIC) - && (sqlType != Types.DECIMAL) && (sqlType != Types.BIGINT)) { - throw new MappingException(Messages.format( - "mapping.keyGenSQLType", getClass().getName(), new Integer(sqlType))); - } - } + //----------------------------------------------------------------------------------- /** - * @param conn An open connection within the given transaction - * @param internalTableName The table name - * @param primKeyName The primary key name - * @param props A temporary replacement for Principal object - * @return A new key - * @throws PersistenceException An error occured talking to persistent - * storage + * {@inheritDoc} */ public synchronized Object generateKey(final Connection conn, final String tableName, final String primKeyName, final Properties props) throws PersistenceException { - Object last; - Object max; - boolean inRange; - String internalTableName = tableName; - if (_global) { - internalTableName = ""; - } - last = _lastValues.get(internalTableName); - max = _maxValues.get(internalTableName); - if (last != null) { + if (_global) { internalTableName = ""; } + + HighLowSqlTypeHandler handler = (HighLowSqlTypeHandler) _handlers.get(internalTableName); + if (handler == null) { if (_sqlType == Types.INTEGER) { - last = new Integer(((Integer) last).intValue() + 1); + handler = new HighLowIntegerSqlTypeHandler(internalTableName, _grabSize); } else if (_sqlType == Types.BIGINT) { - last = new Long(((Long) last).longValue() + 1); + handler = new HighLowLongSqlTypeHandler(internalTableName, _grabSize); } else { - last = ((BigDecimal) last).add(ONE); + handler = new HighLowBigDecimalSqlTypeHandler(internalTableName, _grabSize); } - } else { - QueryExpression query; - String sql; - String sql2; + _handlers.put(internalTableName, handler); + } + + if (!handler.hasNext()) { + // Create "SELECT seq_val FROM seq_table WHERE seq_key='table'" + // with database-dependent keyword for lock + // Note: Some databases (InstantDB, HypersonicSQL) don't support such locks. + QueryExpression query = _factory.getQueryExpression(); + query.addColumn(_seqTable, _seqValue); + query.addCondition(_seqTable, _seqKey, QueryExpression.OP_EQUALS, JDBCSyntax.PARAMETER); + String lockSQL = query.getStatement(true); + + // For the case that "SELECT FOR UPDATE" is not supported, perform dirty checking + String updateSQL = "UPDATE " + _seqTable + + " SET " + _seqValue + "=" + JDBCSyntax.PARAMETER + + JDBCSyntax.WHERE + _seqKey + QueryExpression.OP_EQUALS + JDBCSyntax.PARAMETER + + JDBCSyntax.AND + _seqValue + QueryExpression.OP_EQUALS + JDBCSyntax.PARAMETER; + + String maxSQL = JDBCSyntax.SELECT + "MAX(" + primKeyName + ") " + + "FROM " + internalTableName; + + String insertSQL = "INSERT INTO " + _seqTable + + " (" + _seqKey + "," + _seqValue + ") VALUES (?, ?)"; + PreparedStatement stmt = null; - PreparedStatement stmt2 = null; - ResultSet rs; - boolean success; - try { - // the separate connection should be committed/rolled back at this point - if (!_sameConnection) { - conn.rollback(); - } - - // Create SQL sentence of the form - // "SELECT seq_val FROM seq_table WHERE seq_key='table'" - // with database-dependent keyword for lock - // [george stewart] Note, that some databases (InstantDB, - // HypersonicSQL) don't support such locks. - query = _factory.getQueryExpression(); - query.addColumn(_seqTable, _seqValue); - query.addCondition(_seqTable, _seqKey, QueryExpression.OP_EQUALS, - JDBCSyntax.PARAMETER); - - // SELECT and put lock on the last record - sql = query.getStatement(true); - // For the case if the "SELECT FOR UPDATE" is not supported - // we perform dirty checking - sql2 = "UPDATE " + _seqTable - + " SET " + _seqValue + "=" + JDBCSyntax.PARAMETER - + JDBCSyntax.WHERE + _seqKey + QueryExpression.OP_EQUALS + JDBCSyntax.PARAMETER - + JDBCSyntax.AND + _seqValue + QueryExpression.OP_EQUALS + JDBCSyntax.PARAMETER; - - stmt = conn.prepareStatement(sql); - stmt.setString(1, internalTableName); - stmt2 = conn.prepareStatement(sql2); - stmt2.setString(2, internalTableName); + // Separate connection should be committed/rolled back at this point + if (!_sameConnection) { conn.rollback(); } // Retry 7 times (lucky number) - success = false; + boolean success = false; for (int i = 0; !success && (i < 7); i++) { - rs = stmt.executeQuery(); + stmt = conn.prepareStatement(lockSQL); + handler.bindTable(stmt, 1); + ResultSet rs = stmt.executeQuery(); if (rs.next()) { - if (_sqlType == Types.INTEGER) { - int value; - int maxVal; - - value = rs.getInt(1); - stmt2.setInt(3, value); - last = new Integer(value + 1); - maxVal = value + _grabSizeI; - max = new Integer(maxVal); - stmt2.setInt(1, maxVal); - } else if (_sqlType == Types.BIGINT) { - long value; - long maxVal; - - value = rs.getLong(1); - stmt2.setLong(3, value); - last = new Long(value + 1); - maxVal = value + _grabSizeI; - max = new Long(maxVal); - stmt2.setLong(1, maxVal); - } else { - BigDecimal value; - BigDecimal maxVal; - - value = rs.getBigDecimal(1); - stmt2.setBigDecimal(3, value); - last = value.add(ONE); - maxVal = value.add(_grabSizeD); - max = maxVal; - stmt2.setBigDecimal(1, maxVal); - } - // For the case if the "SELECT FOR UPDATE" is not supported - // we perform dirty checking - success = (stmt2.executeUpdate() == 1); + handler.init(rs); + stmt.close(); + + stmt = conn.prepareStatement(updateSQL); + handler.bindMax(stmt, 1); + handler.bindTable(stmt, 2); + handler.bindLast(stmt, 3); } else { - // [Terry Child] Initialize the counter with MAX(pk) + 1 - // for the case of switching from some other key generator - // to HIGH-LOW stmt.close(); - if (!_global) { - String sqlStatement = JDBCSyntax.SELECT + "MAX(" + primKeyName + ") " - + "FROM " + internalTableName; - stmt = conn.prepareStatement(sqlStatement); - rs = stmt.executeQuery(); - } - if (_sqlType == Types.INTEGER) { - int maxPK = 0; - - if (!_global && rs.next()) { - maxPK = rs.getInt(1); - } - last = new Integer(maxPK + 1); - max = new Integer(maxPK + _grabSizeI); - } else if (_sqlType == Types.BIGINT) { - long maxPK = 0; - if (!_global && rs.next()) { - maxPK = rs.getLong(1); - } - last = new Long(maxPK + 1); - max = new Long(maxPK + _grabSizeI); + if (_global) { + handler.init(null); } else { - BigDecimal maxPK = null; - - if (!_global && rs.next()) { - maxPK = rs.getBigDecimal(1); - } - if (maxPK == null) { - maxPK = new BigDecimal(0); - } - last = maxPK.add(ONE); - max = maxPK.add(_grabSizeD); + stmt = conn.prepareStatement(maxSQL); + rs = stmt.executeQuery(); + handler.init(rs); + stmt.close(); } - stmt2.close(); - String sqlStatement = "INSERT INTO " + _seqTable - + " (" + _seqKey + "," + _seqValue + ") VALUES (?, ?)"; - stmt2 = conn.prepareStatement(sqlStatement); - stmt2.setString(1, internalTableName); - stmt2.setObject(2, max); - stmt2.executeUpdate(); - success = true; - } - } - if (!_sameConnection) { - if (success) { - conn.commit(); - } else { - conn.rollback(); + stmt = conn.prepareStatement(insertSQL); + handler.bindTable(stmt, 1); + handler.bindMax(stmt, 2); } + success = (stmt.executeUpdate() == 1); + stmt.close(); } - if (!success) { + + if (success) { + if (!_sameConnection) { conn.commit(); } + } else { + if (!_sameConnection) { conn.rollback(); } throw new PersistenceException(Messages.format( "persist.keyGenFailed", getClass().getName())); } @@ -353,12 +402,10 @@ "persist.keyGenFailed", getClass().getName())); } } catch (SQLException ex) { - if (!_sameConnection) { - try { - conn.rollback(); - } catch (SQLException ex2) { - _log.warn ("Problem rolling back JDBC transaction.", ex2); - } + try { + if (!_sameConnection) { conn.rollback(); } + } catch (SQLException ex2) { + LOG.warn ("Problem rolling back JDBC transaction.", ex2); } throw new PersistenceException(Messages.format( "persist.keyGenSQL", getClass().getName(), ex.toString()), ex); @@ -363,19 +410,10 @@ throw new PersistenceException(Messages.format( "persist.keyGenSQL", getClass().getName(), ex.toString()), ex); } finally { - if (stmt != null) { - try { - stmt.close(); - } catch (SQLException ex) { - _log.warn (Messages.message("persist.stClosingFailed"), ex); - } - } - if (stmt2 != null) { - try { - stmt2.close(); - } catch (SQLException ex) { - _log.warn (Messages.message("persist.stClosingFailed"), ex); - } + try { + if (stmt != null) { stmt.close(); } + } catch (SQLException ex) { + LOG.warn (Messages.message("persist.stClosingFailed"), ex); } } } @@ -380,48 +418,42 @@ } } - if (_sqlType == Types.INTEGER) { - inRange = (((Integer) last).intValue() < ((Integer) max).intValue()); - } else if (_sqlType == Types.BIGINT) { - inRange = (((Long) last).longValue() < ((Long) max).longValue()); - } else { - inRange = (((BigDecimal) last).compareTo((BigDecimal) max) < 0); - } + return handler.next(); + } - if (inRange) { - _lastValues.put(internalTableName, last); - _maxValues.put(internalTableName, max); - } else { - _lastValues.remove(internalTableName); - _maxValues.remove(internalTableName); + /** + * {@inheritDoc} + */ + public void supportsSqlType(final int sqlType) throws MappingException { + if ((sqlType != Types.INTEGER) + && (sqlType != Types.BIGINT) + && (sqlType != Types.NUMERIC) + && (sqlType != Types.DECIMAL)) { + throw new MappingException(Messages.format("mapping.keyGenSQLType", + getClass().getName(), new Integer(sqlType))); } - return last; } - /** - * Style of key generator: BEFORE_INSERT, DURING_INSERT or AFTER_INSERT ? + * {@inheritDoc} */ - public final byte getStyle() { + public byte getStyle() { return BEFORE_INSERT; } - /** - * Gives a possibility to patch the Castor-generated SQL statement - * for INSERT (makes sense for DURING_INSERT key generators). + * {@inheritDoc} */ - public final String patchSQL(final String insert, final String primKeyName) { - return insert; + public boolean isInSameConnection() { + return _sameConnection; } - /** - * Is key generated in the same connection as INSERT? + * {@inheritDoc} */ - public final boolean isInSameConnection() { - return _sameConnection; + public String patchSQL(final String insert, final String primKeyName) { + return insert; } -} - + //----------------------------------------------------------------------------------- +} \ No newline at end of file Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/HighLowKeyGeneratorFactory.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/HighLowKeyGeneratorFactory.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/HighLowKeyGeneratorFactory.java (working copy) @@ -1,46 +1,17 @@ -/** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. +/* + * Copyright 2008 Oleg Nitz, Ralf Joachim * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Intalio, Inc. For written permission, - * please contact info@exolab.org. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Intalio, Inc. Exolab is a registered - * trademark of Intalio, Inc. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). - * - * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * INTALIO, INC. OR ITS 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. - * - * Copyright 1999 (C) Intalio, Inc. All Rights Reserved. - * - * $Id$ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.castor.cpa.persistence.sql.keygen; @@ -52,36 +23,40 @@ import org.exolab.castor.persist.spi.PersistenceFactory; /** - * HIGH-LOW key generator factory. - * The short name of this key generator is "HIGH-LOW". - * It uses the following alrorithm: a special sequence table must be in - * the database with keeps the maximum key values. The name of the - * sequence table is a mandatory parameter of the key generator, - * the parameter name is "table". - * The name of the primary key column of the sequence table and - * the name of the column in which maximum values are stored are - * mandatory parameters with the names "key-column" and "value-column", - * respectively. The key column contains table names, so it must be of - * a character type (char or varchar). The value column contains primary key - * values, it must be of a numeric type (numeric or int). - * Key generator reads the maximum value X for the given table, - * writes the new value (X + N) to the sequence table and during next N - * calls returns values X + 1, ..., X + N without database access. - * Number N (which sometimes is called "grab size") is an optional - * parameter of the key generator, the parameter name is "grab-size", - * default value is "1". - * For example, if you want to obtain HIGH-LOW key generator with - * 3 digits in the LOW part of the key, you should set "grab-size" to "1000". + * HIGH-LOW key generator factory. The short name of this key generator is + * "HIGH-LOW". + *
+ * It uses the following alrorithm: a special sequence table must be in the + * database with keeps the maximum key values. The name of the sequence table is + * a mandatory parameter of the key generator, the parameter name is "table". + * The name of the primary key column of the sequence table and the name of the + * column in which maximum values are stored are mandatory parameters with the + * names "key-column" and "value-column", respectively. The key column contains + * table names, so it must be of a character type (char or varchar). The value + * column contains primary key values, it must be of a numeric type (numeric or + * int). Key generator reads the maximum value X for the given table, writes the + * new value (X + N) to the sequence table and during next N calls returns values + * X + 1, ..., X + N without database access. Number N (which sometimes is called + * "grab size") is an optional parameter of the key generator, the parameter name + * is "grab-size", default value is "10". For example, if you want to obtain + * HIGH-LOW key generator with 3 digits in the LOW part of the key, you should + * set "grab-size" to "1000". * - * @author Oleg Nitz - * @version $Revision$ $Date: 2005-06-01 06:08:22 -0600 (Wed, 01 Jun 2005) $ * @see HighLowKeyGenerator + * @author Oleg Nitz + * @author Ralf Joachim + * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $ */ public final class HighLowKeyGeneratorFactory implements KeyGeneratorFactory { + //----------------------------------------------------------------------------------- + + /** + * {@inheritDoc} + */ + public String getName() { return "HIGH-LOW"; } + /** - * Produce the key generator. - * @param factory Helper object for obtaining database-specific QuerySyntax. - * @param params Parameters for key generator. + * {@inheritDoc} */ public KeyGenerator getKeyGenerator(final PersistenceFactory factory, final Properties params, final int sqlType) throws MappingException { @@ -89,11 +64,5 @@ return new HighLowKeyGenerator(factory, params, sqlType); } - /** - * The short name of this key generator is "HIGH-LOW". - */ - public String getName() { - return "HIGH-LOW"; - } -} - + //----------------------------------------------------------------------------------- +} \ No newline at end of file Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/IdentityKeyGenerator.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/IdentityKeyGenerator.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/IdentityKeyGenerator.java (working copy) @@ -46,6 +46,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.Properties; @@ -55,6 +56,7 @@ import org.castor.util.Messages; import org.exolab.castor.jdo.PersistenceException; import org.exolab.castor.mapping.MappingException; +import org.exolab.castor.persist.spi.KeyGenerator; import org.exolab.castor.persist.spi.PersistenceFactory; /** @@ -59,6 +61,8 @@ /** * IDENTITY key generator. + * + * @see IdentityKeyGeneratorFactory * @author Oleg Nitz * @author Stein M. Hugubakken * @author Bruce Snyder @@ -63,9 +67,10 @@ * @author Stein M. Hugubakken * @author Bruce Snyder * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $ - * @see IdentityKeyGeneratorFactory */ -public final class IdentityKeyGenerator extends AbstractKeyGenerator { +public final class IdentityKeyGenerator implements KeyGenerator { + //----------------------------------------------------------------------------------- + private abstract class IdentityKeyGenValueHandler extends AbstractKeyGenValueHandler { protected abstract Object getValue(Connection conn, String tableName) throws PersistenceException; } @@ -83,11 +88,10 @@ // Statement worked with IBM UDB v7 and v8 but not with IBM DB2 v6. // StringBuffer buf = new StringBuffer("SELECT IDENTITY_VAL_LOCAL() FROM "); // buf.append(tableName).append(" FETCH FIRST ROW ONLY"); + // return getValue(buf.toString(), conn); // Statement works with IBM UDB and IBM DB2. - StringBuffer buf = new StringBuffer(); - buf.append("SELECT IDENTITY_VAL_LOCAL() FROM sysibm.sysdummy1"); - return getValue(buf.toString(), conn); + return getValue("SELECT IDENTITY_VAL_LOCAL() FROM sysibm.sysdummy1", conn); } } @@ -150,6 +154,34 @@ } } + //----------------------------------------------------------------------------------- + + private class IntegerSqlTypeHandler implements SqlTypeHandler { + public Object getValue(final ResultSet rs) throws SQLException { + return new Integer(rs.getInt(1)); + } + } + + private class LongSqlTypeHandler implements SqlTypeHandler { + public Object getValue(final ResultSet rs) throws SQLException { + return new Long(rs.getLong(1)); + } + } + + private class BigDecimalSqlTypeHandler implements SqlTypeHandler { + public Object getValue(final ResultSet rs) throws SQLException { + return rs.getBigDecimal(1); + } + } + + private class StringSqlTypeHandler implements SqlTypeHandler { + public Object getValue(final ResultSet rs) throws SQLException { + return rs.getString(1); + } + } + + //----------------------------------------------------------------------------------- + /** * The Jakarta * Commons Logging instance used for all logging. @@ -156,9 +188,13 @@ */ private static Log _log = LogFactory.getFactory().getInstance(IdentityKeyGenerator.class); - private IdentityKeyGenValueHandler _type = null; + private final PersistenceFactory _factory; + + private SqlTypeHandler _sqlTypeHandler; + + private IdentityKeyGenValueHandler _type; - private String _fName = null; + //----------------------------------------------------------------------------------- /** * Initialize the IDENTITY key generator. @@ -169,57 +205,61 @@ */ public IdentityKeyGenerator(final PersistenceFactory factory, final int sqlType) throws MappingException { - _fName = factory.getFactoryName(); - - checkSupportedFactory(factory); + _factory = factory; + + supportsFactory(factory); supportsSqlType(sqlType); - initIdentityValue(sqlType); - initType(_fName); + initSqlTypeHandler(sqlType); + initType(); } - public String[] getSupportedFactoryNames() { - return new String[] {"sybase", "sql-server", "hsql", "mysql", "informix", "sapdb", + private void supportsFactory(final PersistenceFactory factory) + throws MappingException { + String factoryName = factory.getFactoryName(); + String[] supportedFactoryNames = new String[] { + "sybase", "sql-server", "hsql", "mysql", "informix", "sapdb", "db2", "derby", "postgresql", "pointbase" }; - } - - /** - * Determine if the key generator supports a given sql type. - * - * @param sqlType - * @throws MappingException - */ - public void supportsSqlType(final int sqlType) throws MappingException { - if (sqlType != Types.INTEGER && sqlType != Types.NUMERIC - && sqlType != Types.DECIMAL && sqlType != Types.BIGINT) { - throw new MappingException(Messages.format( - "mapping.keyGenSQLType", getClass().getName(), new Integer(sqlType))); + boolean supported = false; + for (int i = 0; i < supportedFactoryNames.length; i++) { + if (factoryName.equals(supportedFactoryNames[i])) { + supported = true; + } } - if (sqlType != Types.INTEGER && _fName.equals("hsql")) { - throw new MappingException(Messages.format( - "mapping.keyGenSQLType", getClass().getName(), new Integer(sqlType))); + if (!supported) { + String msg = Messages.format("mapping.keyGenNotCompatible", + getClass().getName(), factoryName); + throw new MappingException(msg); } - - if (sqlType != Types.NUMERIC && _fName.equals("derby")) { - throw new MappingException(Messages.format( - "mapping.keyGenSQLType", getClass().getName(), new Integer(sqlType))); - } } - private void initType(final String fName) { - if (fName.equals("hsql")) { + private void initSqlTypeHandler(final int sqlType) { + if (sqlType == Types.INTEGER) { + _sqlTypeHandler = new IntegerSqlTypeHandler(); + } else if (sqlType == Types.BIGINT) { + _sqlTypeHandler = new LongSqlTypeHandler(); + } else if ((sqlType == Types.CHAR) || (sqlType == Types.VARCHAR)) { + _sqlTypeHandler = new StringSqlTypeHandler(); + } else { + _sqlTypeHandler = new BigDecimalSqlTypeHandler(); + } + } + + private void initType() { + String factoryName = _factory.getFactoryName(); + if (factoryName.equals("hsql")) { _type = new HsqlType(); - } else if (fName.equals("mysql")) { + } else if (factoryName.equals("mysql")) { _type = new MySqlType(); - } else if (fName.equals("informix")) { + } else if (factoryName.equals("informix")) { _type = new InformixType(); - } else if (fName.equals("db2")) { + } else if (factoryName.equals("db2")) { _type = new DB2Type(); - } else if (fName.equals("sapdb")) { + } else if (factoryName.equals("sapdb")) { _type = new SapDbType(); - } else if (fName.equals("derby")) { + } else if (factoryName.equals("derby")) { _type = new DerbyType(); - } else if (fName.equals("postgresql")) { + } else if (factoryName.equals("postgresql")) { _type = new PostgresqlType(); } else { _type = new DefaultType(); @@ -225,16 +265,13 @@ _type = new DefaultType(); } _type.setGenerator(this); - _type.setIdentityValue(_identityValue); + _type.setSqlTypeHandler(_sqlTypeHandler); } + //----------------------------------------------------------------------------------- + /** - * @param conn An open connection within the given transaction. - * @param tableName The table name. - * @param primKeyName The primary key name. - * @param props A temporary replacement for Principal object. - * @return A new key. - * @throws PersistenceException An error occured talking to persistent storage. + * {@inheritDoc} */ public Object generateKey(final Connection conn, final String tableName, final String primKeyName, final Properties props) throws PersistenceException { @@ -245,4 +282,53 @@ return null; } } + + /** + * {@inheritDoc} + */ + public void supportsSqlType(final int sqlType) throws MappingException { + String factoryName = _factory.getFactoryName(); + if (factoryName.equals("hsql")) { + if (sqlType != Types.INTEGER) { + throw new MappingException(Messages.format( + "mapping.keyGenSQLType", getClass().getName(), new Integer(sqlType))); + } + } else if (factoryName.equals("derby")) { + if (sqlType != Types.NUMERIC) { + throw new MappingException(Messages.format( + "mapping.keyGenSQLType", getClass().getName(), new Integer(sqlType))); + } + } else { + if ((sqlType != Types.INTEGER) + && (sqlType != Types.NUMERIC) + && (sqlType != Types.DECIMAL) + && (sqlType != Types.BIGINT)) { + throw new MappingException(Messages.format( + "mapping.keyGenSQLType", getClass().getName(), new Integer(sqlType))); + } + } + } + + /** + * {@inheritDoc} + */ + public byte getStyle() { + return AFTER_INSERT; + } + + /** + * {@inheritDoc} + */ + public boolean isInSameConnection() { + return true; + } + + /** + * {@inheritDoc} + */ + public String patchSQL(final String insert, final String primKeyName) { + return insert; + } + + //----------------------------------------------------------------------------------- } Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/IdentityKeyGeneratorFactory.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/IdentityKeyGeneratorFactory.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/IdentityKeyGeneratorFactory.java (working copy) @@ -1,46 +1,17 @@ -/** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. +/* + * Copyright 2008 Oleg Nitz, Ralf Joachim * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Intalio, Inc. For written permission, - * please contact info@exolab.org. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Intalio, Inc. Exolab is a registered - * trademark of Intalio, Inc. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). - * - * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * INTALIO, INC. OR ITS 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. - * - * Copyright 1999 (C) Intalio, Inc. All Rights Reserved. - * - * $Id$ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.castor.cpa.persistence.sql.keygen; @@ -52,21 +23,26 @@ import org.exolab.castor.persist.spi.PersistenceFactory; /** - * IDENTITY key generator factory. - * The short name of this key generator is "IDENTITY". - * It works for Sybase and SQL Server identity (autoincrement) fields and - * fetched @@identity after insert. + * IDENTITY key generator factory. The short name of this key generator is "IDENTITY". + *
+ * It works for Sybase and SQL Server identity (autoincrement) fields and fetched + * @@identity after insert. * - * @author Oleg Nitz - * @version $Revision$ $Date: 2005-06-01 06:08:22 -0600 (Wed, 01 Jun 2005) $ * @see IdentityKeyGenerator + * @author Oleg Nitz + * @author Ralf Joachim + * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $ */ public final class IdentityKeyGeneratorFactory implements KeyGeneratorFactory { + //----------------------------------------------------------------------------------- + /** - * Produce the key generator. - * - * @param factory Helper object for obtaining database-specific QuerySyntax. - * @param params Parameters for key generator. + * {@inheritDoc} + */ + public String getName() { return "IDENTITY"; } + + /** + * {@inheritDoc} */ public KeyGenerator getKeyGenerator(final PersistenceFactory factory, final Properties params, final int sqlType) throws MappingException { @@ -74,10 +50,5 @@ return new IdentityKeyGenerator(factory, sqlType); } - /** - * The short name of this key generator is "IDENTITY". - */ - public String getName() { - return "IDENTITY"; - } + //----------------------------------------------------------------------------------- } Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/IdentityValue.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/IdentityValue.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/IdentityValue.java (working copy) @@ -1,8 +0,0 @@ -package org.castor.cpa.persistence.sql.keygen; - -import java.sql.ResultSet; -import java.sql.SQLException; - -public interface IdentityValue { - Object getValue(ResultSet rs) throws SQLException; -} Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/MaxKeyGenerator.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/MaxKeyGenerator.java (revision 7613) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/MaxKeyGenerator.java (working copy) @@ -1,46 +1,17 @@ -/** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: +/* + * Copyright 2008 Oleg Nitz, Leonardo Souza Mario Bueno, Bruce Snyder, Ralf Joachim * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Intalio, Inc. For written permission, - * please contact info@exolab.org. - * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Intalio, Inc. Exolab is a registered - * trademark of Intalio, Inc. - * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). - * - * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * INTALIO, INC. OR ITS 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. - * - * Copyright 1999 (C) Intalio, Inc. All Rights Reserved. - * - * $Id$ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.castor.cpa.persistence.sql.keygen; @@ -56,6 +27,7 @@ import org.apache.commons.logging.LogFactory; import org.castor.util.Messages; import org.exolab.castor.jdo.PersistenceException; +import org.exolab.castor.jdo.QueryException; import org.exolab.castor.mapping.MappingException; import org.exolab.castor.persist.spi.KeyGenerator; import org.exolab.castor.persist.spi.PersistenceFactory; @@ -62,118 +34,108 @@ import org.exolab.castor.persist.spi.QueryExpression; /** - * MAX key generator. - * @author Oleg Nitz - * @author Leonardo Souza Mario Bueno - * @author Bruce Snyder - * @version $Revision$ $Date: 2006-04-10 16:39:24 -0600 (Mon, 10 Apr 2006) $ + * MAX key generators. + * * @see MaxKeyGeneratorFactory + * @author Oleg Nitz + * @author Leonardo Souza Mario Bueno + * @author Bruce Snyder + * @author Ralf Joachim + * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $ */ public final class MaxKeyGenerator implements KeyGenerator { + //----------------------------------------------------------------------------------- + + private interface MaxSqlTypeHandler { + Object getValue(ResultSet rs) throws SQLException; + } + + private static class MaxIntegerSqlTypeHandler implements MaxSqlTypeHandler { + private static final Integer ONE = new Integer(1); + + /** + * {@inheritDoc} + */ + public Object getValue(final ResultSet rs) throws SQLException { + if (!rs.next()) { return ONE; } + return new Integer(rs.getInt(1) + 1); + } + } + + private static class MaxLongSqlTypeHandler implements MaxSqlTypeHandler { + private static final Long ONE = new Long(1); + + /** + * {@inheritDoc} + */ + public Object getValue(final ResultSet rs) throws SQLException { + if (!rs.next()) { return ONE; } + return new Long(rs.getLong(1) + 1); + } + } + + private static class MaxBigDecimalSqlTypeHandler implements MaxSqlTypeHandler { + private static final BigDecimal ONE = new BigDecimal(1); + + /** + * {@inheritDoc} + */ + public Object getValue(final ResultSet rs) throws SQLException { + if (!rs.next()) { return ONE; } + BigDecimal max = rs.getBigDecimal(1); + if (max == null) { return ONE; } + return max.add(ONE); + } + } + + //----------------------------------------------------------------------------------- + /** The Jakarta * Commons Logging instance used for all logging. */ - private static Log _log = LogFactory.getFactory().getInstance(MaxKeyGenerator.class); + private static final Log LOG = LogFactory.getLog(MaxKeyGenerator.class); - private static final BigDecimal ONE = new BigDecimal(1); - - private final int _sqlType; - private final PersistenceFactory _factory; + private MaxSqlTypeHandler _sqlTypeHandler; + + //----------------------------------------------------------------------------------- + /** * Initialize the MAX key generator. */ - public MaxKeyGenerator(final PersistenceFactory factory, final int sqlType) - throws MappingException { + public MaxKeyGenerator(final PersistenceFactory factory, final int sqlType) throws MappingException { _factory = factory; - _sqlType = sqlType; + supportsSqlType(sqlType); + initSqlTypeHandler(sqlType); } - /** - * Determine if the key generator supports a given sql type. - * - * @param sqlType - * @throws MappingException - */ - public void supportsSqlType(final int sqlType) throws MappingException { - if ((sqlType != Types.INTEGER) && (sqlType != Types.NUMERIC) - && (sqlType != Types.DECIMAL) && (sqlType != Types.BIGINT)) { - throw new MappingException(Messages.format( - "mapping.keyGenSQLType", getClass().getName(), new Integer(sqlType))); + private void initSqlTypeHandler(final int sqlType) { + if (sqlType == Types.INTEGER) { + _sqlTypeHandler = new MaxIntegerSqlTypeHandler(); + } else if (sqlType == Types.BIGINT) { + _sqlTypeHandler = new MaxLongSqlTypeHandler(); + } else { + _sqlTypeHandler = new MaxBigDecimalSqlTypeHandler(); } } + + //----------------------------------------------------------------------------------- /** * Generate a new key for the specified table as "MAX(primary_key) + 1". - * * If there is no records in the table, then the value 1 is returned. - * - * @param conn An open connection within the given transaction - * @param tableName The table name - * @param primKeyName The primary key name - * @param props A temporary replacement for Principal object - * @return A new key - * @throws PersistenceException An error occured talking to persistent - * storage + *
+ * {@inheritDoc} */ public Object generateKey(final Connection conn, final String tableName, final String primKeyName, final Properties props) throws PersistenceException { - - String sql; PreparedStatement stmt = null; - ResultSet rs; - QueryExpression query; - Object identity = null; - try { - query = _factory.getQueryExpression(); - if (_factory.getFactoryName().equals("mysql")) { - // Create SQL sentence of the form - // "SELECT MAX(pk) FROM table" - query.addSelect("MAX(" + _factory.quoteName(primKeyName) + ")"); - query.addTable(tableName); - - // SELECT without lock - sql = query.getStatement(false); - } else { - // Create SQL sentence of the form - // "SELECT pk FROM table WHERE pk=(SELECT MAX(t1.pk) FROM table t1)" - // with database-dependent keyword for lock - query.addColumn(tableName, primKeyName); - query.addCondition(tableName, primKeyName, QueryExpression.OP_EQUALS, - "(SELECT MAX(t1." + _factory.quoteName(primKeyName) + ") FROM " - + _factory.quoteName(tableName) + " t1)"); - - // SELECT and put lock on the last record - sql = query.getStatement(true); - } - + String sql = getQueryExpression(tableName, primKeyName); stmt = conn.prepareStatement(sql); - rs = stmt.executeQuery(); - - if (rs.next()) { - if (_sqlType == Types.INTEGER) { - identity = new Integer(rs.getInt(1) + 1); - } else if (_sqlType == Types.BIGINT) { - identity = new Long(rs.getLong(1) + 1); - } else { - BigDecimal max = rs.getBigDecimal(1); - if (max == null) { - identity = ONE; - } else { - identity = max.add(ONE); - } - } - } else { - if (_sqlType == Types.INTEGER) { - identity = new Integer(1); - } else if (_sqlType == Types.BIGINT) { - identity = new Long(1); - } else { - identity = ONE; - } - } + ResultSet rs = stmt.executeQuery(); + return _sqlTypeHandler.getValue(rs); } catch (SQLException ex) { throw new PersistenceException(Messages.format( "persist.keyGenSQL", getClass().getName(), ex.toString()), ex); @@ -182,20 +144,65 @@ try { stmt.close(); } catch (SQLException ex) { - _log.warn (Messages.message ("persist.stClosingFailed"), ex); + LOG.warn (Messages.message ("persist.stClosingFailed"), ex); } } } - if (identity == null) { - throw new PersistenceException(Messages.format( - "persist.keyGenOverflow", getClass().getName())); - } - return identity; } + /** + * Get query string to retrive next primary key value. + * + * @param table The table name. + * @param column The primary key column name. + * @return The query string to retrive next primary key value. + * @throws QueryException The query cannot be constructed for this engine. + */ + private String getQueryExpression(final String table, final String column) + throws QueryException { + /* + * Pre 1.2.1 release of Castor we used a different query for MySQL that + * does not create a database lock. The only reason for doing so is, that + * MySQL has not supported such locks at some point. According to my tests + * this is not the case any more so I decided to comment out this special + * handling for MySQL as using locks may prevent us from getting + * DuplikateIdentityExceptions. (20.05.2008/RJ) + * + * // Create SQL sentence of the form + * // "SELECT MAX(pk) FROM table" + * // without lock + * QueryExpression query = _factory.getQueryExpression(); + * query.addSelect("MAX(" + _factory.quoteName(column) + ")"); + * query.addTable(table); + * return query.getStatement(false); + */ + // Create SQL sentence of the form + // "SELECT pk FROM table WHERE pk=(SELECT MAX(t1.pk) FROM table t1)" + // with database-dependent keyword for lock + QueryExpression query = _factory.getQueryExpression(); + query.addColumn(table, column); + query.addCondition(table, column, QueryExpression.OP_EQUALS, + "(SELECT MAX(t1." + _factory.quoteName(column) + ") FROM " + + _factory.quoteName(table) + " t1)"); + return query.getStatement(true); + } + + /** + * {@inheritDoc} + */ + public void supportsSqlType(final int sqlType) throws MappingException { + if ((sqlType != Types.INTEGER) + && (sqlType != Types.BIGINT) + && (sqlType != Types.NUMERIC) + && (sqlType != Types.DECIMAL)) { + throw new MappingException(Messages.format( + "mapping.keyGenSQLType", getClass().getName(), new Integer(sqlType))); + } + } + /** - * Style of key generator: BEFORE_INSERT, DURING_INSERT or AFTER_INSERT ? + * {@inheritDoc} */ public byte getStyle() { return BEFORE_INSERT; @@ -201,10 +208,15 @@ return BEFORE_INSERT; } + /** + * {@inheritDoc} + */ + public boolean isInSameConnection() { + return true; + } /** - * Gives a possibility to patch the Castor-generated SQL statement - * for INSERT (makes sense for DURING_INSERT key generators). + * {@inheritDoc} */ public String patchSQL(final String insert, final String primKeyName) { return insert; @@ -210,13 +222,7 @@ return insert; } - - /** - * Is key generated in the same connection as INSERT? - */ - public boolean isInSameConnection() { - return true; - } + //----------------------------------------------------------------------------------- } Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/MaxKeyGeneratorFactory.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/MaxKeyGeneratorFactory.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/MaxKeyGeneratorFactory.java (working copy) @@ -1,46 +1,17 @@ -/** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. +/* + * Copyright 2008 Oleg Nitz, Ralf Joachim * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Intalio, Inc. For written permission, - * please contact info@exolab.org. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Intalio, Inc. Exolab is a registered - * trademark of Intalio, Inc. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). - * - * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * INTALIO, INC. OR ITS 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. - * - * Copyright 1999 (C) Intalio, Inc. All Rights Reserved. - * - * $Id$ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.castor.cpa.persistence.sql.keygen; @@ -52,38 +23,35 @@ import org.exolab.castor.persist.spi.PersistenceFactory; /** - * MAX key generator factory. - * The short name of this key generator is "MAX". - * It uses the following alrorithm: the maximum value of the primary - * key is fetched and the correspondent record is locked until the end - * of transaction, generator returns (max + 1). - * The lock guarantees that key generators of concurrent transactions - * will not use this key value, so DuplicateKeyException is impossible. - * If the table is empty, generator returns 1, no lock is put, + * MAX key generator factory. The short name of this key generator is "MAX". + *
+ * It uses the following alrorithm: the maximum value of the primary key is + * fetched and the correspondent record is locked until the end of transaction, + * generator returns (max + 1). The lock guarantees that key generators of + * concurrent transactions will not use this key value, so DuplicateKeyException + * is impossible. If the table is empty, generator returns 1, no lock is put, * DuplicateKeyException is possible. * - * @author Oleg Nitz - * @version $Revision$ $Date: 2005-06-01 06:08:22 -0600 (Wed, 01 Jun 2005) $ * @see MaxKeyGenerator + * @author Oleg Nitz + * @author Ralf Joachim + * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $ */ public final class MaxKeyGeneratorFactory implements KeyGeneratorFactory { + //----------------------------------------------------------------------------------- + /** - * Produce the key generator. - * - * @param factory Helper object for obtaining database-specific QuerySyntax. - * @param params Parameters for key generator. + * {@inheritDoc} */ - public KeyGenerator getKeyGenerator(final PersistenceFactory factory, - final Properties params, final int sqlType) throws MappingException { - - return new MaxKeyGenerator(factory, sqlType); - } + public String getName() { return "MAX"; } /** - * The short name of this key generator is "MAX". + * {@inheritDoc} */ - public String getName() { - return "MAX"; + public KeyGenerator getKeyGenerator(final PersistenceFactory factory, + final Properties params, final int sqlType) throws MappingException { + return new MaxKeyGenerator(factory, sqlType); } -} + //----------------------------------------------------------------------------------- +} \ No newline at end of file Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/SequenceKeyGenerator.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/SequenceKeyGenerator.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/SequenceKeyGenerator.java (working copy) @@ -79,7 +79,7 @@ private class DefaultType extends SequenceKeyGenValueHandler { protected Object getValue(final Connection conn, final String tableName, final String primKeyName, final Properties props) throws Exception { - if (_style == BEFORE_INSERT) { + if (getStyle() == BEFORE_INSERT) { return getValue("SELECT nextval('" + getSeqName(tableName, primKeyName) + "')", conn); } else if (_triggerPresent && _factoryName.equals("postgresql")) { Object insStmt = props.get("insertStatement"); @@ -135,7 +135,7 @@ final int sqlType) throws MappingException { checkSupportedFactory(factory); supportsSqlType(sqlType); - initIdentityValue(sqlType); + initSqlTypeHandler(sqlType); initType(); boolean returning = "true".equals(params.getProperty("returning")); @@ -150,7 +150,7 @@ _seqName = params.getProperty("sequence", "{0}_seq"); - _style = (_factoryName.equals(PostgreSQLFactory.FACTORY_NAME) + setStyle(_factoryName.equals(PostgreSQLFactory.FACTORY_NAME) || _factoryName.equals(InterbaseFactory.FACTORY_NAME) || _factoryName.equals(DB2Factory.FACTORY_NAME) ? BEFORE_INSERT : (returning ? DURING_INSERT : AFTER_INSERT)); @@ -155,9 +155,9 @@ || _factoryName.equals(DB2Factory.FACTORY_NAME) ? BEFORE_INSERT : (returning ? DURING_INSERT : AFTER_INSERT)); if (_triggerPresent && !returning) { - _style = AFTER_INSERT; + setStyle(AFTER_INSERT); } - if (_triggerPresent && _style == BEFORE_INSERT) { + if (_triggerPresent && (getStyle() == BEFORE_INSERT)) { throw new MappingException(Messages.format("mapping.keyGenParamNotCompat", "trigger=\"true\"", getClass().getName(), _factoryName)); } @@ -205,7 +205,7 @@ _type = new DefaultType(); } _type.setGenerator(this); - _type.setIdentityValue(_identityValue); + _type.setSqlTypeHandler(getSqlTypeHandler()); } /** @@ -240,7 +240,7 @@ int lp1; // the first left parenthesis, which starts fields list int lp2; // the second left parenthesis, which starts values list - if (_style == BEFORE_INSERT) { + if (getStyle() == BEFORE_INSERT) { return insert; } @@ -301,7 +301,7 @@ sb.insert(lp1 + 1, _factory.quoteName(primKeyName) + ","); } } - if (_style == DURING_INSERT) { + if (getStyle() == DURING_INSERT) { // append 'RETURNING primKeyName INTO ?' sb.append(" RETURNING "); sb.append(_factory.quoteName(primKeyName)); Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/SequenceKeyGeneratorFactory.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/SequenceKeyGeneratorFactory.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/SequenceKeyGeneratorFactory.java (working copy) @@ -1,46 +1,17 @@ -/** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Intalio, Inc. For written permission, - * please contact info@exolab.org. +/* + * Copyright 2008 Oleg Nitz, Ralf Joachim * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Intalio, Inc. Exolab is a registered - * trademark of Intalio, Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * INTALIO, INC. OR ITS 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. - * - * Copyright 1999 (C) Intalio, Inc. All Rights Reserved. - * - * $Id$ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.castor.cpa.persistence.sql.keygen; @@ -52,38 +23,39 @@ import org.exolab.castor.persist.spi.PersistenceFactory; /** - * SEQUENCE key generator factory. - * The short name of this key generator is "SEQUENCE". - * It uses Oracle/PostrgeSQL SEQUENCEs - * There are two optional parameters for this key generator: - * 1) name is "sequence" and the default value is "{0}_seq"; - * 2) name is "returning", values: "true"/"false", default is "false". - * The latter parameter should be used only with Oracle8i, "true" value - * turns on more efficient RETURNING syntax. - * It is possible to use naming patterns like this for obtaining - * SEQUENCE name by table name. This gives the possibility to use - * one global key generator declaration rather than one per table. + * SEQUENCE key generator factory. The short name of this key generator is "SEQUENCE". + *
+ * It uses Oracle/PostrgeSQL SEQUENCEs. There are two optional parameters for this + * key generator: + * + * The latter parameter should be used only with Oracle8i, "true" value turns on more + * efficient RETURNING syntax. It is possible to use naming patterns like this to + * obtain SEQUENCE name by table name. This gives the possibility to use one global + * key generator declaration rather than one per table. * - * @author Oleg Nitz - * @version $Revision$ $Date: 2005-06-01 06:08:22 -0600 (Wed, 01 Jun 2005) $ * @see SequenceKeyGenerator + * @author Oleg Nitz + * @author Ralf Joachim + * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $ */ public final class SequenceKeyGeneratorFactory implements KeyGeneratorFactory { + //----------------------------------------------------------------------------------- + /** - * Produce the key generator. - * - * @param factory Helper object for obtaining database-specific QuerySyntax. - * @param params Parameters for key generator. + * {@inheritDoc} */ - public KeyGenerator getKeyGenerator(final PersistenceFactory factory, final Properties params, - final int sqlType) throws MappingException { - return new SequenceKeyGenerator(factory, params, sqlType); - } + public String getName() { return "SEQUENCE"; } /** - * The short name of this key generator is "SEQUENCE". + * {@inheritDoc} */ - public String getName() { - return "SEQUENCE"; + public KeyGenerator getKeyGenerator(final PersistenceFactory factory, + final Properties params, final int sqlType) throws MappingException { + return new SequenceKeyGenerator(factory, params, sqlType); } + + //----------------------------------------------------------------------------------- } Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/SqlTypeHandler.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/SqlTypeHandler.java (revision 0) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/SqlTypeHandler.java (revision 0) @@ -0,0 +1,8 @@ +package org.castor.cpa.persistence.sql.keygen; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public interface SqlTypeHandler { + Object getValue(ResultSet rs) throws SQLException; +} Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/UUIDKeyGenerator.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/UUIDKeyGenerator.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/UUIDKeyGenerator.java (working copy) @@ -1,68 +1,18 @@ -/** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. +/* + * Copyright 2008 Thomas Fach, Ralf Joachim * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Intalio, Inc. For written permission, - * please contact info@exolab.org. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Intalio, Inc. Exolab is a registered - * trademark of Intalio, Inc. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). - * - * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * INTALIO, INC. OR ITS 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. - * - * Copyright 1999 (C) Intalio, Inc. All Rights Reserved. - * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - -/** - * ---------------------------------------------- - * UUIDKeyGenerator - * ---------------------------------------------- - * Developed by Publica Data-Service GmbH - * - * Team "New Projects" are: - * Joerg Sailer, Martin Goettler, Andreas Grimme, - * Thorsten Prix, Norbert Fuss, Thomas Fach - * - * Address: - * Publica Data-Service GmbH - * Steinerne Furt 72 - * 86167 Augsburg - * Germany - * - * Internet: http://www.publica.de - * - * "live long and in prosper" - * ---------------------------------------------- -**/ package org.castor.cpa.persistence.sql.keygen; import java.net.InetAddress; @@ -80,17 +30,29 @@ /** * UUID key generator. - * @author Thomas Fach - * @author Bruce Snyder - * @version $Revision$ $Date: 2006-04-10 16:39:24 -0600 (Mon, 10 Apr 2006) $ + * * @see UUIDKeyGeneratorFactory + * @author Thomas Fach + * @author Bruce Snyder + * @author Ralf Joachim + * @version $Revision$ $Date: 2006-04-10 16:39:24 -0600 (Mon, 10 Apr 2006) $ */ public final class UUIDKeyGenerator implements KeyGenerator { - private DecimalFormat _df = new DecimalFormat(); + //----------------------------------------------------------------------------------- + + private static final DecimalFormat IP_FORMAT = new DecimalFormat("000"); + + private static final DecimalFormat TIME_FORMAT = new DecimalFormat("0000000000000"); - private String _sHost = null; + private static final DecimalFormat COUNTER_FORMAT = new DecimalFormat("00000"); + + private static final long COUNTER_MAX = 99999; - private static long _staticCounter = 0; + private static long _staticCounter = 0; + + private String _hostAddress; + + //----------------------------------------------------------------------------------- /** * Initialize the UUID key generator. @@ -98,81 +60,64 @@ public UUIDKeyGenerator(final PersistenceFactory factory, final int sqlType) throws MappingException { supportsSqlType(sqlType); + initHostAddress(); + } - /** - * Determine if the key generator supports a given sql type. - * - * @param sqlType - * @throws MappingException - */ - public void supportsSqlType(final int sqlType) throws MappingException { - if (sqlType != Types.CHAR && sqlType != Types.VARCHAR && sqlType != Types.LONGVARCHAR) { - throw new MappingException(Messages.format("mapping.keyGenSQLType", - getClass().getName(), new Integer(sqlType))); + private void initHostAddress() throws MappingException { + try { + String host = InetAddress.getLocalHost().getHostAddress(); + + StringBuffer sb = new StringBuffer(); + StringTokenizer st = new StringTokenizer(host, "."); + while (st.hasMoreTokens()) { + sb.append(IP_FORMAT.format(new Integer(st.nextToken()))); + } + + _hostAddress = sb.toString(); + } catch (Exception ex) { + throw new MappingException(Messages.format( + "persist.keyGenSQL", getClass().getName(), ex.toString()), ex); } } + + //----------------------------------------------------------------------------------- /** - * Generate a new unique key for the specified table. - * - * @param conn An open connection within the given transaction - * @param tableName The table name - * @param primKeyName The primary key name - * @param props A temporary replacement for Principal object - * @return A new key - * @throws PersistenceException An error occured talking to persistent - * storage + * {@inheritDoc} */ public Object generateKey(final Connection conn, final String tableName, final String primKeyName, final Properties props) throws PersistenceException { - String sUUID = null; + StringBuffer sb = new StringBuffer(); + + // getting IP (fixed length: 12 character) + sb.append(_hostAddress); + + // getting currentTimeMillis (fixed length: 13 character) + sb.append(TIME_FORMAT.format(System.currentTimeMillis())); + + // getting static counter (fixed length: 5 character) + if (_staticCounter >= COUNTER_MAX) { _staticCounter = 0; } + _staticCounter++; + sb.append(COUNTER_FORMAT.format(_staticCounter)); - try { - // getting IP (fixed length: 12 character) - if (_sHost == null) { - _sHost = InetAddress.getLocalHost().getHostAddress(); - } - - StringTokenizer st = new StringTokenizer(_sHost, "."); - _df.applyPattern("000"); - while (st.hasMoreTokens()) { - if (sUUID == null) { - sUUID = _df.format(new Integer(st.nextToken())); - } else { - sUUID += _df.format(new Integer(st.nextToken())); - } - } - - // getting currentTimeMillis (fixed length: 13 character) - _df.applyPattern("0000000000000"); - sUUID += _df.format(System.currentTimeMillis()); + return sb.toString(); + } - // getting static counter (fixed length: 15 character) - if (_staticCounter >= 99999) { - // 99999 generated keys in one timer interval? no... - _staticCounter = 0; - } - - _staticCounter++; - _df.applyPattern("00000"); - sUUID += _df.format(_staticCounter); - } catch (Exception ex) { - throw new PersistenceException(Messages.format( - "persist.keyGenSQL", getClass().getName(), ex.toString()), ex); - } - - if (sUUID == null) { - throw new PersistenceException(Messages.format( - "persist.keyGenOverflow", getClass().getName())); + /** + * {@inheritDoc} + */ + public void supportsSqlType(final int sqlType) throws MappingException { + if ((sqlType != Types.CHAR) + && (sqlType != Types.VARCHAR) + && (sqlType != Types.LONGVARCHAR)) { + throw new MappingException(Messages.format("mapping.keyGenSQLType", + getClass().getName(), new Integer(sqlType))); } - - return sUUID; } - /** - * Style of key generator: BEFORE_INSERT, DURING_INSERT or AFTER_INSERT ? + * {@inheritDoc} */ public byte getStyle() { return BEFORE_INSERT; @@ -178,21 +123,19 @@ return BEFORE_INSERT; } - /** - * Gives a possibility to patch the Castor-generated SQL statement - * for INSERT (makes sense for DURING_INSERT key generators). + * {@inheritDoc} */ - public String patchSQL(final String insert, final String primKeyName) { - return insert; + public boolean isInSameConnection() { + return true; } - /** - * Is key generated in the same connection as INSERT? + * {@inheritDoc} */ - public boolean isInSameConnection() { - return true; + public String patchSQL(final String insert, final String primKeyName) { + return insert; } + //----------------------------------------------------------------------------------- } \ No newline at end of file Index: /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/UUIDKeyGeneratorFactory.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/UUIDKeyGeneratorFactory.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/castor/cpa/persistence/sql/keygen/UUIDKeyGeneratorFactory.java (working copy) @@ -1,68 +1,18 @@ -/** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Intalio, Inc. For written permission, - * please contact info@exolab.org. - * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Intalio, Inc. Exolab is a registered - * trademark of Intalio, Inc. - * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). +/* + * Copyright 2008 Thomas Fach, Ralf Joachim * - * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * INTALIO, INC. OR ITS 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * Copyright 1999 (C) Intalio, Inc. All Rights Reserved. + * http://www.apache.org/licenses/LICENSE-2.0 * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - -/** - * ---------------------------------------------- - * UUIDKeyGenerator - * ---------------------------------------------- - * Developed by Publica Data-Service GmbH - * - * Team "New Projects" are: - * Joerg Sailer, Martin Goettler, Andreas Grimme, - * Thorsten Prix, Norbert Fuss, Thomas Fach - * - * Address: - * Publica Data-Service GmbH - * Steinerne Furt 72 - * 86167 Augsburg - * Germany - * - * Internet: http://www.publica.de - * - * "live long and in prosper" - * ---------------------------------------------- -**/ package org.castor.cpa.persistence.sql.keygen; import java.util.Properties; @@ -73,28 +23,32 @@ import org.exolab.castor.persist.spi.PersistenceFactory; /** - * UUID key generator factory. - * The short name of this key generator is "UUID". - * It uses the following alrorithm: - * The uuid is a combination of the IP address, the current - * time in milliseconds since 1970 and a static counter. - * The complete key consists of a 30 character fixed length string. - * Brief statement: - * The ip only exists once during runtime of castor, the - * current time in milliseconds (updated every 55 mills) is - * in combination to the ip pretty unique. considering a static - * counter will be used a database-wide unique key will be created. + * UUID key generator factory. The short name of this key generator is "UUID". + *
+ * It uses the following alrorithm: The uuid is a combination of the IP address, + * the current time in milliseconds since 1970 and a static counter. The complete + * key consists of a 30 character fixed length string. + *
+ * Brief statement: The IP only exists once during runtime of castor, the current + * time in milliseconds (updated every 55 mills) is in combination to the IP + * pretty unique. Considering a static counter will be used a database-wide unique + * key will be created. * - * @author Thomas Fach - * @version $Revision$ $Date: 2005-06-01 06:08:22 -0600 (Wed, 01 Jun 2005) $ * @see UUIDKeyGenerator + * @author Thomas Fach + * @author Ralf Joachim + * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $ */ public final class UUIDKeyGeneratorFactory implements KeyGeneratorFactory { + //----------------------------------------------------------------------------------- + + /** + * {@inheritDoc} + */ + public String getName() { return "UUID"; } + /** - * Produce the key generator. - * - * @param factory Helper object for obtaining database-specific QuerySyntax. - * @param params Parameters for key generator. + * {@inheritDoc} */ public KeyGenerator getKeyGenerator(final PersistenceFactory factory, final Properties params, final int sqlType) throws MappingException { @@ -101,11 +55,5 @@ return new UUIDKeyGenerator(factory, sqlType); } - /** - * The short name of this key generator is "UUID". - */ - public String getName() { - return "UUID"; - } -} - + //----------------------------------------------------------------------------------- +} \ No newline at end of file Index: /home/ralf/castor/keygen/cpa/src/main/java/org/exolab/castor/persist/spi/KeyGenerator.java =================================================================== --- /home/ralf/castor/keygen/cpa/src/main/java/org/exolab/castor/persist/spi/KeyGenerator.java (revision 7714) +++ /home/ralf/castor/keygen/cpa/src/main/java/org/exolab/castor/persist/spi/KeyGenerator.java (working copy) @@ -111,6 +111,12 @@ byte getStyle(); /** + * Is key generated in the same connection as INSERT? + * For DURING_INSERT style this method is never called. + */ + boolean isInSameConnection(); + + /** * Gives a possibility to patch the Castor-generated SQL statement * for INSERT (indended mainly for DURING_INSERT style of key generators, * other key generators usually simply return the passed parameter). @@ -134,10 +140,4 @@ * @param primKeyName The primary key name */ String patchSQL(String insert, String primKeyName) throws MappingException; - - /** - * Is key generated in the same connection as INSERT? - * For DURING_INSERT style this method is never called. - */ - boolean isInSameConnection(); } Index: /home/ralf/castor/keygen/src/doc/release-notes.xml =================================================================== --- /home/ralf/castor/keygen/src/doc/release-notes.xml (revision 7714) +++ /home/ralf/castor/keygen/src/doc/release-notes.xml (working copy) @@ -100,6 +100,26 @@ org.castor.cpa.persistence.sql.keygen.UUIDKeyGeneratorFactory + + + Prepared KeyGenerator to taken over role of SQLSatementCreate (Part 1) + + + Ralf Joachim + ralf.joachim@syscon.eu + + + Ralf Joachim + ralf.joachim@syscon.eu + + + Ralf Joachim + ralf.joachim@syscon.eu + + Enh. + JDO + 20080714 + Added a JDO-specific nature for 1:1 relations.