1 16 17 package org.springframework.orm.jpa; 18 19 import java.lang.reflect.InvocationHandler ; 20 import java.lang.reflect.InvocationTargetException ; 21 import java.lang.reflect.Method ; 22 import java.lang.reflect.Proxy ; 23 import java.util.Map ; 24 25 import javax.persistence.EntityManager; 26 import javax.persistence.EntityManagerFactory; 27 import javax.persistence.EntityTransaction; 28 import javax.persistence.TransactionRequiredException; 29 30 import org.apache.commons.logging.Log; 31 import org.apache.commons.logging.LogFactory; 32 33 import org.springframework.dao.DataAccessException; 34 import org.springframework.dao.support.PersistenceExceptionTranslator; 35 import org.springframework.transaction.support.TransactionSynchronizationAdapter; 36 import org.springframework.transaction.support.TransactionSynchronizationManager; 37 import org.springframework.util.Assert; 38 import org.springframework.util.ClassUtils; 39 import org.springframework.util.CollectionUtils; 40 import org.springframework.util.ObjectUtils; 41 42 55 public abstract class ExtendedEntityManagerCreator { 56 57 67 public static EntityManager createApplicationManagedEntityManager( 68 EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations) { 69 70 return createProxy(rawEntityManager, plusOperations, null, false); 71 } 72 73 86 public static EntityManager createApplicationManagedEntityManager( 87 EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations, 88 PersistenceExceptionTranslator exceptionTranslator) { 89 90 return createProxy(rawEntityManager, plusOperations, exceptionTranslator, false); 91 } 92 93 102 public static EntityManager createContainerManagedEntityManager( 103 EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations) { 104 105 return createProxy(rawEntityManager, plusOperations, null, true); 106 } 107 108 120 public static EntityManager createContainerManagedEntityManager( 121 EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations, 122 PersistenceExceptionTranslator exceptionTranslator) { 123 124 return createProxy(rawEntityManager, plusOperations, exceptionTranslator, true); 125 } 126 127 138 public static EntityManager createContainerManagedEntityManager(EntityManagerFactory emf) { 139 return createContainerManagedEntityManager(emf, null); 140 } 141 142 155 public static EntityManager createContainerManagedEntityManager(EntityManagerFactory emf, Map properties) { 156 Assert.notNull(emf, "EntityManagerFactory must not be null"); 157 if (emf instanceof EntityManagerFactoryInfo) { 158 EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) emf; 159 EntityManagerFactory nativeEmf = emfInfo.getNativeEntityManagerFactory(); 160 EntityManager rawEntityManager = (!CollectionUtils.isEmpty(properties) ? 161 nativeEmf.createEntityManager(properties) : nativeEmf.createEntityManager()); 162 JpaDialect jpaDialect = emfInfo.getJpaDialect(); 163 EntityManagerPlusOperations plusOperations = null; 164 if (jpaDialect != null && jpaDialect.supportsEntityManagerPlusOperations()) { 165 plusOperations = jpaDialect.getEntityManagerPlusOperations(rawEntityManager); 166 } 167 return createProxy(rawEntityManager, plusOperations, emfInfo.getJpaDialect(), true); 168 } 169 else { 170 EntityManager rawEntityManager = (!CollectionUtils.isEmpty(properties) ? 171 emf.createEntityManager(properties) : emf.createEntityManager()); 172 return createProxy(rawEntityManager, null, null, true); 173 } 174 } 175 176 185 private static EntityManager createProxy( 186 EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations, 187 PersistenceExceptionTranslator exceptionTranslator, boolean containerManaged) { 188 189 Assert.notNull(rawEntityManager, "EntityManager must not be null"); 190 Class [] ifcs = ClassUtils.getAllInterfaces(rawEntityManager); 191 if (plusOperations != null) { 192 ifcs = (Class []) ObjectUtils.addObjectToArray(ifcs, EntityManagerPlusOperations.class); 193 } 194 return (EntityManager) Proxy.newProxyInstance( 195 ExtendedEntityManagerCreator.class.getClassLoader(), ifcs, 196 new ExtendedEntityManagerInvocationHandler( 197 rawEntityManager, plusOperations, exceptionTranslator, containerManaged)); 198 } 199 200 201 204 private static class ExtendedEntityManagerInvocationHandler implements InvocationHandler { 205 206 private static final Log logger = LogFactory.getLog(ExtendedEntityManagerInvocationHandler.class); 207 208 private final EntityManager target; 209 210 private final EntityManagerPlusOperations plusOperations; 211 212 private final PersistenceExceptionTranslator exceptionTranslator; 213 214 private final boolean containerManaged; 215 216 private boolean jta; 217 218 private ExtendedEntityManagerInvocationHandler( 219 EntityManager target, EntityManagerPlusOperations plusOperations, 220 PersistenceExceptionTranslator exceptionTranslator, boolean containerManaged) { 221 222 this.target = target; 223 this.plusOperations = plusOperations; 224 this.exceptionTranslator = exceptionTranslator; 225 this.containerManaged = containerManaged; 226 this.jta = isJtaEntityManager(); 227 } 228 229 private boolean isJtaEntityManager() { 230 try { 231 this.target.getTransaction(); 232 return false; 233 } 234 catch (IllegalStateException ex) { 235 logger.debug("Cannot access EntityTransaction handle - assuming we're in a JTA environment"); 236 return true; 237 } 238 } 239 240 public Object invoke(Object proxy, Method method, Object [] args) throws Throwable { 241 243 if (method.getDeclaringClass().equals(EntityManagerPlusOperations.class)) { 244 return method.invoke(this.plusOperations, args); 245 } 246 247 if (method.getName().equals("equals")) { 248 return (proxy == args[0]); 250 } 251 else if (method.getName().equals("hashCode")) { 252 return hashCode(); 254 } 255 else if (method.getName().equals("joinTransaction")) { 256 doJoinTransaction(true); 257 return null; 258 } 259 else if (method.getName().equals("getTransaction")) { 260 if (this.containerManaged) { 261 throw new IllegalStateException ("Cannot execute getTransaction() on " + 262 "a container-managed EntityManager"); 263 } 264 } 265 else if (method.getName().equals("close")) { 266 if (this.containerManaged) { 267 throw new IllegalStateException ("Invalid usage: Cannot close a container-managed EntityManager"); 268 } 269 } 270 else if (method.getName().equals("isOpen")) { 271 if (this.containerManaged) { 272 return true; 273 } 274 } 275 276 if (this.containerManaged) { 278 doJoinTransaction(false); 279 } 280 281 try { 283 return method.invoke(this.target, args); 284 } 285 catch (InvocationTargetException ex) { 286 throw ex.getTargetException(); 287 } 288 } 289 290 293 private void doJoinTransaction(boolean enforce) { 294 if (this.jta) { 295 try { 297 this.target.joinTransaction(); 298 logger.debug("Joined JTA transaction"); 299 } 300 catch (TransactionRequiredException ex) { 301 if (!enforce) { 302 logger.debug("No JTA transaction to join: " + ex); 303 } 304 else { 305 throw ex; 306 } 307 } 308 } 309 else { 310 if (TransactionSynchronizationManager.isSynchronizationActive()) { 311 if (!TransactionSynchronizationManager.hasResource(this.target)) { 312 enlistInCurrentTransaction(); 313 } 314 logger.debug("Joined local transaction"); 315 } 316 else { 317 if (!enforce) { 318 logger.debug("No local transaction to join"); 319 } 320 else { 321 throw new TransactionRequiredException("No local transaction to join"); 322 } 323 } 324 } 325 } 326 327 330 private void enlistInCurrentTransaction() { 331 EntityTransaction et = this.target.getTransaction(); 335 et.begin(); 336 if (logger.isDebugEnabled()) { 337 logger.debug("Starting resource local transaction on application-managed " + 338 "EntityManager [" + this.target + "]"); 339 } 340 EntityManagerHolder emh = new EntityManagerHolder(this.target); 341 ContainerManagedExtendedEntityManagerSynchronization applicationManagedEntityManagerSynchronization = 342 new ContainerManagedExtendedEntityManagerSynchronization(emh, this.exceptionTranslator); 343 TransactionSynchronizationManager.bindResource(this.target, 344 applicationManagedEntityManagerSynchronization); 345 TransactionSynchronizationManager.registerSynchronization(applicationManagedEntityManagerSynchronization); 346 } 347 } 348 349 350 354 private static class ContainerManagedExtendedEntityManagerSynchronization extends 355 TransactionSynchronizationAdapter { 356 357 private final EntityManagerHolder entityManagerHolder; 358 359 private final PersistenceExceptionTranslator exceptionTranslator; 360 361 private boolean holderActive = true; 362 363 public ContainerManagedExtendedEntityManagerSynchronization( 364 EntityManagerHolder emHolder, PersistenceExceptionTranslator exceptionTranslator) { 365 366 this.entityManagerHolder = emHolder; 367 this.exceptionTranslator = exceptionTranslator; 368 } 369 370 public int getOrder() { 371 return EntityManagerFactoryUtils.ENTITY_MANAGER_SYNCHRONIZATION_ORDER + 1; 372 } 373 374 public void suspend() { 375 if (this.holderActive) { 376 TransactionSynchronizationManager.unbindResource(this.entityManagerHolder.getEntityManager()); 377 } 378 } 379 380 public void resume() { 381 if (this.holderActive) { 382 TransactionSynchronizationManager.bindResource( 383 this.entityManagerHolder.getEntityManager(), this.entityManagerHolder); 384 } 385 } 386 387 public void beforeCompletion() { 388 TransactionSynchronizationManager.unbindResource(this.entityManagerHolder.getEntityManager()); 389 this.holderActive = false; 390 } 391 392 public void afterCommit() { 393 try { 395 this.entityManagerHolder.getEntityManager().getTransaction().commit(); 396 } 397 catch (RuntimeException ex) { 398 throw convertCompletionException(ex); 399 } 400 } 401 402 public void afterCompletion(int status) { 403 this.entityManagerHolder.setSynchronizedWithTransaction(false); 404 if (status != STATUS_COMMITTED) { 405 try { 407 this.entityManagerHolder.getEntityManager().getTransaction().rollback(); 408 } 409 catch (RuntimeException ex) { 410 throw convertCompletionException(ex); 411 } 412 } 413 } 415 416 private RuntimeException convertCompletionException(RuntimeException ex) { 417 DataAccessException daex = (this.exceptionTranslator != null) ? 418 this.exceptionTranslator.translateExceptionIfPossible(ex) : 419 EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex); 420 return (daex != null ? daex : ex); 421 } 422 } 423 424 } 425 | Popular Tags |