KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > id > MultipleHiLoPerTableGenerator


1 //$Id: MultipleHiLoPerTableGenerator.java,v 1.12 2005/07/04 02:40:29 oneovthafew Exp $
2
package org.hibernate.id;
3
4 import java.io.Serializable JavaDoc;
5 import java.sql.Connection JavaDoc;
6 import java.sql.PreparedStatement JavaDoc;
7 import java.sql.ResultSet JavaDoc;
8 import java.sql.SQLException JavaDoc;
9 import java.sql.Types JavaDoc;
10 import java.util.Properties JavaDoc;
11
12 import org.apache.commons.logging.Log;
13 import org.apache.commons.logging.LogFactory;
14 import org.hibernate.HibernateException;
15 import org.hibernate.LockMode;
16 import org.hibernate.MappingException;
17 import org.hibernate.dialect.Dialect;
18 import org.hibernate.engine.SessionImplementor;
19 import org.hibernate.engine.TransactionHelper;
20 import org.hibernate.mapping.Table;
21 import org.hibernate.type.Type;
22 import org.hibernate.util.PropertiesHelper;
23
24 /**
25  *
26  * A hilo <tt>IdentifierGenerator</tt> that returns a <tt>Long</tt>, constructed using
27  * a hi/lo algorithm. The hi value MUST be fetched in a seperate transaction
28  * to the <tt>Session</tt> transaction so the generator must be able to obtain
29  * a new connection and commit it. Hence this implementation may not
30  * be used when the user is supplying connections. In this
31  * case a <tt>SequenceHiLoGenerator</tt> would be a better choice (where
32  * supported).<br>
33  * <br>
34  *
35  * A hilo <tt>IdentifierGenerator</tt> that uses a database
36  * table to store the last generated values. A table can contains
37  * several hi values. They are distinct from each other through a key
38  * <p/>
39  * <p>This implementation is not compliant with a user connection</p>
40  * <p/>
41  *
42  * <p>Allowed parameters:</p>
43  * <ul>
44  * <li>table: table name (default <tt>hibernate_sequences</tt>)</li>
45  * <li>primary_key_column: key column name (default <tt>sequence_name</tt>)</li>
46  * <li>value_column: hi value column name(default <tt>sequence_next_hi_value</tt>)</li>
47  * <li>primary_key_value: key value for the current entity (default to table name)</li>
48  * <li>primary_key_length: length of the varchar column in DB (default to 255)</li>
49  * </ul>
50  *
51  * @author Emmanuel Bernard
52  * @author <a HREF="mailto:kr@hbt.de">Klaus Richarz</a>.
53  */

54 public class MultipleHiLoPerTableGenerator
55     extends TransactionHelper
56     implements PersistentIdentifierGenerator, Configurable {
57     
58     private static final Log log = LogFactory.getLog(MultipleHiLoPerTableGenerator.class);
59     
60     public static final String JavaDoc ID_TABLE = "table";
61     public static final String JavaDoc PK_COLUMN_NAME = "primary_key_column";
62     public static final String JavaDoc PK_VALUE_NAME = "primary_key_value";
63     public static final String JavaDoc VALUE_COLUMN_NAME = "value_column";
64     public static final String JavaDoc PK_LENGTH_NAME = "primary_key_length";
65     
66     private static final int DEFAULT_PK_LENGTH = 255;
67     public static final String JavaDoc DEFAULT_TABLE = "hibernate_sequences";
68     private static final String JavaDoc DEFAULT_PK_COLUMN = "sequence_name";
69     private static final String JavaDoc DEFAULT_VALUE_COLUMN = "sequence_next_hi_value";
70     
71     private String JavaDoc tableName;
72     private String JavaDoc pkColumnName;
73     private String JavaDoc valueColumnName;
74     private String JavaDoc query;
75     private String JavaDoc insert;
76     private String JavaDoc update;
77
78     //hilo params
79
public static final String JavaDoc MAX_LO = "max_lo";
80
81     private long hi;
82     private int lo;
83     private int maxLo;
84     private Class JavaDoc returnClass;
85     private int keySize;
86
87
88     public String JavaDoc[] sqlCreateStrings(Dialect dialect) throws HibernateException {
89         return new String JavaDoc[] {
90             new StringBuffer JavaDoc()
91                 .append("create table ")
92                 .append(tableName)
93                 .append(" ( ")
94                 .append(pkColumnName)
95                 .append(" ")
96                 .append( dialect.getTypeName(Types.VARCHAR, keySize, 0, 0) )
97                 .append(", ")
98                 .append(valueColumnName)
99                 .append(" ")
100                 .append( dialect.getTypeName(Types.INTEGER) )
101                 .append(" ) ")
102                 .toString()
103         };
104     }
105
106     public String JavaDoc[] sqlDropStrings(Dialect dialect) throws HibernateException {
107         StringBuffer JavaDoc sqlDropString = new StringBuffer JavaDoc()
108             .append("drop table ");
109         if ( dialect.supportsIfExistsBeforeTableName() ) sqlDropString.append("if exists ");
110         sqlDropString.append(tableName)
111             .append( dialect.getCascadeConstraintsString() );
112         if ( dialect.supportsIfExistsAfterTableName() ) sqlDropString.append(" if exists");
113         return new String JavaDoc[] { sqlDropString.toString() };
114     }
115
116     public Object JavaDoc generatorKey() {
117         return tableName;
118     }
119
120     public Serializable JavaDoc doWorkInCurrentTransaction(Connection JavaDoc conn, String JavaDoc sql) throws SQLException JavaDoc {
121         int result;
122         int rows;
123         do {
124             // The loop ensures atomicity of the
125
// select + update even for no transaction
126
// or read committed isolation level
127

128             //sql = query;
129
SQL.debug(query);
130             PreparedStatement JavaDoc qps = conn.prepareStatement(query);
131             PreparedStatement JavaDoc ips = null;
132             try {
133                 //qps.setString(1, key);
134
ResultSet JavaDoc rs = qps.executeQuery();
135                 boolean isInitialized = rs.next();
136                 if ( !isInitialized ) {
137                     result = 0;
138                     ips = conn.prepareStatement(insert);
139                     //ips.setString(1, key);
140
ips.setInt(1, result);
141                     ips.execute();
142                 }
143                 else {
144                     result = rs.getInt(1);
145                 }
146                 rs.close();
147             }
148             catch (SQLException JavaDoc sqle) {
149                 log.error("could not read or init a hi value", sqle);
150                 throw sqle;
151             }
152             finally {
153                 if (ips != null) {
154                     ips.close();
155                 }
156                 qps.close();
157             }
158
159             //sql = update;
160
PreparedStatement JavaDoc ups = conn.prepareStatement(update);
161             try {
162                 ups.setInt( 1, result + 1 );
163                 ups.setInt( 2, result );
164                 //ups.setString( 3, key );
165
rows = ups.executeUpdate();
166             }
167             catch (SQLException JavaDoc sqle) {
168                 log.error("could not update hi value in: " + tableName, sqle);
169                 throw sqle;
170             }
171             finally {
172                 ups.close();
173             }
174         }
175         while (rows==0);
176         return new Integer JavaDoc(result);
177     }
178
179     public synchronized Serializable JavaDoc generate(SessionImplementor session, Object JavaDoc obj)
180         throws HibernateException {
181         if (lo>maxLo) {
182             int hival = ( (Integer JavaDoc) doWorkInNewTransaction(session) ).intValue();
183             lo = (hival == 0) ? 1 : 0;
184             hi = hival * (maxLo+1);
185             log.debug("new hi value: " + hival);
186         }
187
188         return IdentifierGeneratorFactory.createNumber( hi + lo++, returnClass );
189     }
190
191     public void configure(Type type, Properties JavaDoc params, Dialect dialect) throws MappingException {
192         this.tableName = PropertiesHelper.getString(ID_TABLE, params, DEFAULT_TABLE);
193         this.pkColumnName = PropertiesHelper.getString(PK_COLUMN_NAME, params, DEFAULT_PK_COLUMN);
194         this.valueColumnName = PropertiesHelper.getString(VALUE_COLUMN_NAME, params, DEFAULT_VALUE_COLUMN);
195         String JavaDoc schemaName = params.getProperty(SCHEMA);
196         String JavaDoc catalogName = params.getProperty(CATALOG);
197         this.keySize = PropertiesHelper.getInt(PK_LENGTH_NAME, params, DEFAULT_PK_LENGTH);
198         String JavaDoc keyValue = PropertiesHelper.getString(PK_VALUE_NAME, params, params.getProperty(TABLE) );
199
200         if ( tableName.indexOf( dialect.getSchemaSeparator() )<0 ) {
201             tableName = Table.qualify( catalogName, schemaName, tableName, dialect.getSchemaSeparator() );
202         }
203
204         query = "select " +
205             valueColumnName +
206             " from " +
207             dialect.appendLockHint(LockMode.UPGRADE, tableName) +
208             " where " + pkColumnName + " = '" + keyValue + "'" +
209             dialect.getForUpdateString();
210
211         update = "update " +
212             tableName +
213             " set " +
214             valueColumnName +
215             " = ? where " +
216             valueColumnName +
217             " = ? and " +
218             pkColumnName +
219             " = '" +
220             keyValue
221             + "'";
222         
223         insert = "insert into " + tableName +
224             "(" + pkColumnName + ", " + valueColumnName + ") " +
225             "values('"+ keyValue +"', ?)";
226
227
228         //hilo config
229
maxLo = PropertiesHelper.getInt(MAX_LO, params, Short.MAX_VALUE);
230         lo = maxLo + 1; // so we "clock over" on the first invocation
231
returnClass = type.getReturnedClass();
232     }
233 }
234
Popular Tags