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.action;
26
27 import java.io.Serializable;
28
29 import org.hibernate.AssertionFailure;
30 import org.hibernate.HibernateException;
31 import org.hibernate.cache.CacheException;
32 import org.hibernate.cache.CacheKey;
33 import org.hibernate.cache.access.SoftLock;
34 import org.hibernate.cache.entry.CacheEntry;
35 import org.hibernate.engine.EntityEntry;
36 import org.hibernate.engine.SessionFactoryImplementor;
37 import org.hibernate.engine.SessionImplementor;
38 import org.hibernate.engine.Status;
39 import org.hibernate.engine.Versioning;
40 import org.hibernate.event.PostUpdateEvent;
41 import org.hibernate.event.PostUpdateEventListener;
42 import org.hibernate.event.PreUpdateEvent;
43 import org.hibernate.event.PreUpdateEventListener;
44 import org.hibernate.event.EventSource;
45 import org.hibernate.persister.entity.EntityPersister;
46 import org.hibernate.type.TypeFactory;
47
48 public final class EntityUpdateAction extends EntityAction {
49
50 private final Object[] state;
51 private final Object[] previousState;
52 private final Object previousVersion;
53 private Object nextVersion;
54 private final int[] dirtyFields;
55 private final boolean hasDirtyCollection;
56 private final Object rowId;
57 private Object cacheEntry;
58 private SoftLock lock;
59
60 public EntityUpdateAction(
61 final Serializable id,
62 final Object[] state,
63 final int[] dirtyProperties,
64 final boolean hasDirtyCollection,
65 final Object[] previousState,
66 final Object previousVersion,
67 final Object nextVersion,
68 final Object instance,
69 final Object rowId,
70 final EntityPersister persister,
71 final SessionImplementor session) throws HibernateException {
72 super( session, id, instance, persister );
73 this.state = state;
74 this.previousState = previousState;
75 this.previousVersion = previousVersion;
76 this.nextVersion = nextVersion;
77 this.dirtyFields = dirtyProperties;
78 this.hasDirtyCollection = hasDirtyCollection;
79 this.rowId = rowId;
80 }
81
82 public void execute() throws HibernateException {
83 Serializable id = getId();
84 EntityPersister persister = getPersister();
85 SessionImplementor session = getSession();
86 Object instance = getInstance();
87
88 boolean veto = preUpdate();
89
90 final SessionFactoryImplementor factory = getSession().getFactory();
91 Object previousVersion = this.previousVersion;
92 if ( persister.isVersionPropertyGenerated() ) {
93 // we need to grab the version value from the entity, otherwise
94 // we have issues with generated-version entities that may have
95 // multiple actions queued during the same flush
96 previousVersion = persister.getVersion( instance, session.getEntityMode() );
97 }
98
99 final CacheKey ck;
100 if ( persister.hasCache() ) {
101 ck = new CacheKey(
102 id,
103 persister.getIdentifierType(),
104 persister.getRootEntityName(),
105 session.getEntityMode(),
106 session.getFactory()
107 );
108 lock = persister.getCacheAccessStrategy().lockItem( ck, previousVersion );
109 }
110 else {
111 ck = null;
112 }
113
114 if ( !veto ) {
115 persister.update(
116 id,
117 state,
118 dirtyFields,
119 hasDirtyCollection,
120 previousState,
121 previousVersion,
122 instance,
123 rowId,
124 session
125 );
126 }
127
128 EntityEntry entry = getSession().getPersistenceContext().getEntry( instance );
129 if ( entry == null ) {
130 throw new AssertionFailure( "possible nonthreadsafe access to session" );
131 }
132
133 if ( entry.getStatus()==Status.MANAGED || persister.isVersionPropertyGenerated() ) {
134 // get the updated snapshot of the entity state by cloning current state;
135 // it is safe to copy in place, since by this time no-one else (should have)
136 // has a reference to the array
137 TypeFactory.deepCopy(
138 state,
139 persister.getPropertyTypes(),
140 persister.getPropertyCheckability(),
141 state,
142 session
143 );
144 if ( persister.hasUpdateGeneratedProperties() ) {
145 // this entity defines proeprty generation, so process those generated
146 // values...
147 persister.processUpdateGeneratedProperties( id, instance, state, session );
148 if ( persister.isVersionPropertyGenerated() ) {
149 nextVersion = Versioning.getVersion( state, persister );
150 }
151 }
152 // have the entity entry perform post-update processing, passing it the
153 // update state and the new version (if one).
154 entry.postUpdate( instance, state, nextVersion );
155 }
156
157 if ( persister.hasCache() ) {
158 if ( persister.isCacheInvalidationRequired() || entry.getStatus()!=Status.MANAGED ) {
159 persister.getCacheAccessStrategy().remove( ck );
160 }
161 else {
162 //TODO: inefficient if that cache is just going to ignore the updated state!
163 CacheEntry ce = new CacheEntry(
164 state,
165 persister,
166 persister.hasUninitializedLazyProperties( instance, session.getEntityMode() ),
167 nextVersion,
168 getSession(),
169 instance
170 );
171 cacheEntry = persister.getCacheEntryStructure().structure( ce );
172 boolean put = persister.getCacheAccessStrategy().update( ck, cacheEntry, nextVersion, previousVersion );
173 if ( put && factory.getStatistics().isStatisticsEnabled() ) {
174 factory.getStatisticsImplementor().secondLevelCachePut( getPersister().getCacheAccessStrategy().getRegion().getName() );
175 }
176 }
177 }
178
179 postUpdate();
180
181 if ( factory.getStatistics().isStatisticsEnabled() && !veto ) {
182 factory.getStatisticsImplementor()
183 .updateEntity( getPersister().getEntityName() );
184 }
185 }
186
187 private void postUpdate() {
188 PostUpdateEventListener[] postListeners = getSession().getListeners()
189 .getPostUpdateEventListeners();
190 if (postListeners.length>0) {
191 PostUpdateEvent postEvent = new PostUpdateEvent(
192 getInstance(),
193 getId(),
194 state,
195 previousState,
196 getPersister(),
197 (EventSource) getSession()
198 );
199 for ( int i = 0; i < postListeners.length; i++ ) {
200 postListeners[i].onPostUpdate(postEvent);
201 }
202 }
203 }
204
205 private void postCommitUpdate() {
206 PostUpdateEventListener[] postListeners = getSession().getListeners()
207 .getPostCommitUpdateEventListeners();
208 if (postListeners.length>0) {
209 PostUpdateEvent postEvent = new PostUpdateEvent(
210 getInstance(),
211 getId(),
212 state,
213 previousState,
214 getPersister(),
215 (EventSource) getSession()
216 );
217 for ( int i = 0; i < postListeners.length; i++ ) {
218 postListeners[i].onPostUpdate(postEvent);
219 }
220 }
221 }
222
223 private boolean preUpdate() {
224 PreUpdateEventListener[] preListeners = getSession().getListeners()
225 .getPreUpdateEventListeners();
226 boolean veto = false;
227 if (preListeners.length>0) {
228 PreUpdateEvent preEvent = new PreUpdateEvent(
229 getInstance(),
230 getId(),
231 state,
232 previousState,
233 getPersister(),
234 (EventSource)getSession()
235 );
236 for ( int i = 0; i < preListeners.length; i++ ) {
237 veto = preListeners[i].onPreUpdate(preEvent) || veto;
238 }
239 }
240 return veto;
241 }
242
243 public void afterTransactionCompletion(boolean success) throws CacheException {
244 EntityPersister persister = getPersister();
245 if ( persister.hasCache() ) {
246
247 final CacheKey ck = new CacheKey(
248 getId(),
249 persister.getIdentifierType(),
250 persister.getRootEntityName(),
251 getSession().getEntityMode(),
252 getSession().getFactory()
253 );
254
255 if ( success && cacheEntry!=null /*!persister.isCacheInvalidationRequired()*/ ) {
256 boolean put = persister.getCacheAccessStrategy().afterUpdate( ck, cacheEntry, nextVersion, previousVersion, lock );
257
258 if ( put && getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
259 getSession().getFactory().getStatisticsImplementor().secondLevelCachePut( getPersister().getCacheAccessStrategy().getRegion().getName() );
260 }
261 }
262 else {
263 persister.getCacheAccessStrategy().unlockItem( ck, lock );
264 }
265 }
266 postCommitUpdate();
267 }
268
269 protected boolean hasPostCommitEventListeners() {
270 return getSession().getListeners().getPostCommitUpdateEventListeners().length>0;
271 }
272
273 }
274
275
276
277
278
279
280