/** * Run a block of code in a JPA transaction. * * @param name The persistence unit name * @param readOnly Is the transaction read-only? * @param block Block of code to execute */ public <T> T withTransaction(String name, boolean readOnly, Supplier<T> block) { EntityManager entityManager = null; EntityTransaction tx = null; try { entityManager = em(name); if (entityManager == null) { throw new RuntimeException("No JPA entity manager defined for '" + name + "'"); } JPA.bindForSync(entityManager); if (!readOnly) { tx = entityManager.getTransaction(); tx.begin(); } T result = block.get(); if (tx != null) { if (tx.getRollbackOnly()) { tx.rollback(); } else { tx.commit(); } } return result; } catch (Throwable t) { if (tx != null) { try { tx.rollback(); } catch (Throwable e) { } } throw t; } finally { JPA.bindForSync(null); if (entityManager != null) { entityManager.close(); } } }
/** Returns the correct JPAConfig used to manage this entity-class */ public static JPAConfig getJPAConfig(Class clazz) { return JPA.getJPAConfig(Entity2JPAConfigResolver.getJPAConfigNameForEntityClass(clazz)); }
protected JPAContext getJPAContext() { if (_jpaConfig == null) { _jpaConfig = JPA.getJPAConfig(jpaConfigName); } return _jpaConfig.getJPAContext(); }
@Override public void afterFixtureLoad() { JPA.clear(); }
/** * clear current JPA context and transaction if JPAPlugin.autoTxs is true When using multiple * databases in the same request this method tries to commit/rollback as many transactions as * possible, but there is not guaranteed that all transactions are committed. * * @param rollback shall current transaction be committed (false) or cancelled (true) */ public static void closeTx(boolean rollback) { if (autoTxs) { JPA.closeTx(rollback); } }
/** * initialize the JPA context and starts a JPA transaction if not already started. * * <p>This method is not needed since transaction is created automatically on first use. * * <p>It is better to specify readonly like this: @Transactional(readOnly=true) * * @param readonly true for a readonly transaction * @deprecated use @Transactional with readOnly-property instead */ @Deprecated public static void startTx(boolean readonly) { // Create new transaction by getting the JPAContext JPA.getJPAConfig(DBConfig.defaultDbConfigName).getJPAContext(readonly); }
@Override public void beforeInvocation() { // just to be safe we must clear all possible previous // JPAContexts in this thread JPA.clearJPAContext(); }
@Override public void onApplicationStop() { JPA.close(); }
@Override public void onApplicationStart() { // must check and configure JPA for each DBConfig for (DBConfig dbConfig : DB.getDBConfigs()) { // check and enable JPA on this config // is JPA already configured? String configName = dbConfig.getDBConfigName(); if (JPA.getJPAConfig(configName, true) == null) { // must configure it // resolve prefix for hibernate config.. // should be nothing for default, and db_<name> for others String propPrefix = ""; if (!DBConfig.defaultDbConfigName.equalsIgnoreCase(configName)) { propPrefix = "db_" + configName + "."; } List<Class> classes = findEntityClassesForThisConfig(configName, propPrefix); if (classes == null) continue; // we're ready to configure this instance of JPA final String hibernateDataSource = Play.configuration.getProperty(propPrefix + "hibernate.connection.datasource"); if (StringUtils.isEmpty(hibernateDataSource) && dbConfig == null) { throw new JPAException( "Cannot start a JPA manager without a properly configured database" + getConfigInfoString(configName), new NullPointerException("No datasource configured")); } Ejb3Configuration cfg = new Ejb3Configuration(); if (dbConfig.getDatasource() != null) { cfg.setDataSource(dbConfig.getDatasource()); } if (!Play.configuration .getProperty(propPrefix + "jpa.ddl", Play.mode.isDev() ? "update" : "none") .equals("none")) { cfg.setProperty( "hibernate.hbm2ddl.auto", Play.configuration.getProperty(propPrefix + "jpa.ddl", "update")); } String driver = null; if (StringUtils.isEmpty(propPrefix)) { driver = Play.configuration.getProperty("db.driver"); } else { driver = Play.configuration.getProperty(propPrefix + "driver"); } cfg.setProperty("hibernate.dialect", getDefaultDialect(propPrefix, driver)); cfg.setProperty("javax.persistence.transaction", "RESOURCE_LOCAL"); cfg.setInterceptor(new PlayInterceptor()); // This setting is global for all JPAs - only configure if configuring default JPA if (StringUtils.isEmpty(propPrefix)) { if (Play.configuration.getProperty(propPrefix + "jpa.debugSQL", "false").equals("true")) { org.apache.log4j.Logger.getLogger("org.hibernate.SQL").setLevel(Level.ALL); } else { org.apache.log4j.Logger.getLogger("org.hibernate.SQL").setLevel(Level.OFF); } } // inject additional hibernate.* settings declared in Play! configuration Properties additionalProperties = (Properties) Utils.Maps.filterMap(Play.configuration, "^" + propPrefix + "hibernate\\..*"); // We must remove prefix from names Properties transformedAdditionalProperties = new Properties(); for (Map.Entry<Object, Object> entry : additionalProperties.entrySet()) { Object key = entry.getKey(); if (!StringUtils.isEmpty(propPrefix)) { key = ((String) key).substring(propPrefix.length()); // chop off the prefix } transformedAdditionalProperties.put(key, entry.getValue()); } cfg.addProperties(transformedAdditionalProperties); try { // nice hacking :) I like it.. Field field = cfg.getClass().getDeclaredField("overridenClassLoader"); field.setAccessible(true); field.set(cfg, Play.classloader); } catch (Exception e) { Logger.error( e, "Error trying to override the hibernate classLoader (new hibernate version ???)"); } for (Class<?> clazz : classes) { cfg.addAnnotatedClass(clazz); if (Logger.isTraceEnabled()) { Logger.trace("JPA Model : %s", clazz); } } String[] moreEntities = Play.configuration.getProperty(propPrefix + "jpa.entities", "").split(", "); for (String entity : moreEntities) { if (entity.trim().equals("")) { continue; } try { cfg.addAnnotatedClass(Play.classloader.loadClass(entity)); } catch (Exception e) { Logger.warn("JPA -> Entity not found: %s", entity); } } for (ApplicationClass applicationClass : Play.classes.all()) { if (applicationClass.isClass() || applicationClass.javaPackage == null) { continue; } Package p = applicationClass.javaPackage; Logger.info("JPA -> Adding package: %s", p.getName()); cfg.addPackage(p.getName()); } String mappingFile = Play.configuration.getProperty(propPrefix + "jpa.mapping-file", ""); if (mappingFile != null && mappingFile.length() > 0) { cfg.addResource(mappingFile); } if (Logger.isTraceEnabled()) { Logger.trace("Initializing JPA" + getConfigInfoString(configName) + " ..."); } try { JPA.addConfiguration(configName, cfg); } catch (PersistenceException e) { throw new JPAException( e.getMessage() + getConfigInfoString(configName), e.getCause() != null ? e.getCause() : e); } } } // must look for Entity-objects referring to none-existing JPAConfig List<Class> allEntityClasses = Play.classloader.getAnnotatedClasses(Entity.class); for (Class clazz : allEntityClasses) { String configName = Entity2JPAConfigResolver.getJPAConfigNameForEntityClass(clazz); if (JPA.getJPAConfig(configName, true) == null) { throw new JPAException( "Found Entity-class (" + clazz.getName() + ") referring to none-existing JPAConfig" + getConfigInfoString(configName) + ". " + "Is JPA properly configured?"); } } }
/** * Run a block of asynchronous code in a JPA transaction. * * @param name The persistence unit name * @param readOnly Is the transaction read-only? * @param block Block of code to execute. * @deprecated This may cause deadlocks */ @Deprecated public <T> F.Promise<T> withTransactionAsync( String name, boolean readOnly, Supplier<F.Promise<T>> block) { EntityManager entityManager = null; EntityTransaction tx = null; try { entityManager = em(name); if (entityManager == null) { throw new RuntimeException("No JPA entity manager defined for '" + name + "'"); } JPA.bindForAsync(entityManager); if (!readOnly) { tx = entityManager.getTransaction(); tx.begin(); } F.Promise<T> result = block.get(); final EntityManager fem = entityManager; final EntityTransaction ftx = tx; F.Promise<T> committedResult = (ftx == null) ? result : result.map( t -> { if (ftx.getRollbackOnly()) { ftx.rollback(); } else { ftx.commit(); } return t; }); committedResult.onFailure( t -> { if (ftx != null) { try { if (ftx.isActive()) { ftx.rollback(); } } catch (Throwable e) { } } try { fem.close(); } finally { JPA.bindForAsync(null); } }); committedResult.onRedeem( t -> { try { fem.close(); } finally { JPA.bindForAsync(null); } }); return committedResult; } catch (Throwable t) { if (tx != null) { try { tx.rollback(); } catch (Throwable e) { } } if (entityManager != null) { try { entityManager.close(); } finally { JPA.bindForAsync(null); } } throw t; } }
public boolean isPersistent() { return JPA.em().contains(this); }
/** * Retrieve the current entityManager * * @return the current entityManager */ public static EntityManager em() { return JPA.em(); }