-
Notifications
You must be signed in to change notification settings - Fork 3.5k
/
DefaultPersistEventListener.java
193 lines (179 loc) · 7.41 KB
/
DefaultPersistEventListener.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.event.internal;
import org.hibernate.HibernateException;
import org.hibernate.ObjectDeletedException;
import org.hibernate.PersistentObjectException;
import org.hibernate.engine.spi.CascadingAction;
import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.Status;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.PersistContext;
import org.hibernate.event.spi.PersistEvent;
import org.hibernate.event.spi.PersistEventListener;
import org.hibernate.id.ForeignGenerator;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.jpa.event.spi.CallbackRegistryConsumer;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import static org.hibernate.event.internal.EntityState.getEntityState;
/**
* Defines the default create event listener used by hibernate for creating
* transient entities in response to generated create events.
*
* @author Gavin King
*/
public class DefaultPersistEventListener
extends AbstractSaveEventListener<PersistContext>
implements PersistEventListener, CallbackRegistryConsumer {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( DefaultPersistEventListener.class );
@Override
protected CascadingAction<PersistContext> getCascadeAction() {
return CascadingActions.PERSIST;
}
/**
* Handle the given create event.
*
* @param event The create event to be handled.
*
*/
public void onPersist(PersistEvent event) throws HibernateException {
onPersist( event, PersistContext.create() );
}
/**
* Handle the given create event.
*
* @param event The create event to be handled.
*
*/
public void onPersist(PersistEvent event, PersistContext createCache) throws HibernateException {
final Object object = event.getObject();
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( object );
if ( lazyInitializer != null ) {
if ( lazyInitializer.isUninitialized() ) {
if ( lazyInitializer.getSession() != event.getSession() ) {
throw new PersistentObjectException( "uninitialized proxy passed to persist()" );
}
}
else {
persist( event, createCache, lazyInitializer.getImplementation() );
}
}
else {
persist( event, createCache, object );
}
}
private void persist(PersistEvent event, PersistContext createCache, Object entity) {
final EventSource source = event.getSession();
final EntityEntry entityEntry = source.getPersistenceContextInternal().getEntry( entity );
final String entityName = entityName( event, entity, entityEntry );
switch ( entityState( event, entity, entityName, entityEntry ) ) {
case DETACHED:
throw new PersistentObjectException( "detached entity passed to persist: "
+ EventUtil.getLoggableName( event.getEntityName(), entity) );
case PERSISTENT:
entityIsPersistent( event, createCache );
break;
case TRANSIENT:
entityIsTransient( event, createCache );
break;
case DELETED:
entityEntry.setStatus( Status.MANAGED );
entityEntry.setDeletedState( null );
source.getActionQueue().unScheduleDeletion( entityEntry, event.getObject() );
entityIsDeleted( event, createCache );
break;
default:
throw new ObjectDeletedException(
"deleted entity passed to persist",
null,
EventUtil.getLoggableName( event.getEntityName(), entity )
);
}
}
private static EntityState entityState(PersistEvent event, Object entity, String entityName, EntityEntry entityEntry) {
final EventSource source = event.getSession();
EntityState entityState = getEntityState( entity, entityName, entityEntry, source, true );
if ( entityState == EntityState.DETACHED ) {
// JPA 2, in its version of a "foreign generated", allows the id attribute value
// to be manually set by the user, even though this manual value is irrelevant.
// The issue is that this causes problems with the Hibernate unsaved-value strategy
// which comes into play here in determining detached/transient state.
//
// Detect if we have this situation and if so null out the id value and calculate the
// entity state again.
// NOTE: entityEntry must be null to get here, so we cannot use any of its values
final EntityPersister persister = event.getFactory().getMappingMetamodel()
.getEntityDescriptor( entityName );
if ( persister.getGenerator() instanceof ForeignGenerator ) {
if ( LOG.isDebugEnabled() && persister.getIdentifier( entity, source ) != null ) {
LOG.debug( "Resetting entity id attribute to null for foreign generator" );
}
persister.setIdentifier( entity, null, source );
entityState = getEntityState( entity, entityName, entityEntry, source, true );
}
}
return entityState;
}
private static String entityName(PersistEvent event, Object entity, EntityEntry entityEntry) {
if ( event.getEntityName() != null ) {
return event.getEntityName();
}
else {
// changes event.entityName by side effect!
final String entityName = event.getSession().bestGuessEntityName( entity, entityEntry );
event.setEntityName( entityName );
return entityName;
}
}
protected void entityIsPersistent(PersistEvent event, PersistContext createCache) {
LOG.trace( "Ignoring persistent instance" );
final EventSource source = event.getSession();
//TODO: check that entry.getIdentifier().equals(requestedId)
final Object entity = source.getPersistenceContextInternal().unproxy( event.getObject() );
if ( createCache.add( entity ) ) {
justCascade( createCache, source, entity, source.getEntityPersister( event.getEntityName(), entity ) );
}
}
private void justCascade(PersistContext createCache, EventSource source, Object entity, EntityPersister persister) {
//TODO: merge into one method!
cascadeBeforeSave( source, persister, entity, createCache );
cascadeAfterSave( source, persister, entity, createCache );
}
/**
* Handle the given create event.
*
* @param event The save event to be handled.
* @param createCache The copy cache of entity instance to merge/copy instance.
*/
protected void entityIsTransient(PersistEvent event, PersistContext createCache) {
LOG.trace( "Saving transient instance" );
final EventSource source = event.getSession();
final Object entity = source.getPersistenceContextInternal().unproxy( event.getObject() );
if ( createCache.add( entity ) ) {
saveWithGeneratedId( entity, event.getEntityName(), createCache, source, false );
}
}
private void entityIsDeleted(PersistEvent event, PersistContext createCache) {
final EventSource source = event.getSession();
final Object entity = source.getPersistenceContextInternal().unproxy( event.getObject() );
final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
if ( LOG.isTraceEnabled() ) {
LOG.tracef(
"un-scheduling entity deletion [%s]",
MessageHelper.infoString( persister, persister.getIdentifier( entity, source ), event.getFactory() )
);
}
if ( createCache.add( entity ) ) {
justCascade( createCache, source, entity, persister );
}
}
}