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