Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » id » [javadoc | source]
    1   /*
    2    * Hibernate, Relational Persistence for Idiomatic Java
    3    *
    4    * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
    5    * indicated by the @author tags or express copyright attribution
    6    * statements applied by the authors.  All third-party contributions are
    7    * distributed under license by Red Hat Middleware LLC.
    8    *
    9    * This copyrighted material is made available to anyone wishing to use, modify,
   10    * copy, or redistribute it subject to the terms and conditions of the GNU
   11    * Lesser General Public License, as published by the Free Software Foundation.
   12    *
   13    * This program is distributed in the hope that it will be useful,
   14    * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   15    * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   16    * for more details.
   17    *
   18    * You should have received a copy of the GNU Lesser General Public License
   19    * along with this distribution; if not, write to:
   20    * Free Software Foundation, Inc.
   21    * 51 Franklin Street, Fifth Floor
   22    * Boston, MA  02110-1301  USA
   23    *
   24    */
   25   package org.hibernate.id;
   26   
   27   import java.io.Serializable;
   28   import java.sql.Connection;
   29   import java.sql.PreparedStatement;
   30   import java.sql.ResultSet;
   31   import java.sql.SQLException;
   32   import java.sql.Types;
   33   import java.util.Properties;
   34   
   35   import org.slf4j.Logger;
   36   import org.slf4j.LoggerFactory;
   37   import org.hibernate.HibernateException;
   38   import org.hibernate.LockMode;
   39   import org.hibernate.MappingException;
   40   import org.hibernate.jdbc.util.FormatStyle;
   41   import org.hibernate.dialect.Dialect;
   42   import org.hibernate.engine.SessionImplementor;
   43   import org.hibernate.engine.TransactionHelper;
   44   import org.hibernate.mapping.Table;
   45   import org.hibernate.type.Type;
   46   import org.hibernate.util.PropertiesHelper;
   47   
   48   /**
   49    *
   50    * A hilo <tt>IdentifierGenerator</tt> that returns a <tt>Long</tt>, constructed using
   51    * a hi/lo algorithm. The hi value MUST be fetched in a seperate transaction
   52    * to the <tt>Session</tt> transaction so the generator must be able to obtain
   53    * a new connection and commit it. Hence this implementation may not
   54    * be used  when the user is supplying connections. In this
   55    * case a <tt>SequenceHiLoGenerator</tt> would be a better choice (where
   56    * supported).<br>
   57    * <br>
   58    *
   59    * A hilo <tt>IdentifierGenerator</tt> that uses a database
   60    * table to store the last generated values. A table can contains
   61    * several hi values. They are distinct from each other through a key
   62    * <p/>
   63    * <p>This implementation is not compliant with a user connection</p>
   64    * <p/>
   65    * 
   66    * <p>Allowed parameters (all of them are optional):</p>
   67    * <ul>
   68    * <li>table: table name (default <tt>hibernate_sequences</tt>)</li>
   69    * <li>primary_key_column: key column name (default <tt>sequence_name</tt>)</li>
   70    * <li>value_column: hi value column name(default <tt>sequence_next_hi_value</tt>)</li>
   71    * <li>primary_key_value: key value for the current entity (default to the entity's primary table name)</li>
   72    * <li>primary_key_length: length of the key column in DB represented as a varchar (default to 255)</li>
   73    * <li>max_lo: max low value before increasing hi (default to Short.MAX_VALUE)</li>
   74    * </ul>
   75    *
   76    * @author Emmanuel Bernard
   77    * @author <a href="mailto:kr@hbt.de">Klaus Richarz</a>.
   78    */
   79   public class MultipleHiLoPerTableGenerator 
   80   	extends TransactionHelper
   81   	implements PersistentIdentifierGenerator, Configurable {
   82   	
   83   	private static final Logger log = LoggerFactory.getLogger(MultipleHiLoPerTableGenerator.class);
   84   	
   85   	public static final String ID_TABLE = "table";
   86   	public static final String PK_COLUMN_NAME = "primary_key_column";
   87   	public static final String PK_VALUE_NAME = "primary_key_value";
   88   	public static final String VALUE_COLUMN_NAME = "value_column";
   89   	public static final String PK_LENGTH_NAME = "primary_key_length";
   90   
   91   	private static final int DEFAULT_PK_LENGTH = 255;
   92   	public static final String DEFAULT_TABLE = "hibernate_sequences";
   93   	private static final String DEFAULT_PK_COLUMN = "sequence_name";
   94   	private static final String DEFAULT_VALUE_COLUMN = "sequence_next_hi_value";
   95   	
   96   	private String tableName;
   97   	private String pkColumnName;
   98   	private String valueColumnName;
   99   	private String query;
  100   	private String insert;
  101   	private String update;
  102   
  103   	//hilo params
  104   	public static final String MAX_LO = "max_lo";
  105   
  106   	private long hi;
  107   	private int lo;
  108   	private int maxLo;
  109   	private Class returnClass;
  110   	private int keySize;
  111   
  112   
  113   	public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
  114   		return new String[] {
  115   			new StringBuffer( dialect.getCreateTableString() )
  116   					.append( ' ' )
  117   					.append( tableName )
  118   					.append( " ( " )
  119   					.append( pkColumnName )
  120   					.append( ' ' )
  121   					.append( dialect.getTypeName( Types.VARCHAR, keySize, 0, 0 ) )
  122   					.append( ",  " )
  123   					.append( valueColumnName )
  124   					.append( ' ' )
  125   					.append( dialect.getTypeName( Types.INTEGER ) )
  126   					.append( " ) " )
  127   					.toString()
  128   		};
  129   	}
  130   
  131   	public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
  132   		StringBuffer sqlDropString = new StringBuffer( "drop table " );
  133   		if ( dialect.supportsIfExistsBeforeTableName() ) {
  134   			sqlDropString.append( "if exists " );
  135   		}
  136   		sqlDropString.append( tableName ).append( dialect.getCascadeConstraintsString() );
  137   		if ( dialect.supportsIfExistsAfterTableName() ) {
  138   			sqlDropString.append( " if exists" );
  139   		}
  140   		return new String[] { sqlDropString.toString() };
  141   	}
  142   
  143   	public Object generatorKey() {
  144   		return tableName;
  145   	}
  146   
  147   	public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
  148   		int result;
  149   		int rows;
  150   		do {
  151   			// The loop ensures atomicity of the
  152   			// select + update even for no transaction
  153   			// or read committed isolation level
  154   
  155   			//sql = query;
  156   			SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
  157   			PreparedStatement qps = conn.prepareStatement(query);
  158   			PreparedStatement ips = null;
  159   			try {
  160   				//qps.setString(1, key);
  161   				ResultSet rs = qps.executeQuery();
  162   				boolean isInitialized = rs.next();
  163   				if ( !isInitialized ) {
  164   					result = 0;
  165   					ips = conn.prepareStatement(insert);
  166   					//ips.setString(1, key);
  167   					ips.setInt(1, result);
  168   					ips.execute();
  169   				}
  170   				else {
  171   					result = rs.getInt(1);
  172   				}
  173   				rs.close();
  174   			}
  175   			catch (SQLException sqle) {
  176   				log.error("could not read or init a hi value", sqle);
  177   				throw sqle;
  178   			}
  179   			finally {
  180   				if (ips != null) {
  181   					ips.close();
  182   				}
  183   				qps.close();
  184   			}
  185   
  186   			//sql = update;
  187   			PreparedStatement ups = conn.prepareStatement(update);
  188   			try {
  189   				ups.setInt( 1, result + 1 );
  190   				ups.setInt( 2, result );
  191   				//ups.setString( 3, key );
  192   				rows = ups.executeUpdate();
  193   			}
  194   			catch (SQLException sqle) {
  195   				log.error("could not update hi value in: " + tableName, sqle);
  196   				throw sqle;
  197   			}
  198   			finally {
  199   				ups.close();
  200   			}
  201   		}
  202   		while (rows==0);
  203   		return new Integer(result);
  204   	}
  205   
  206   	public synchronized Serializable generate(SessionImplementor session, Object obj)
  207   		throws HibernateException {
  208   		if (maxLo < 1) {
  209   			//keep the behavior consistent even for boundary usages
  210   			int val = ( (Integer) doWorkInNewTransaction(session) ).intValue();
  211   			if (val == 0) val = ( (Integer) doWorkInNewTransaction(session) ).intValue();
  212   			return IdentifierGeneratorFactory.createNumber( val, returnClass );
  213   		}
  214   		if (lo>maxLo) {
  215   			int hival = ( (Integer) doWorkInNewTransaction(session) ).intValue();
  216   			lo = (hival == 0) ? 1 : 0;
  217   			hi = hival * (maxLo+1);
  218   			log.debug("new hi value: " + hival);
  219   		}
  220   		return IdentifierGeneratorFactory.createNumber( hi + lo++, returnClass );
  221   	}
  222   
  223   	public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
  224   		tableName = PropertiesHelper.getString(ID_TABLE, params, DEFAULT_TABLE);
  225   		pkColumnName = PropertiesHelper.getString(PK_COLUMN_NAME, params, DEFAULT_PK_COLUMN);
  226   		valueColumnName = PropertiesHelper.getString(VALUE_COLUMN_NAME, params, DEFAULT_VALUE_COLUMN);
  227   		String schemaName = params.getProperty(SCHEMA);
  228   		String catalogName = params.getProperty(CATALOG);
  229   		keySize = PropertiesHelper.getInt(PK_LENGTH_NAME, params, DEFAULT_PK_LENGTH);
  230   		String keyValue = PropertiesHelper.getString(PK_VALUE_NAME, params, params.getProperty(TABLE) );
  231   
  232   		if ( tableName.indexOf( '.' )<0 ) {
  233   			tableName = Table.qualify( catalogName, schemaName, tableName );
  234   		}
  235   
  236   		query = "select " +
  237   			valueColumnName +
  238   			" from " +
  239   			dialect.appendLockHint(LockMode.UPGRADE, tableName) +
  240   			" where " + pkColumnName + " = '" + keyValue + "'" +
  241   			dialect.getForUpdateString();
  242   
  243   		update = "update " +
  244   			tableName +
  245   			" set " +
  246   			valueColumnName +
  247   			" = ? where " +
  248   			valueColumnName +
  249   			" = ? and " +
  250   			pkColumnName +
  251   			" = '" + 
  252   			keyValue 
  253   			+ "'";
  254   		
  255   		insert = "insert into " + tableName +
  256   			"(" + pkColumnName + ", " +	valueColumnName + ") " +
  257   			"values('"+ keyValue +"', ?)";
  258   
  259   
  260   		//hilo config
  261   		maxLo = PropertiesHelper.getInt(MAX_LO, params, Short.MAX_VALUE);
  262   		lo = maxLo + 1; // so we "clock over" on the first invocation
  263   		returnClass = type.getReturnedClass();
  264   	}
  265   }

Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » id » [javadoc | source]