~ubuntu-branches/ubuntu/wily/libhibernate3-java/wily-proposed

« back to all changes in this revision

Viewing changes to src/org/hibernate/persister/collection/OneToManyPersister.java

  • Committer: Bazaar Package Importer
  • Author(s): Torsten Werner
  • Date: 2007-10-14 14:43:34 UTC
  • Revision ID: james.westby@ubuntu.com-20071014144334-eamc8i0q10gs1aro
Tags: upstream-3.2.5
ImportĀ upstreamĀ versionĀ 3.2.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//$Id: OneToManyPersister.java 10040 2006-06-22 19:51:43Z steve.ebersole@jboss.com $
 
2
package org.hibernate.persister.collection;
 
3
 
 
4
import java.io.Serializable;
 
5
import java.sql.PreparedStatement;
 
6
import java.sql.SQLException;
 
7
import java.util.Iterator;
 
8
 
 
9
import org.hibernate.HibernateException;
 
10
import org.hibernate.MappingException;
 
11
import org.hibernate.jdbc.Expectation;
 
12
import org.hibernate.jdbc.Expectations;
 
13
import org.hibernate.cache.CacheConcurrencyStrategy;
 
14
import org.hibernate.cache.CacheException;
 
15
import org.hibernate.cfg.Configuration;
 
16
import org.hibernate.collection.PersistentCollection;
 
17
import org.hibernate.engine.SessionFactoryImplementor;
 
18
import org.hibernate.engine.SessionImplementor;
 
19
import org.hibernate.engine.SubselectFetch;
 
20
import org.hibernate.exception.JDBCExceptionHelper;
 
21
import org.hibernate.loader.collection.BatchingCollectionInitializer;
 
22
import org.hibernate.loader.collection.CollectionInitializer;
 
23
import org.hibernate.loader.collection.SubselectOneToManyLoader;
 
24
import org.hibernate.loader.entity.CollectionElementLoader;
 
25
import org.hibernate.mapping.Collection;
 
26
import org.hibernate.persister.entity.Joinable;
 
27
import org.hibernate.persister.entity.OuterJoinLoadable;
 
28
import org.hibernate.pretty.MessageHelper;
 
29
import org.hibernate.sql.Update;
 
30
import org.hibernate.util.ArrayHelper;
 
31
 
 
32
/**
 
33
 * Collection persister for one-to-many associations.
 
34
 *
 
35
 * @author Gavin King
 
36
 */
 
37
public class OneToManyPersister extends AbstractCollectionPersister {
 
38
 
 
39
        private final boolean cascadeDeleteEnabled;
 
40
        private final boolean keyIsNullable;
 
41
        private final boolean keyIsUpdateable;
 
42
 
 
43
        protected boolean isRowDeleteEnabled() {
 
44
                return keyIsUpdateable && keyIsNullable;
 
45
        }
 
46
 
 
47
        protected boolean isRowInsertEnabled() {
 
48
                return keyIsUpdateable;
 
49
        }
 
50
 
 
51
        public boolean isCascadeDeleteEnabled() {
 
52
                return cascadeDeleteEnabled;
 
53
        }
 
54
 
 
55
        public OneToManyPersister(Collection collection,
 
56
                                                          CacheConcurrencyStrategy cache,
 
57
                                                          Configuration cfg,
 
58
                                                          SessionFactoryImplementor factory)
 
59
                        throws MappingException, CacheException {
 
60
                super( collection, cache, cfg, factory );
 
61
                cascadeDeleteEnabled = collection.getKey().isCascadeDeleteEnabled() &&
 
62
                                factory.getDialect().supportsCascadeDelete();
 
63
                keyIsNullable = collection.getKey().isNullable();
 
64
                keyIsUpdateable = collection.getKey().isUpdateable();
 
65
        }
 
66
 
 
67
        /**
 
68
         * Generate the SQL UPDATE that updates all the foreign keys to null
 
69
         */
 
70
        protected String generateDeleteString() {
 
71
                
 
72
                Update update = new Update( getDialect() )
 
73
                                .setTableName( qualifiedTableName )
 
74
                                .addColumns( keyColumnNames, "null" )
 
75
                                .setPrimaryKeyColumnNames( keyColumnNames );
 
76
                
 
77
                if ( hasIndex && !indexContainsFormula ) update.addColumns( indexColumnNames, "null" );
 
78
                
 
79
                if ( hasWhere ) update.setWhere( sqlWhereString );
 
80
                
 
81
                if ( getFactory().getSettings().isCommentsEnabled() ) {
 
82
                        update.setComment( "delete one-to-many " + getRole() );
 
83
                }
 
84
                
 
85
                return update.toStatementString();
 
86
        }
 
87
 
 
88
        /**
 
89
         * Generate the SQL UPDATE that updates a foreign key to a value
 
90
         */
 
91
        protected String generateInsertRowString() {
 
92
                
 
93
                Update update = new Update( getDialect() )
 
94
                                .setTableName( qualifiedTableName )
 
95
                                .addColumns( keyColumnNames );
 
96
                
 
97
                if ( hasIndex && !indexContainsFormula ) update.addColumns( indexColumnNames );
 
98
                
 
99
                //identifier collections not supported for 1-to-many
 
100
                if ( getFactory().getSettings().isCommentsEnabled() ) {
 
101
                        update.setComment( "create one-to-many row " + getRole() );
 
102
                }
 
103
                
 
104
                return update.setPrimaryKeyColumnNames( elementColumnNames )
 
105
                                .toStatementString();
 
106
        }
 
107
 
 
108
        /**
 
109
         * Not needed for one-to-many association
 
110
         */
 
111
        protected String generateUpdateRowString() {
 
112
                return null;
 
113
        }
 
114
 
 
115
        /**
 
116
         * Generate the SQL UPDATE that updates a particular row's foreign
 
117
         * key to null
 
118
         */
 
119
        protected String generateDeleteRowString() {
 
120
                
 
121
                Update update = new Update( getDialect() )
 
122
                                .setTableName( qualifiedTableName )
 
123
                                .addColumns( keyColumnNames, "null" );
 
124
                
 
125
                if ( hasIndex && !indexContainsFormula ) update.addColumns( indexColumnNames, "null" );
 
126
                
 
127
                if ( getFactory().getSettings().isCommentsEnabled() ) {
 
128
                        update.setComment( "delete one-to-many row " + getRole() );
 
129
                }
 
130
                
 
131
                //use a combination of foreign key columns and pk columns, since
 
132
                //the ordering of removal and addition is not guaranteed when
 
133
                //a child moves from one parent to another
 
134
                String[] rowSelectColumnNames = ArrayHelper.join(keyColumnNames, elementColumnNames);
 
135
                return update.setPrimaryKeyColumnNames( rowSelectColumnNames )
 
136
                                .toStatementString();
 
137
        }
 
138
 
 
139
        public boolean consumesEntityAlias() {
 
140
                return true;
 
141
        }
 
142
        public boolean consumesCollectionAlias() {
 
143
                return true;
 
144
        }
 
145
 
 
146
        public boolean isOneToMany() {
 
147
                return true;
 
148
        }
 
149
 
 
150
        public boolean isManyToMany() {
 
151
                return false;
 
152
        }
 
153
 
 
154
        protected int doUpdateRows(Serializable id, PersistentCollection collection, SessionImplementor session)
 
155
                        throws HibernateException {
 
156
 
 
157
                // we finish all the "removes" first to take care of possible unique
 
158
                // constraints and so that we can take better advantage of batching
 
159
                
 
160
                try {
 
161
                        int count = 0;
 
162
                        if ( isRowDeleteEnabled() ) {
 
163
                                boolean useBatch = true;
 
164
                                PreparedStatement st = null;
 
165
                                // update removed rows fks to null
 
166
                                try {
 
167
                                        int i = 0;
 
168
        
 
169
                                        Iterator entries = collection.entries( this );
 
170
                                        int offset = 1;
 
171
                                        Expectation expectation = Expectations.NONE;
 
172
                                        while ( entries.hasNext() ) {
 
173
        
 
174
                                                Object entry = entries.next();
 
175
                                                if ( collection.needsUpdating( entry, i, elementType ) ) {  // will still be issued when it used to be null
 
176
                                                        if ( st == null ) {
 
177
                                                                String sql = getSQLDeleteRowString();
 
178
                                                                if ( isDeleteCallable() ) {
 
179
                                                                        expectation = Expectations.appropriateExpectation( getDeleteCheckStyle() );
 
180
                                                                        useBatch = expectation.canBeBatched();
 
181
                                                                        st = useBatch
 
182
                                                                                        ? session.getBatcher().prepareBatchCallableStatement( sql )
 
183
                                                                            : session.getBatcher().prepareCallableStatement( sql );
 
184
                                                                        offset += expectation.prepare( st );
 
185
                                                                }
 
186
                                                                else {
 
187
                                                                        st = session.getBatcher().prepareBatchStatement( getSQLDeleteRowString() );
 
188
                                                                }
 
189
                                                        }
 
190
                                                        int loc = writeKey( st, id, offset, session );
 
191
                                                        writeElementToWhere( st, collection.getSnapshotElement(entry, i), loc, session );
 
192
                                                        if ( useBatch ) {
 
193
                                                                session.getBatcher().addToBatch( expectation );
 
194
                                                        }
 
195
                                                        else {
 
196
                                                                expectation.verifyOutcome( st.executeUpdate(), st, -1 );
 
197
                                                        }
 
198
                                                        count++;
 
199
                                                }
 
200
                                                i++;
 
201
                                        }
 
202
                                }
 
203
                                catch ( SQLException sqle ) {
 
204
                                        if ( useBatch ) {
 
205
                                                session.getBatcher().abortBatch( sqle );
 
206
                                        }
 
207
                                        throw sqle;
 
208
                                }
 
209
                                finally {
 
210
                                        if ( !useBatch ) {
 
211
                                                session.getBatcher().closeStatement( st );
 
212
                                        }
 
213
                                }
 
214
                        }
 
215
                        
 
216
                        if ( isRowInsertEnabled() ) {
 
217
                                Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );
 
218
                                boolean callable = isInsertCallable();
 
219
                                boolean useBatch = expectation.canBeBatched();
 
220
                                String sql = getSQLInsertRowString();
 
221
                                PreparedStatement st = null;
 
222
                                // now update all changed or added rows fks
 
223
                                try {
 
224
                                        int i = 0;
 
225
                                        Iterator entries = collection.entries( this );
 
226
                                        while ( entries.hasNext() ) {
 
227
                                                Object entry = entries.next();
 
228
                                                int offset = 1;
 
229
                                                if ( collection.needsUpdating( entry, i, elementType ) ) {
 
230
                                                        if ( useBatch ) {
 
231
                                                                if ( st == null ) {
 
232
                                                                        if ( callable ) {
 
233
                                                                                st = session.getBatcher().prepareBatchCallableStatement( sql );
 
234
                                                                        }
 
235
                                                                        else {
 
236
                                                                                st = session.getBatcher().prepareBatchStatement( sql );
 
237
                                                                        }
 
238
                                                                }
 
239
                                                        }
 
240
                                                        else {
 
241
                                                                if ( callable ) {
 
242
                                                                        st = session.getBatcher().prepareCallableStatement( sql );
 
243
                                                                }
 
244
                                                                else {
 
245
                                                                        st = session.getBatcher().prepareStatement( sql );
 
246
                                                                }
 
247
                                                        }
 
248
 
 
249
                                                        offset += expectation.prepare( st );
 
250
 
 
251
                                                        int loc = writeKey( st, id, offset, session );
 
252
                                                        if ( hasIndex && !indexContainsFormula ) {
 
253
                                                                loc = writeIndexToWhere( st, collection.getIndex( entry, i, this ), loc, session );
 
254
                                                        }
 
255
 
 
256
                                                        writeElementToWhere( st, collection.getElement( entry ), loc, session );
 
257
 
 
258
                                                        if ( useBatch ) {
 
259
                                                                session.getBatcher().addToBatch( expectation );
 
260
                                                        }
 
261
                                                        else {
 
262
                                                                expectation.verifyOutcome( st.executeUpdate(), st, -1 );
 
263
                                                        }
 
264
                                                        count++;
 
265
                                                }
 
266
                                                i++;
 
267
                                        }
 
268
                                }
 
269
                                catch ( SQLException sqle ) {
 
270
                                        if ( useBatch ) {
 
271
                                                session.getBatcher().abortBatch( sqle );
 
272
                                        }
 
273
                                        throw sqle;
 
274
                                }
 
275
                                finally {
 
276
                                        if ( !useBatch ) {
 
277
                                                session.getBatcher().closeStatement( st );
 
278
                                        }
 
279
                                }
 
280
                        }
 
281
 
 
282
                        return count;
 
283
                }
 
284
                catch ( SQLException sqle ) {
 
285
                        throw JDBCExceptionHelper.convert(
 
286
                                        getSQLExceptionConverter(),
 
287
                                        sqle,
 
288
                                        "could not update collection rows: " + 
 
289
                                        MessageHelper.collectionInfoString( this, id, getFactory() ),
 
290
                                        getSQLInsertRowString()
 
291
                        );
 
292
                }
 
293
        }
 
294
 
 
295
        public String selectFragment(
 
296
                Joinable rhs,
 
297
                String rhsAlias,
 
298
                String lhsAlias,
 
299
                String entitySuffix,
 
300
                String collectionSuffix,
 
301
                boolean includeCollectionColumns) {
 
302
                StringBuffer buf = new StringBuffer();
 
303
                if ( includeCollectionColumns ) {
 
304
//                      buf.append( selectFragment( lhsAlias, "" ) )//ignore suffix for collection columns!
 
305
                        buf.append( selectFragment( lhsAlias, collectionSuffix ) )
 
306
                                        .append( ", " );
 
307
                }
 
308
                OuterJoinLoadable ojl = ( OuterJoinLoadable ) getElementPersister();
 
309
                return buf.append( ojl.selectFragment( lhsAlias, entitySuffix ) )//use suffix for the entity columns
 
310
                                .toString();
 
311
        }
 
312
 
 
313
        /**
 
314
         * Create the <tt>OneToManyLoader</tt>
 
315
         *
 
316
         * @see org.hibernate.loader.collection.OneToManyLoader
 
317
         */
 
318
        protected CollectionInitializer createCollectionInitializer(java.util.Map enabledFilters) throws MappingException {
 
319
                return BatchingCollectionInitializer.createBatchingOneToManyInitializer( this, batchSize, getFactory(), enabledFilters );
 
320
        }
 
321
 
 
322
        public String fromJoinFragment(String alias,
 
323
                                                                   boolean innerJoin,
 
324
                                                                   boolean includeSubclasses) {
 
325
                return ( ( Joinable ) getElementPersister() ).fromJoinFragment( alias, innerJoin, includeSubclasses );
 
326
        }
 
327
 
 
328
        public String whereJoinFragment(String alias,
 
329
                                                                        boolean innerJoin,
 
330
                                                                        boolean includeSubclasses) {
 
331
                return ( ( Joinable ) getElementPersister() ).whereJoinFragment( alias, innerJoin, includeSubclasses );
 
332
        }
 
333
 
 
334
        public String getTableName() {
 
335
                return ( ( Joinable ) getElementPersister() ).getTableName();
 
336
        }
 
337
 
 
338
        public String filterFragment(String alias) throws MappingException {
 
339
                String result = super.filterFragment( alias );
 
340
                if ( getElementPersister() instanceof Joinable ) {
 
341
                        result += ( ( Joinable ) getElementPersister() ).oneToManyFilterFragment( alias );
 
342
                }
 
343
                return result;
 
344
 
 
345
        }
 
346
 
 
347
        protected CollectionInitializer createSubselectInitializer(SubselectFetch subselect, SessionImplementor session) {
 
348
                return new SubselectOneToManyLoader( 
 
349
                                this,
 
350
                                subselect.toSubselectString( getCollectionType().getLHSPropertyName() ),
 
351
                                subselect.getResult(),
 
352
                                subselect.getQueryParameters(),
 
353
                                subselect.getNamedParameterLocMap(),
 
354
                                session.getFactory(),
 
355
                                session.getEnabledFilters() 
 
356
                        );
 
357
        }
 
358
 
 
359
        public Object getElementByIndex(Serializable key, Object index, SessionImplementor session, Object owner) {
 
360
                return new CollectionElementLoader( this, getFactory(), session.getEnabledFilters() )
 
361
                                .loadElement( session, key, incrementIndexByBase(index) );
 
362
        }
 
363
 
 
364
}