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.lang.reflect.Method;
28 import java.lang.reflect.Modifier;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.Map;
32 import java.util.Set;
33
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.hibernate.EntityMode;
37 import org.hibernate.HibernateException;
38 import org.hibernate.MappingException;
39 import org.hibernate.EntityNameResolver;
40 import org.hibernate.tuple.Instantiator;
41 import org.hibernate.tuple.PojoInstantiator;
42 import org.hibernate.bytecode.ReflectionOptimizer;
43 import org.hibernate.cfg.Environment;
44 import org.hibernate.classic.Lifecycle;
45 import org.hibernate.classic.Validatable;
46 import org.hibernate.engine.SessionImplementor;
47 import org.hibernate.engine.SessionFactoryImplementor;
48 import org.hibernate.intercept.FieldInterceptor;
49 import org.hibernate.intercept.FieldInterceptionHelper;
50 import org.hibernate.mapping.PersistentClass;
51 import org.hibernate.mapping.Property;
52 import org.hibernate.mapping.Subclass;
53 import org.hibernate.property.Getter;
54 import org.hibernate.property.Setter;
55 import org.hibernate.proxy.HibernateProxy;
56 import org.hibernate.proxy.ProxyFactory;
57 import org.hibernate.type.AbstractComponentType;
58 import org.hibernate.util.ReflectHelper;
59
60 /**
61 * An {@link EntityTuplizer} specific to the pojo entity mode.
62 *
63 * @author Steve Ebersole
64 * @author Gavin King
65 */
66 public class PojoEntityTuplizer extends AbstractEntityTuplizer {
67
68 static final Logger log = LoggerFactory.getLogger( PojoEntityTuplizer.class );
69
70 private final Class mappedClass;
71 private final Class proxyInterface;
72 private final boolean lifecycleImplementor;
73 private final boolean validatableImplementor;
74 private final Set lazyPropertyNames = new HashSet();
75 private final ReflectionOptimizer optimizer;
76
77 public PojoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
78 super( entityMetamodel, mappedEntity );
79 this.mappedClass = mappedEntity.getMappedClass();
80 this.proxyInterface = mappedEntity.getProxyInterface();
81 this.lifecycleImplementor = Lifecycle.class.isAssignableFrom( mappedClass );
82 this.validatableImplementor = Validatable.class.isAssignableFrom( mappedClass );
83
84 Iterator iter = mappedEntity.getPropertyClosureIterator();
85 while ( iter.hasNext() ) {
86 Property property = (Property) iter.next();
87 if ( property.isLazy() ) {
88 lazyPropertyNames.add( property.getName() );
89 }
90 }
91
92 String[] getterNames = new String[propertySpan];
93 String[] setterNames = new String[propertySpan];
94 Class[] propTypes = new Class[propertySpan];
95 for ( int i = 0; i < propertySpan; i++ ) {
96 getterNames[i] = getters[i].getMethodName();
97 setterNames[i] = setters[i].getMethodName();
98 propTypes[i] = getters[i].getReturnType();
99 }
100
101 if ( hasCustomAccessors || !Environment.useReflectionOptimizer() ) {
102 optimizer = null;
103 }
104 else {
105 // todo : YUCK!!!
106 optimizer = Environment.getBytecodeProvider().getReflectionOptimizer( mappedClass, getterNames, setterNames, propTypes );
107 // optimizer = getFactory().getSettings().getBytecodeProvider().getReflectionOptimizer(
108 // mappedClass, getterNames, setterNames, propTypes
109 // );
110 }
111
112 }
113
114 /**
115 * {@inheritDoc}
116 */
117 protected ProxyFactory buildProxyFactory(PersistentClass persistentClass, Getter idGetter, Setter idSetter) {
118 // determine the id getter and setter methods from the proxy interface (if any)
119 // determine all interfaces needed by the resulting proxy
120 HashSet proxyInterfaces = new HashSet();
121 proxyInterfaces.add( HibernateProxy.class );
122
123 Class mappedClass = persistentClass.getMappedClass();
124 Class proxyInterface = persistentClass.getProxyInterface();
125
126 if ( proxyInterface!=null && !mappedClass.equals( proxyInterface ) ) {
127 if ( !proxyInterface.isInterface() ) {
128 throw new MappingException(
129 "proxy must be either an interface, or the class itself: " +
130 getEntityName()
131 );
132 }
133 proxyInterfaces.add( proxyInterface );
134 }
135
136 if ( mappedClass.isInterface() ) {
137 proxyInterfaces.add( mappedClass );
138 }
139
140 Iterator iter = persistentClass.getSubclassIterator();
141 while ( iter.hasNext() ) {
142 Subclass subclass = ( Subclass ) iter.next();
143 Class subclassProxy = subclass.getProxyInterface();
144 Class subclassClass = subclass.getMappedClass();
145 if ( subclassProxy!=null && !subclassClass.equals( subclassProxy ) ) {
146 if ( !proxyInterface.isInterface() ) {
147 throw new MappingException(
148 "proxy must be either an interface, or the class itself: " +
149 subclass.getEntityName()
150 );
151 }
152 proxyInterfaces.add( subclassProxy );
153 }
154 }
155
156 Iterator properties = persistentClass.getPropertyIterator();
157 Class clazz = persistentClass.getMappedClass();
158 while ( properties.hasNext() ) {
159 Property property = (Property) properties.next();
160 Method method = property.getGetter(clazz).getMethod();
161 if ( method != null && Modifier.isFinal( method.getModifiers() ) ) {
162 log.error(
163 "Getters of lazy classes cannot be final: " + persistentClass.getEntityName() +
164 "." + property.getName()
165 );
166 }
167 method = property.getSetter(clazz).getMethod();
168 if ( method != null && Modifier.isFinal( method.getModifiers() ) ) {
169 log.error(
170 "Setters of lazy classes cannot be final: " + persistentClass.getEntityName() +
171 "." + property.getName()
172 );
173 }
174 }
175
176 Method idGetterMethod = idGetter==null ? null : idGetter.getMethod();
177 Method idSetterMethod = idSetter==null ? null : idSetter.getMethod();
178
179 Method proxyGetIdentifierMethod = idGetterMethod==null || proxyInterface==null ?
180 null :
181 ReflectHelper.getMethod(proxyInterface, idGetterMethod);
182 Method proxySetIdentifierMethod = idSetterMethod==null || proxyInterface==null ?
183 null :
184 ReflectHelper.getMethod(proxyInterface, idSetterMethod);
185
186 ProxyFactory pf = buildProxyFactoryInternal( persistentClass, idGetter, idSetter );
187 try {
188 pf.postInstantiate(
189 getEntityName(),
190 mappedClass,
191 proxyInterfaces,
192 proxyGetIdentifierMethod,
193 proxySetIdentifierMethod,
194 persistentClass.hasEmbeddedIdentifier() ?
195 (AbstractComponentType) persistentClass.getIdentifier().getType() :
196 null
197 );
198 }
199 catch ( HibernateException he ) {
200 log.warn( "could not create proxy factory for:" + getEntityName(), he );
201 pf = null;
202 }
203 return pf;
204 }
205
206 protected ProxyFactory buildProxyFactoryInternal(PersistentClass persistentClass, Getter idGetter, Setter idSetter) {
207 // TODO : YUCK!!! fix after HHH-1907 is complete
208 return Environment.getBytecodeProvider().getProxyFactoryFactory().buildProxyFactory();
209 // return getFactory().getSettings().getBytecodeProvider().getProxyFactoryFactory().buildProxyFactory();
210 }
211
212 /**
213 * {@inheritDoc}
214 */
215 protected Instantiator buildInstantiator(PersistentClass persistentClass) {
216 if ( optimizer == null ) {
217 return new PojoInstantiator( persistentClass, null );
218 }
219 else {
220 return new PojoInstantiator( persistentClass, optimizer.getInstantiationOptimizer() );
221 }
222 }
223
224 /**
225 * {@inheritDoc}
226 */
227 public void setPropertyValues(Object entity, Object[] values) throws HibernateException {
228 if ( !getEntityMetamodel().hasLazyProperties() && optimizer != null && optimizer.getAccessOptimizer() != null ) {
229 setPropertyValuesWithOptimizer( entity, values );
230 }
231 else {
232 super.setPropertyValues( entity, values );
233 }
234 }
235
236 /**
237 * {@inheritDoc}
238 */
239 public Object[] getPropertyValues(Object entity) throws HibernateException {
240 if ( shouldGetAllProperties( entity ) && optimizer != null && optimizer.getAccessOptimizer() != null ) {
241 return getPropertyValuesWithOptimizer( entity );
242 }
243 else {
244 return super.getPropertyValues( entity );
245 }
246 }
247
248 /**
249 * {@inheritDoc}
250 */
251 public Object[] getPropertyValuesToInsert(Object entity, Map mergeMap, SessionImplementor session) throws HibernateException {
252 if ( shouldGetAllProperties( entity ) && optimizer != null && optimizer.getAccessOptimizer() != null ) {
253 return getPropertyValuesWithOptimizer( entity );
254 }
255 else {
256 return super.getPropertyValuesToInsert( entity, mergeMap, session );
257 }
258 }
259
260 protected void setPropertyValuesWithOptimizer(Object object, Object[] values) {
261 optimizer.getAccessOptimizer().setPropertyValues( object, values );
262 }
263
264 protected Object[] getPropertyValuesWithOptimizer(Object object) {
265 return optimizer.getAccessOptimizer().getPropertyValues( object );
266 }
267
268 /**
269 * {@inheritDoc}
270 */
271 public EntityMode getEntityMode() {
272 return EntityMode.POJO;
273 }
274
275 /**
276 * {@inheritDoc}
277 */
278 public Class getMappedClass() {
279 return mappedClass;
280 }
281
282 /**
283 * {@inheritDoc}
284 */
285 public boolean isLifecycleImplementor() {
286 return lifecycleImplementor;
287 }
288
289 /**
290 * {@inheritDoc}
291 */
292 public boolean isValidatableImplementor() {
293 return validatableImplementor;
294 }
295
296 /**
297 * {@inheritDoc}
298 */
299 protected Getter buildPropertyGetter(Property mappedProperty, PersistentClass mappedEntity) {
300 return mappedProperty.getGetter( mappedEntity.getMappedClass() );
301 }
302
303 /**
304 * {@inheritDoc}
305 */
306 protected Setter buildPropertySetter(Property mappedProperty, PersistentClass mappedEntity) {
307 return mappedProperty.getSetter( mappedEntity.getMappedClass() );
308 }
309
310 /**
311 * {@inheritDoc}
312 */
313 public Class getConcreteProxyClass() {
314 return proxyInterface;
315 }
316
317 //TODO: need to make the majority of this functionality into a top-level support class for custom impl support
318
319 /**
320 * {@inheritDoc}
321 */
322 public void afterInitialize(Object entity, boolean lazyPropertiesAreUnfetched, SessionImplementor session) {
323 if ( isInstrumented() ) {
324 Set lazyProps = lazyPropertiesAreUnfetched && getEntityMetamodel().hasLazyProperties() ?
325 lazyPropertyNames : null;
326 //TODO: if we support multiple fetch groups, we would need
327 // to clone the set of lazy properties!
328 FieldInterceptionHelper.injectFieldInterceptor( entity, getEntityName(), lazyProps, session );
329 }
330 }
331
332 /**
333 * {@inheritDoc}
334 */
335 public boolean hasUninitializedLazyProperties(Object entity) {
336 if ( getEntityMetamodel().hasLazyProperties() ) {
337 FieldInterceptor callback = FieldInterceptionHelper.extractFieldInterceptor( entity );
338 return callback != null && !callback.isInitialized();
339 }
340 else {
341 return false;
342 }
343 }
344
345 /**
346 * {@inheritDoc}
347 */
348 public boolean isInstrumented() {
349 return FieldInterceptionHelper.isInstrumented( getMappedClass() );
350 }
351
352 /**
353 * {@inheritDoc}
354 */
355 public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory) {
356 final Class concreteEntityClass = entityInstance.getClass();
357 if ( concreteEntityClass == getMappedClass() ) {
358 return getEntityName();
359 }
360 else {
361 String entityName = getEntityMetamodel().findEntityNameByEntityClass( concreteEntityClass );
362 if ( entityName == null ) {
363 throw new HibernateException(
364 "Unable to resolve entity name from Class [" + concreteEntityClass.getName() + "]"
365 + " expected instance/subclass of [" + getEntityName() + "]"
366 );
367 }
368 return entityName;
369 }
370 }
371
372 /**
373 * {@inheritDoc}
374 */
375 public EntityNameResolver[] getEntityNameResolvers() {
376 return null;
377 }
378 }