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.tuple.entity;
26
27 import java.io.Serializable;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.Set;
31
32 import org.hibernate.EntityMode;
33 import org.hibernate.HibernateException;
34 import org.hibernate.MappingException;
35 import org.hibernate.tuple.Instantiator;
36 import org.hibernate.tuple.VersionProperty;
37 import org.hibernate.tuple.StandardProperty;
38 import org.hibernate.engine.SessionFactoryImplementor;
39 import org.hibernate.engine.SessionImplementor;
40 import org.hibernate.id.Assigned;
41 import org.hibernate.intercept.LazyPropertyInitializer;
42 import org.hibernate.mapping.Component;
43 import org.hibernate.mapping.PersistentClass;
44 import org.hibernate.mapping.Property;
45 import org.hibernate.property.Getter;
46 import org.hibernate.property.Setter;
47 import org.hibernate.proxy.ProxyFactory;
48 import org.hibernate.type.AbstractComponentType;
49 import org.hibernate.type.ComponentType;
50
51
52 /**
53 * Support for tuplizers relating to entities.
54 *
55 * @author Steve Ebersole
56 * @author Gavin King
57 */
58 public abstract class AbstractEntityTuplizer implements EntityTuplizer {
59
60 //TODO: currently keeps Getters and Setters (instead of PropertyAccessors) because of the way getGetter() and getSetter() are implemented currently; yuck!
61
62 private final EntityMetamodel entityMetamodel;
63
64 private final Getter idGetter;
65 private final Setter idSetter;
66
67 protected final Getter[] getters;
68 protected final Setter[] setters;
69 protected final int propertySpan;
70 protected final boolean hasCustomAccessors;
71 private final Instantiator instantiator;
72 private final ProxyFactory proxyFactory;
73 private final AbstractComponentType identifierMapperType;
74
75
76 /**
77 * Return the entity-mode handled by this tuplizer instance.
78 *
79 * @return The entity-mode
80 */
81 protected abstract EntityMode getEntityMode();
82
83 /**
84 * Build an appropriate Getter for the given property.
85 *
86 * @param mappedProperty The property to be accessed via the built Getter.
87 * @param mappedEntity The entity information regarding the mapped entity owning this property.
88 * @return An appropriate Getter instance.
89 */
90 protected abstract Getter buildPropertyGetter(Property mappedProperty, PersistentClass mappedEntity);
91
92 /**
93 * Build an appropriate Setter for the given property.
94 *
95 * @param mappedProperty The property to be accessed via the built Setter.
96 * @param mappedEntity The entity information regarding the mapped entity owning this property.
97 * @return An appropriate Setter instance.
98 */
99 protected abstract Setter buildPropertySetter(Property mappedProperty, PersistentClass mappedEntity);
100
101 /**
102 * Build an appropriate Instantiator for the given mapped entity.
103 *
104 * @param mappingInfo The mapping information regarding the mapped entity.
105 * @return An appropriate Instantiator instance.
106 */
107 protected abstract Instantiator buildInstantiator(PersistentClass mappingInfo);
108
109 /**
110 * Build an appropriate ProxyFactory for the given mapped entity.
111 *
112 * @param mappingInfo The mapping information regarding the mapped entity.
113 * @param idGetter The constructed Getter relating to the entity's id property.
114 * @param idSetter The constructed Setter relating to the entity's id property.
115 * @return An appropriate ProxyFactory instance.
116 */
117 protected abstract ProxyFactory buildProxyFactory(PersistentClass mappingInfo, Getter idGetter, Setter idSetter);
118
119 /**
120 * Constructs a new AbstractEntityTuplizer instance.
121 *
122 * @param entityMetamodel The "interpreted" information relating to the mapped entity.
123 * @param mappingInfo The parsed "raw" mapping data relating to the given entity.
124 */
125 public AbstractEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappingInfo) {
126 this.entityMetamodel = entityMetamodel;
127
128 if ( !entityMetamodel.getIdentifierProperty().isVirtual() ) {
129 idGetter = buildPropertyGetter( mappingInfo.getIdentifierProperty(), mappingInfo );
130 idSetter = buildPropertySetter( mappingInfo.getIdentifierProperty(), mappingInfo );
131 }
132 else {
133 idGetter = null;
134 idSetter = null;
135 }
136
137 propertySpan = entityMetamodel.getPropertySpan();
138
139 getters = new Getter[propertySpan];
140 setters = new Setter[propertySpan];
141
142 Iterator iter = mappingInfo.getPropertyClosureIterator();
143 boolean foundCustomAccessor=false;
144 int i=0;
145 while ( iter.hasNext() ) {
146 //TODO: redesign how PropertyAccessors are acquired...
147 Property property = (Property) iter.next();
148 getters[i] = buildPropertyGetter(property, mappingInfo);
149 setters[i] = buildPropertySetter(property, mappingInfo);
150 if ( !property.isBasicPropertyAccessor() ) foundCustomAccessor = true;
151 i++;
152 }
153 hasCustomAccessors = foundCustomAccessor;
154
155 instantiator = buildInstantiator( mappingInfo );
156
157 if ( entityMetamodel.isLazy() ) {
158 proxyFactory = buildProxyFactory( mappingInfo, idGetter, idSetter );
159 if (proxyFactory == null) {
160 entityMetamodel.setLazy( false );
161 }
162 }
163 else {
164 proxyFactory = null;
165 }
166
167 Component mapper = mappingInfo.getIdentifierMapper();
168 identifierMapperType = mapper==null ? null : (AbstractComponentType) mapper.getType();
169 }
170
171 /** Retreives the defined entity-name for the tuplized entity.
172 *
173 * @return The entity-name.
174 */
175 protected String getEntityName() {
176 return entityMetamodel.getName();
177 }
178
179 /**
180 * Retreives the defined entity-names for any subclasses defined for this
181 * entity.
182 *
183 * @return Any subclass entity-names.
184 */
185 protected Set getSubclassEntityNames() {
186 return entityMetamodel.getSubclassEntityNames();
187 }
188
189 public Serializable getIdentifier(Object entity) throws HibernateException {
190 final Object id;
191 if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
192 id = entity;
193 }
194 else {
195 if ( idGetter == null ) {
196 if (identifierMapperType==null) {
197 throw new HibernateException( "The class has no identifier property: " + getEntityName() );
198 }
199 else {
200 ComponentType copier = (ComponentType) entityMetamodel.getIdentifierProperty().getType();
201 id = copier.instantiate( getEntityMode() );
202 copier.setPropertyValues( id, identifierMapperType.getPropertyValues( entity, getEntityMode() ), getEntityMode() );
203 }
204 }
205 else {
206 id = idGetter.get( entity );
207 }
208 }
209
210 try {
211 return (Serializable) id;
212 }
213 catch ( ClassCastException cce ) {
214 StringBuffer msg = new StringBuffer( "Identifier classes must be serializable. " );
215 if ( id != null ) {
216 msg.append( id.getClass().getName() + " is not serializable. " );
217 }
218 if ( cce.getMessage() != null ) {
219 msg.append( cce.getMessage() );
220 }
221 throw new ClassCastException( msg.toString() );
222 }
223 }
224
225
226 public void setIdentifier(Object entity, Serializable id) throws HibernateException {
227 if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
228 if ( entity != id ) {
229 AbstractComponentType copier = (AbstractComponentType) entityMetamodel.getIdentifierProperty().getType();
230 copier.setPropertyValues( entity, copier.getPropertyValues( id, getEntityMode() ), getEntityMode() );
231 }
232 }
233 else if ( idSetter != null ) {
234 idSetter.set( entity, id, getFactory() );
235 }
236 }
237
238 public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion) {
239 if ( entityMetamodel.getIdentifierProperty().getIdentifierGenerator() instanceof Assigned ) {
240 //return currentId;
241 }
242 else {
243 //reset the id
244 Serializable result = entityMetamodel.getIdentifierProperty()
245 .getUnsavedValue()
246 .getDefaultValue( currentId );
247 setIdentifier( entity, result );
248 //reset the version
249 VersionProperty versionProperty = entityMetamodel.getVersionProperty();
250 if ( entityMetamodel.isVersioned() ) {
251 setPropertyValue(
252 entity,
253 entityMetamodel.getVersionPropertyIndex(),
254 versionProperty.getUnsavedValue().getDefaultValue( currentVersion )
255 );
256 }
257 //return the id, so we can use it to reset the proxy id
258 //return result;
259 }
260 }
261
262 public Object getVersion(Object entity) throws HibernateException {
263 if ( !entityMetamodel.isVersioned() ) return null;
264 return getters[ entityMetamodel.getVersionPropertyIndex() ].get( entity );
265 }
266
267 protected boolean shouldGetAllProperties(Object entity) {
268 return !hasUninitializedLazyProperties( entity );
269 }
270
271 public Object[] getPropertyValues(Object entity) throws HibernateException {
272 boolean getAll = shouldGetAllProperties( entity );
273 final int span = entityMetamodel.getPropertySpan();
274 final Object[] result = new Object[span];
275
276 for ( int j = 0; j < span; j++ ) {
277 StandardProperty property = entityMetamodel.getProperties()[j];
278 if ( getAll || !property.isLazy() ) {
279 result[j] = getters[j].get( entity );
280 }
281 else {
282 result[j] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
283 }
284 }
285 return result;
286 }
287
288 public Object[] getPropertyValuesToInsert(Object entity, Map mergeMap, SessionImplementor session)
289 throws HibernateException {
290 final int span = entityMetamodel.getPropertySpan();
291 final Object[] result = new Object[span];
292
293 for ( int j = 0; j < span; j++ ) {
294 result[j] = getters[j].getForInsert( entity, mergeMap, session );
295 }
296 return result;
297 }
298
299 public Object getPropertyValue(Object entity, int i) throws HibernateException {
300 return getters[i].get( entity );
301 }
302
303 public Object getPropertyValue(Object entity, String propertyPath) throws HibernateException {
304
305 int loc = propertyPath.indexOf('.');
306 String basePropertyName = loc>0 ?
307 propertyPath.substring(0, loc) : propertyPath;
308
309 int index = entityMetamodel.getPropertyIndex( basePropertyName );
310 Object baseValue = getPropertyValue( entity, index );
311 if ( loc>0 ) {
312 ComponentType type = (ComponentType) entityMetamodel.getPropertyTypes()[index];
313 return getComponentValue( type, baseValue, propertyPath.substring(loc+1) );
314 }
315 else {
316 return baseValue;
317 }
318 }
319
320 /**
321 * Extract a component property value.
322 *
323 * @param type The component property types.
324 * @param component The component instance itself.
325 * @param propertyPath The property path for the property to be extracted.
326 * @return The property value extracted.
327 */
328 protected Object getComponentValue(ComponentType type, Object component, String propertyPath) {
329
330 int loc = propertyPath.indexOf('.');
331 String basePropertyName = loc>0 ?
332 propertyPath.substring(0, loc) : propertyPath;
333
334 String[] propertyNames = type.getPropertyNames();
335 int index=0;
336 for ( ; index<propertyNames.length; index++ ) {
337 if ( basePropertyName.equals( propertyNames[index] ) ) break;
338 }
339 if (index==propertyNames.length) {
340 throw new MappingException( "component property not found: " + basePropertyName );
341 }
342
343 Object baseValue = type.getPropertyValue( component, index, getEntityMode() );
344
345 if ( loc>0 ) {
346 ComponentType subtype = (ComponentType) type.getSubtypes()[index];
347 return getComponentValue( subtype, baseValue, propertyPath.substring(loc+1) );
348 }
349 else {
350 return baseValue;
351 }
352
353 }
354
355 public void setPropertyValues(Object entity, Object[] values) throws HibernateException {
356 boolean setAll = !entityMetamodel.hasLazyProperties();
357
358 for ( int j = 0; j < entityMetamodel.getPropertySpan(); j++ ) {
359 if ( setAll || values[j] != LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
360 setters[j].set( entity, values[j], getFactory() );
361 }
362 }
363 }
364
365 public void setPropertyValue(Object entity, int i, Object value) throws HibernateException {
366 setters[i].set( entity, value, getFactory() );
367 }
368
369 public void setPropertyValue(Object entity, String propertyName, Object value) throws HibernateException {
370 setters[ entityMetamodel.getPropertyIndex( propertyName ) ].set( entity, value, getFactory() );
371 }
372
373 public final Object instantiate(Serializable id) throws HibernateException {
374 Object result = getInstantiator().instantiate( id );
375 if ( id != null ) {
376 setIdentifier( result, id );
377 }
378 return result;
379 }
380
381 public final Object instantiate() throws HibernateException {
382 return instantiate( null );
383 }
384
385 public void afterInitialize(Object entity, boolean lazyPropertiesAreUnfetched, SessionImplementor session) {}
386
387 public boolean hasUninitializedLazyProperties(Object entity) {
388 // the default is to simply not lazy fetch properties for now...
389 return false;
390 }
391
392 public final boolean isInstance(Object object) {
393 return getInstantiator().isInstance( object );
394 }
395
396 public boolean hasProxy() {
397 return entityMetamodel.isLazy();
398 }
399
400 public final Object createProxy(Serializable id, SessionImplementor session)
401 throws HibernateException {
402 return getProxyFactory().getProxy( id, session );
403 }
404
405 public boolean isLifecycleImplementor() {
406 return false;
407 }
408
409 public boolean isValidatableImplementor() {
410 return false;
411 }
412
413 protected final EntityMetamodel getEntityMetamodel() {
414 return entityMetamodel;
415 }
416
417 protected final SessionFactoryImplementor getFactory() {
418 return entityMetamodel.getSessionFactory();
419 }
420
421 protected final Instantiator getInstantiator() {
422 return instantiator;
423 }
424
425 protected final ProxyFactory getProxyFactory() {
426 return proxyFactory;
427 }
428
429 public String toString() {
430 return getClass().getName() + '(' + getEntityMetamodel().getName() + ')';
431 }
432
433 }