diff --git a/.gitignore b/.gitignore index ec28e920ca..95da09e28b 100644 --- a/.gitignore +++ b/.gitignore @@ -84,6 +84,7 @@ support/textmate samples-and-tests/yabe/logs samples-and-tests/yabe/test-result samples-and-tests/yabe/tmp +samples-and-tests/yabe/modules *.ser modules/cobertura/lib modules/scala/lib/play-scala.jar diff --git a/.travis.yml b/.travis.yml index 61f45d91df..7951a6671c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,13 +13,12 @@ addons: hosts: - myshorthost hostname: myshorthost + script: ant -buildfile ./framework/build.xml test -after_failure: - cat ./samples-and-tests/just-test-cases/test-result/*.failed.html - cat ./samples-and-tests/forum/test-result/*.failed.html - cat ./samples-and-tests/zencontact/test-result/*.failed.html - cat ./samples-and-tests/jobboard/test-result/*.failed.html - cat ./samples-and-tests/yabe/test-result/*.failed.html + +after_failure: + find samples-and-tests -name '*.failed.html' -exec echo {} \; -exec cat {} \; + notifications: webhooks: urls: diff --git a/documentation/manual/releases/release1.0.x/releasenotes-1.0.1.textile b/documentation/manual/releases/release1.0.x/releasenotes-1.0.1.textile index 8f3587fe7d..7b6fbd1283 100644 --- a/documentation/manual/releases/release1.0.x/releasenotes-1.0.1.textile +++ b/documentation/manual/releases/release1.0.x/releasenotes-1.0.1.textile @@ -12,7 +12,7 @@ Now all dynamic expressions are escaped by the template engine to avoid XSS secu bc. ${title} --> <h1>Title</h1> -If you really want to display it in an unescaped way, you need to explicitely call the @raw()@ method: +If you really want to display it in an unescaped way, you need to explicitly call the @raw()@ method: bc. ${title.raw()} -->

Title

@@ -119,7 +119,7 @@ bc. public class User { h2. Test runner update -We’ve updated Selenium to the 1.0.1 final version and improved the UI. Selenium tests now run in fullscreen. And some new functionality like the "Run all tests" have been added. +We’ve updated Selenium to the 1.0.1 final version and improved the UI. Selenium tests now run in full screen. And some new functionality like the "Run all tests" have been added. !images/selenium-fullscreen! diff --git a/documentation/manual/releases/release1.0.x/releasenotes.textile b/documentation/manual/releases/release1.0.x/releasenotes.textile index c0f1a43f1d..b192ba9c86 100644 --- a/documentation/manual/releases/release1.0.x/releasenotes.textile +++ b/documentation/manual/releases/release1.0.x/releasenotes.textile @@ -12,7 +12,7 @@ Now all dynamic expressions are escaped by the template engine to avoid XSS secu bc. ${title} --> <h1>Title</h1> -If you really want to display it in an unescaped way, you need to explicitely call the **raw()** method: +If you really want to display it in an unescaped way, you need to explicitly call the **raw()** method: bc. ${title.raw()} -->

Title

@@ -71,7 +71,7 @@ bc. public static void save(User user) { user.save(); // ok with 1.0.1 } -Of course as this feature can break existing applications it is not enabled by default. You can enable it by adding the followin line to your **application.conf** file: +Of course as this feature can break existing applications it is not enabled by default. You can enable it by adding the following line to your **application.conf** file: bc. future.bindJPAObjects=true @@ -119,7 +119,7 @@ bc. public class User { h2. Test runner update -We've updated selenium to the 1.0.1 final version and improved the UI. Selenium tests now run in fullscreen. And some new functionalities like the "Run all tests" have been added. +We've updated selenium to the 1.0.1 final version and improved the UI. Selenium tests now run in full screen. And some new functionalities like the "Run all tests" have been added. !images/selenium-fullscreen! diff --git a/documentation/manual/templates.textile b/documentation/manual/templates.textile index fdc3929436..90e5270227 100644 --- a/documentation/manual/templates.textile +++ b/documentation/manual/templates.textile @@ -80,7 +80,7 @@ All dynamic expressions are escaped by the template engine to avoid XSS security bc. ${title} --> <h1>Title</h1> -If you really want to display it in an unescaped way, you need to explicitely call the @raw()@ method: +If you really want to display it in an unescaped way, you need to explicitly call the @raw()@ method: bc. ${title.raw()} -->

Title

diff --git a/framework/dependencies.yml b/framework/dependencies.yml index 924fcff4fd..39227487a5 100644 --- a/framework/dependencies.yml +++ b/framework/dependencies.yml @@ -45,15 +45,16 @@ require: &allDependencies - org.bouncycastle -> bcprov-jdk15 1.46 - org.codehaus.groovy -> groovy-all 2.4.11 - org.eclipse.jdt.core 3.12.3 - - org.hibernate -> hibernate-core 4.2.19.Final - - org.hibernate -> hibernate-commons-annotations 4.0.2.Final - - org.hibernate -> hibernate-entitymanager 4.2.19.Final - - org.hibernate -> hibernate-validator 4.1.0.Final - - org.hibernate -> jboss-logging 3.1.0.GA - - org.hibernate -> jboss-transaction-api_1.1_spec 1.0.1.Final - - org.hibernate.javax.persistence -> hibernate-jpa-2.0-api 1.0.1.Final - - org.hibernate -> hibernate-c3p0 4.2.19.Final - - org.hibernate -> hibernate-ehcache 4.2.19.Final + - org.hibernate -> hibernate-core 5.2.10.patched + - org.hibernate.common -> hibernate-commons-annotations 5.0.1.Final + - org.hibernate -> hibernate-entitymanager 5.2.10.Final + - org.hibernate -> hibernate-validator 5.4.1.Final + - org.jboss.logging -> jboss-logging 3.3.0.Final + - org.jboss.spec.javax.transaction -> jboss-transaction-api_1.2_spec 1.0.1.Final + - org.hibernate.javax.persistence -> hibernate-jpa-2.1-api 1.0.0.Final + - com.fasterxml -> classmate 1.3.3 + - org.hibernate -> hibernate-c3p0 5.2.10.Final + - org.hibernate -> hibernate-ehcache 5.2.10.Final - com.mchange -> mchange-commons-java 0.2.12 - org.javassist -> javassist 3.21.0-GA - io.netty -> netty 3.10.6.Final diff --git a/framework/lib/classmate-1.3.3.jar b/framework/lib/classmate-1.3.3.jar new file mode 100644 index 0000000000..d44873ea54 Binary files /dev/null and b/framework/lib/classmate-1.3.3.jar differ diff --git a/framework/lib/hibernate-c3p0-4.2.19.Final.jar b/framework/lib/hibernate-c3p0-4.2.19.Final.jar deleted file mode 100644 index 984d327a6e..0000000000 Binary files a/framework/lib/hibernate-c3p0-4.2.19.Final.jar and /dev/null differ diff --git a/framework/lib/hibernate-c3p0-5.2.10.Final.jar b/framework/lib/hibernate-c3p0-5.2.10.Final.jar new file mode 100644 index 0000000000..9416da0e1a Binary files /dev/null and b/framework/lib/hibernate-c3p0-5.2.10.Final.jar differ diff --git a/framework/lib/hibernate-commons-annotations-4.0.2.Final.jar b/framework/lib/hibernate-commons-annotations-4.0.2.Final.jar deleted file mode 100644 index c26aba4ed5..0000000000 Binary files a/framework/lib/hibernate-commons-annotations-4.0.2.Final.jar and /dev/null differ diff --git a/framework/lib/hibernate-commons-annotations-5.0.1.Final.jar b/framework/lib/hibernate-commons-annotations-5.0.1.Final.jar new file mode 100644 index 0000000000..82e425dc2e Binary files /dev/null and b/framework/lib/hibernate-commons-annotations-5.0.1.Final.jar differ diff --git a/framework/lib/hibernate-core-4.2.19.Final.jar b/framework/lib/hibernate-core-4.2.19.Final.jar deleted file mode 100644 index 54b3fea2d7..0000000000 Binary files a/framework/lib/hibernate-core-4.2.19.Final.jar and /dev/null differ diff --git a/framework/lib/hibernate-core-5.2.10.patched.jar b/framework/lib/hibernate-core-5.2.10.patched.jar new file mode 100644 index 0000000000..65249ba583 Binary files /dev/null and b/framework/lib/hibernate-core-5.2.10.patched.jar differ diff --git a/framework/lib/hibernate-ehcache-4.2.19.Final.jar b/framework/lib/hibernate-ehcache-4.2.19.Final.jar deleted file mode 100644 index 1ce3f341c3..0000000000 Binary files a/framework/lib/hibernate-ehcache-4.2.19.Final.jar and /dev/null differ diff --git a/framework/lib/hibernate-ehcache-5.2.10.Final.jar b/framework/lib/hibernate-ehcache-5.2.10.Final.jar new file mode 100644 index 0000000000..4be83a7700 Binary files /dev/null and b/framework/lib/hibernate-ehcache-5.2.10.Final.jar differ diff --git a/framework/lib/hibernate-entitymanager-4.2.19.Final.jar b/framework/lib/hibernate-entitymanager-4.2.19.Final.jar deleted file mode 100644 index 5700709aed..0000000000 Binary files a/framework/lib/hibernate-entitymanager-4.2.19.Final.jar and /dev/null differ diff --git a/framework/lib/hibernate-entitymanager-5.2.10.Final.jar b/framework/lib/hibernate-entitymanager-5.2.10.Final.jar new file mode 100644 index 0000000000..0e328fd5f5 Binary files /dev/null and b/framework/lib/hibernate-entitymanager-5.2.10.Final.jar differ diff --git a/framework/lib/hibernate-jpa-2.0-api-1.0.1.Final.jar b/framework/lib/hibernate-jpa-2.0-api-1.0.1.Final.jar deleted file mode 100644 index 1e9f71b8c1..0000000000 Binary files a/framework/lib/hibernate-jpa-2.0-api-1.0.1.Final.jar and /dev/null differ diff --git a/framework/lib/hibernate-jpa-2.1-api-1.0.0.Final.jar b/framework/lib/hibernate-jpa-2.1-api-1.0.0.Final.jar new file mode 100644 index 0000000000..e2f2c59287 Binary files /dev/null and b/framework/lib/hibernate-jpa-2.1-api-1.0.0.Final.jar differ diff --git a/framework/lib/hibernate-validator-4.1.0.Final.jar b/framework/lib/hibernate-validator-4.1.0.Final.jar deleted file mode 100644 index abf87e4b41..0000000000 Binary files a/framework/lib/hibernate-validator-4.1.0.Final.jar and /dev/null differ diff --git a/framework/lib/hibernate-validator-5.4.1.Final.jar b/framework/lib/hibernate-validator-5.4.1.Final.jar new file mode 100644 index 0000000000..1975846ff4 Binary files /dev/null and b/framework/lib/hibernate-validator-5.4.1.Final.jar differ diff --git a/framework/lib/jboss-logging-3.1.0.GA.jar b/framework/lib/jboss-logging-3.1.0.GA.jar deleted file mode 100644 index 72113b0f84..0000000000 Binary files a/framework/lib/jboss-logging-3.1.0.GA.jar and /dev/null differ diff --git a/framework/lib/jboss-logging-3.3.0.Final.jar b/framework/lib/jboss-logging-3.3.0.Final.jar new file mode 100644 index 0000000000..ea45d4d341 Binary files /dev/null and b/framework/lib/jboss-logging-3.3.0.Final.jar differ diff --git a/framework/lib/jboss-transaction-api_1.1_spec-1.0.1.Final.jar b/framework/lib/jboss-transaction-api_1.1_spec-1.0.1.Final.jar deleted file mode 100644 index 981f8f9910..0000000000 Binary files a/framework/lib/jboss-transaction-api_1.1_spec-1.0.1.Final.jar and /dev/null differ diff --git a/framework/lib/jboss-transaction-api_1.2_spec-1.0.1.Final.jar b/framework/lib/jboss-transaction-api_1.2_spec-1.0.1.Final.jar new file mode 100644 index 0000000000..2113cc0279 Binary files /dev/null and b/framework/lib/jboss-transaction-api_1.2_spec-1.0.1.Final.jar differ diff --git a/framework/patches/hibernate-4.2.19-patch-play.README b/framework/patches/hibernate-4.2.19-patch-play.README deleted file mode 100644 index c78e35d3ac..0000000000 --- a/framework/patches/hibernate-4.2.19-patch-play.README +++ /dev/null @@ -1,6 +0,0 @@ ----- -Download Hibernate 4.2.19.Final source code, apply the patch, and build with gradle (tip use export GRADLE_OPTS=-Xmx1G -XX:MaxPermSize=512m) ----- - -DRY RUN -> patch --dry-run -p1 -i hibernate-4.2.19-patch-play.patch -APPLY -> patch -p1 -i hibernate-4.2.19-patch-play.patch diff --git a/framework/patches/hibernate-5.2.10-patch-play.README b/framework/patches/hibernate-5.2.10-patch-play.README new file mode 100644 index 0000000000..5898a0f527 --- /dev/null +++ b/framework/patches/hibernate-5.2.10-patch-play.README @@ -0,0 +1,6 @@ +---- +Download Hibernate 5.2.10.Final source code, apply the patch, and build with gradle (tip use export GRADLE_OPTS=-Xmx1G -XX:MaxPermSize=512m) +---- + +DRY RUN -> patch --dry-run -p1 -i hibernate-5.2.10-patch-play.patch +APPLY -> patch -p1 -i hibernate-5.2.10-patch-play.patch diff --git a/framework/patches/hibernate-4.2.19-patch-play.patch.patch b/framework/patches/hibernate-5.2.10-patch-play.patch similarity index 100% rename from framework/patches/hibernate-4.2.19-patch-play.patch.patch rename to framework/patches/hibernate-5.2.10-patch-play.patch diff --git a/framework/src/play/classloading/ApplicationClassloader.java b/framework/src/play/classloading/ApplicationClassloader.java index f9f686ba66..920f59da24 100644 --- a/framework/src/play/classloading/ApplicationClassloader.java +++ b/framework/src/play/classloading/ApplicationClassloader.java @@ -255,15 +255,21 @@ public InputStream getResourceAsStream(String name) { @Override public URL getResource(String name) { - for (VirtualFile vf : Play.javaPath) { - VirtualFile res = vf.child(name); - if (res != null && res.exists()) { - try { + try { + for (VirtualFile vf : Play.javaPath) { + VirtualFile res = vf.child(name); + if (res != null && res.exists()) { return res.getRealFile().toURI().toURL(); - } catch (MalformedURLException ex) { - throw new UnexpectedException(ex); } } + if (Play.configuration.getProperty("play.bytecodeCache", "true").equals("true")) { + File f = new File(Play.tmpDir, "classes/" + name); + if (f.exists()) { + return f.toURI().toURL(); + } + } + } catch (MalformedURLException ex) { + throw new UnexpectedException(ex); } return super.getResource(name); } diff --git a/framework/src/play/db/DB.java b/framework/src/play/db/DB.java index ce043bcf6c..b538b8a242 100644 --- a/framework/src/play/db/DB.java +++ b/framework/src/play/db/DB.java @@ -15,6 +15,7 @@ import javax.sql.RowSet; import javax.sql.rowset.CachedRowSet; +import org.hibernate.jpa.HibernateEntityManager; import org.hibernate.internal.SessionImpl; import com.sun.rowset.CachedRowSetImpl; @@ -177,7 +178,7 @@ public static void close(String name) { public static Connection getConnection(String name) { try { if (JPA.isEnabled()) { - return ((SessionImpl) ((org.hibernate.ejb.EntityManagerImpl) JPA.em(name)).getSession()).connection(); + return ((SessionImpl) ((HibernateEntityManager) JPA.em(name)).getSession()).connection(); } Connection localConnection = getLocalConnection(name); diff --git a/framework/src/play/db/jpa/Blob.java b/framework/src/play/db/jpa/Blob.java index 4945d0f295..f29c503245 100644 --- a/framework/src/play/db/jpa/Blob.java +++ b/framework/src/play/db/jpa/Blob.java @@ -11,6 +11,7 @@ import org.hibernate.HibernateException; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.type.StringType; import org.hibernate.usertype.UserType; @@ -114,17 +115,17 @@ public int hashCode(Object o) throws HibernateException { } @Override - public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor sessionImplementor, Object o) throws HibernateException, SQLException { - String val = (String) StringType.INSTANCE.nullSafeGet(resultSet, names[0], sessionImplementor, o); + public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { + String val = (String) StringType.INSTANCE.nullSafeGet(rs, names[0], session, owner); return new Blob(val); } @Override - public void nullSafeSet(PreparedStatement ps, Object o, int i, SessionImplementor sessionImplementor) throws HibernateException, SQLException { - if(o != null) { - ps.setString(i, encode((Blob) o)); + public void nullSafeSet(PreparedStatement ps, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { + if (value != null) { + ps.setString(index, encode((Blob) value)); } else { - ps.setNull(i, Types.VARCHAR); + ps.setNull(index, Types.VARCHAR); } } diff --git a/framework/src/play/db/jpa/JPAPlugin.java b/framework/src/play/db/jpa/JPAPlugin.java index 1941adc2d1..0f9a343916 100644 --- a/framework/src/play/db/jpa/JPAPlugin.java +++ b/framework/src/play/db/jpa/JPAPlugin.java @@ -1,8 +1,9 @@ package play.db.jpa; import org.apache.log4j.Level; -import org.hibernate.ejb.Ejb3Configuration; - +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; +import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor; import play.Logger; import play.Play; import play.PlayPlugin; @@ -10,27 +11,24 @@ import play.data.binding.Binder; import play.data.binding.ParamNode; import play.data.binding.RootParamNode; +import play.db.Configuration; import play.db.DB; import play.db.Model; -import play.db.Configuration; import play.exceptions.JPAException; import play.exceptions.UnexpectedException; import javax.persistence.*; +import javax.persistence.spi.PersistenceUnitInfo; import java.lang.annotation.Annotation; import java.util.*; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; -/** - * JPA Plugin - */ public class JPAPlugin extends PlayPlugin { - - public static boolean autoTxs = true; - @Override public Object bind(RootParamNode rootParamNode, String name, Class clazz, java.lang.reflect.Type type, Annotation[] annotations) { @@ -110,74 +108,12 @@ public EntityManager em(String key) { */ @Override public void onApplicationStart() { - org.hibernate.ejb.HibernatePersistence persistence = new org.hibernate.ejb.HibernatePersistence(); org.apache.log4j.Logger.getLogger("org.hibernate.SQL").setLevel(Level.OFF); Set dBNames = Configuration.getDbNames(); for (String dbName : dBNames) { Configuration dbConfig = new Configuration(dbName); - Ejb3Configuration cfg = new Ejb3Configuration(); - List classes = Play.classloader.getAnnotatedClasses(Entity.class); - for (Class clazz : classes) { - if (clazz.isAnnotationPresent(Entity.class)) { - // Do we have a transactional annotation matching our dbname? - PersistenceUnit pu = clazz.getAnnotation(PersistenceUnit.class); - if (pu != null && pu.name().equals(dbName)) { - cfg.addAnnotatedClass(clazz); - Logger.debug("Add JPA Model : %s to db %s", clazz, dbName); - } else if (pu == null && JPA.DEFAULT.equals(dbName)) { - cfg.addAnnotatedClass(clazz); - Logger.debug("Add JPA Model : %s to db %s", clazz, dbName); - } - } - } - - // Add entities - String[] moreEntities = Play.configuration.getProperty("jpa.entities", "").split(", "); - for (String entity : moreEntities) { - if (entity.trim().equals("")) { - continue; - } - try { - Class clazz = Play.classloader.loadClass(entity); - // Do we have a transactional annotation matching our dbname? - PersistenceUnit pu = clazz.getAnnotation(PersistenceUnit.class); - if (pu != null && pu.name().equals(dbName)) { - cfg.addAnnotatedClass(clazz); - Logger.debug("Add JPA Model : %s to db %s", clazz, dbName); - } else if (pu == null && JPA.DEFAULT.equals(dbName)) { - cfg.addAnnotatedClass(clazz); - Logger.debug("Add JPA Model : %s to db %s", clazz, dbName); - } - } catch (Exception e) { - Logger.warn(e, "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 = dbConfig.getProperty("jpa.mapping-file", ""); - if (mappingFile != null && mappingFile.length() > 0) { - cfg.addResource(mappingFile); - } - - if (!dbConfig.getProperty("jpa.ddl", Play.mode.isDev() ? "update" : "none").equals("none")) { - cfg.setProperty("hibernate.hbm2ddl.auto", dbConfig.getProperty("jpa.ddl", "update")); - } - - Map properties = dbConfig.getProperties(); - properties.put("javax.persistence.transaction", "RESOURCE_LOCAL"); - properties.put("javax.persistence.provider", "org.hibernate.ejb.HibernatePersistence"); - properties.put("hibernate.dialect", getDefaultDialect(dbConfig, dbConfig.getProperty("db.driver"))); - if (dbConfig.getProperty("jpa.debugSQL", "false").equals("true")) { org.apache.log4j.Logger.getLogger("org.hibernate.SQL").setLevel(Level.ALL); } @@ -186,22 +122,92 @@ public void onApplicationStart() { ClassLoader contextClassLoader = thread.getContextClassLoader(); thread.setContextClassLoader(Play.classloader); try { - cfg.configure(properties); - cfg.setDataSource(DB.getDataSource(dbName)); - - cfg.setInterceptor(new HibernateInterceptor()); if (Logger.isTraceEnabled()) { Logger.trace("Initializing JPA for %s...", dbName); } - - JPA.emfs.put(dbName, cfg.buildEntityManagerFactory()); + + JPA.emfs.put(dbName, newEntityManagerFactory(dbName, dbConfig)); } finally { - if (thread != null) thread.setContextClassLoader(contextClassLoader); + thread.setContextClassLoader(contextClassLoader); } } JPQL.instance = new JPQL(); } + + private List entityClasses(String dbName) { + List entityClasses = new ArrayList<>(); + + List classes = Play.classloader.getAnnotatedClasses(Entity.class); + for (Class clazz : classes) { + if (clazz.isAnnotationPresent(Entity.class)) { + // Do we have a transactional annotation matching our dbname? + PersistenceUnit pu = clazz.getAnnotation(PersistenceUnit.class); + if (pu != null && pu.name().equals(dbName)) { + entityClasses.add(clazz.getName()); + } else if (pu == null && JPA.DEFAULT.equals(dbName)) { + entityClasses.add(clazz.getName()); + } + } + } + + // Add entities + String[] moreEntities = Play.configuration.getProperty("jpa.entities", "").split(", "); + for (String entity : moreEntities) { + if (entity.trim().equals("")) { + continue; + } + try { + Class clazz = Play.classloader.loadClass(entity); + // Do we have a transactional annotation matching our dbname? + PersistenceUnit pu = clazz.getAnnotation(PersistenceUnit.class); + if (pu != null && pu.name().equals(dbName)) { + entityClasses.add(clazz.getName()); + } else if (pu == null && JPA.DEFAULT.equals(dbName)) { + entityClasses.add(clazz.getName()); + } + } catch (Exception e) { + Logger.warn(e, "JPA -> Entity not found: %s", entity); + } + } + return entityClasses; + } + + protected EntityManagerFactory newEntityManagerFactory(String dbName, Configuration dbConfig) { + PersistenceUnitInfo persistenceUnitInfo = persistenceUnitInfo(dbName, dbConfig); + Map configuration = new HashMap<>(); + configuration.put(AvailableSettings.INTERCEPTOR, new HibernateInterceptor()); + + return new EntityManagerFactoryBuilderImpl( + new PersistenceUnitInfoDescriptor(persistenceUnitInfo), configuration + ).build(); + } + + protected PersistenceUnitInfoImpl persistenceUnitInfo(String dbName, Configuration dbConfig) { + return new PersistenceUnitInfoImpl(dbName, + entityClasses(dbName), mappingFiles(dbConfig), properties(dbName, dbConfig)); + } + + private List mappingFiles(Configuration dbConfig) { + String mappingFile = dbConfig.getProperty("jpa.mapping-file", ""); + return mappingFile != null && mappingFile.length() > 0 ? singletonList(mappingFile) : emptyList(); + + } + + protected Properties properties(String dbName, Configuration dbConfig) { + Properties properties = new Properties(); + properties.putAll(dbConfig.getProperties()); + properties.put("javax.persistence.transaction", "RESOURCE_LOCAL"); + properties.put("javax.persistence.provider", "org.hibernate.ejb.HibernatePersistence"); + properties.put("hibernate.dialect", getDefaultDialect(dbConfig, dbConfig.getProperty("db.driver"))); + + if (!dbConfig.getProperty("jpa.ddl", Play.mode.isDev() ? "update" : "none").equals("none")) { + properties.setProperty("hibernate.hbm2ddl.auto", dbConfig.getProperty("jpa.ddl", "update")); + } + + properties.put("hibernate.connection.datasource", DB.getDataSource(dbName)); + return properties; + } public static String getDefaultDialect(String driver) { return getDefaultDialect(new Configuration("default"), driver); @@ -257,15 +263,18 @@ public static String getDefaultDialect(Configuration dbConfig, String driver) { @Override public void onApplicationStop() { - // Close all presistence units - for(EntityManagerFactory emf: JPA.emfs.values()) { - if(emf.isOpen()){ + closeAllPersistenceUnits(); + } + + private void closeAllPersistenceUnits() { + for (EntityManagerFactory emf : JPA.emfs.values()) { + if (emf.isOpen()) { emf.close(); } } - JPA.emfs.clear(); + JPA.emfs.clear(); } - + @Override public void afterFixtureLoad() { if (JPA.isEnabled()) { diff --git a/framework/src/play/db/jpa/PersistenceUnitInfoImpl.java b/framework/src/play/db/jpa/PersistenceUnitInfoImpl.java new file mode 100644 index 0000000000..1394fe1b0d --- /dev/null +++ b/framework/src/play/db/jpa/PersistenceUnitInfoImpl.java @@ -0,0 +1,141 @@ +package play.db.jpa; + +import org.hibernate.jpa.HibernatePersistenceProvider; + +import javax.persistence.SharedCacheMode; +import javax.persistence.ValidationMode; +import javax.persistence.spi.ClassTransformer; +import javax.persistence.spi.PersistenceUnitInfo; +import javax.persistence.spi.PersistenceUnitTransactionType; +import javax.sql.DataSource; +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +/** + * @author Vlad Mihalcea + * + * Taken from https://vladmihalcea.com/2015/11/26/how-to-bootstrap-hibernate-without-the-persistence-xml-file/ + */ +public class PersistenceUnitInfoImpl implements PersistenceUnitInfo { + + private final String persistenceUnitName; + + private PersistenceUnitTransactionType transactionType = PersistenceUnitTransactionType.RESOURCE_LOCAL; + + private final List managedClassNames; + private final List mappingFileNames; + + private final Properties properties; + + private DataSource jtaDataSource; + + private DataSource nonJtaDataSource; + + public PersistenceUnitInfoImpl(String persistenceUnitName, List managedClassNames, List mappingFileNames, Properties properties) { + this.persistenceUnitName = persistenceUnitName; + this.managedClassNames = managedClassNames; + this.mappingFileNames = mappingFileNames; + this.properties = properties; + } + + @Override + public String getPersistenceUnitName() { + return persistenceUnitName; + } + + @Override + public String getPersistenceProviderClassName() { + return HibernatePersistenceProvider.class.getName(); + } + + @Override + public PersistenceUnitTransactionType getTransactionType() { + return transactionType; + } + + @Override + public DataSource getJtaDataSource() { + return jtaDataSource; + } + + public PersistenceUnitInfoImpl setJtaDataSource(DataSource jtaDataSource) { + this.jtaDataSource = jtaDataSource; + this.nonJtaDataSource = null; + transactionType = PersistenceUnitTransactionType.JTA; + return this; + } + + @Override + public DataSource getNonJtaDataSource() { + return nonJtaDataSource; + } + + public PersistenceUnitInfoImpl setNonJtaDataSource(DataSource nonJtaDataSource) { + this.nonJtaDataSource = nonJtaDataSource; + this.jtaDataSource = null; + transactionType = PersistenceUnitTransactionType.RESOURCE_LOCAL; + return this; + } + + @Override + public List getMappingFileNames() { + return mappingFileNames; + } + + @Override + public List getJarFileUrls() { + return Collections.emptyList(); + } + + @Override + public URL getPersistenceUnitRootUrl() { + return null; + } + + @Override + public List getManagedClassNames() { + return managedClassNames; + } + + @Override + public boolean excludeUnlistedClasses() { + return false; + } + + @Override + public SharedCacheMode getSharedCacheMode() { + return SharedCacheMode.UNSPECIFIED; + } + + @Override + public ValidationMode getValidationMode() { + return ValidationMode.AUTO; + } + + @Override + public Properties getProperties() { + return properties; + } + + @Override + public String getPersistenceXMLSchemaVersion() { + return "2.1"; + } + + @Override + public ClassLoader getClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + + @Override + public void addTransformer(ClassTransformer transformer) { + + } + + @Override + public ClassLoader getNewTempClassLoader() { + return null; + } +} \ No newline at end of file diff --git a/samples-and-tests/forum/app/models/Forum.java b/samples-and-tests/forum/app/models/Forum.java index 558afb73e7..e51903d44d 100644 --- a/samples-and-tests/forum/app/models/Forum.java +++ b/samples-and-tests/forum/app/models/Forum.java @@ -46,7 +46,7 @@ public List getTopics(int page, int pageSize) { } public Post getLastPost() { - return Post.find("topic.forum = ? order by postedAt desc", this).first(); + return Post.find("topic.forum = ?1 order by postedAt desc", this).first(); } } diff --git a/samples-and-tests/forum/app/models/Topic.java b/samples-and-tests/forum/app/models/Topic.java index 2de5867b01..95d6f47d17 100644 --- a/samples-and-tests/forum/app/models/Topic.java +++ b/samples-and-tests/forum/app/models/Topic.java @@ -50,7 +50,7 @@ public Long getVoicesCount() { } public Post getLastPost() { - return Post.find("topic = ? order by postedAt desc", this).first(); + return Post.find("topic = ?1 order by postedAt desc", this).first(); } } diff --git a/samples-and-tests/forum/app/models/User.java b/samples-and-tests/forum/app/models/User.java index 9a323aa4cb..7b859fb7a4 100644 --- a/samples-and-tests/forum/app/models/User.java +++ b/samples-and-tests/forum/app/models/User.java @@ -46,7 +46,7 @@ public boolean isAdmin() { // ~~~~~~~~~~~~ public List getRecentsPosts() { - return Post.find("postedBy = ? order by postedAt", this).fetch(1, 10); + return Post.find("postedBy = ?1 order by postedAt", this).fetch(1, 10); } public Long getPostsCount() { diff --git a/samples-and-tests/just-test-cases/test/SimpleJPATest.java b/samples-and-tests/just-test-cases/test/SimpleJPATest.java index bca2e1fe9c..fb274e50b2 100644 --- a/samples-and-tests/just-test-cases/test/SimpleJPATest.java +++ b/samples-and-tests/just-test-cases/test/SimpleJPATest.java @@ -103,13 +103,13 @@ public void simpleFinders() { User b = users.get(1); // assertEquals(a, User.find("name", "A").first()); - assertEquals(a, User.find("name = ?", "A").first()); - assertEquals(a, User.find("name=?", "A").first()); - assertEquals(b, User.find("name = ? and c = ?", "B", true).first()); - assertNull(User.find("name = ? and c = ?", "B", false).first()); - assertEquals(a, User.find("name like ? and j = ?", "%A%", 45).first()); - assertEquals(b, User.find("name like ? and b = ? and c = ? and l = ? and i = ?", "%B%", false, true, 10000L, 34).first()); - assertNull(User.find("name like ? and b = ? and c = ? and l = ? and i = ?", "%B%", false, true, 10000L, 32).first()); + assertEquals(a, User.find("name = ?1", "A").first()); + assertEquals(a, User.find("name=?1", "A").first()); + assertEquals(b, User.find("name = ?1 and c = ?2", "B", true).first()); + assertNull(User.find("name = ?1 and c = ?2", "B", false).first()); + assertEquals(a, User.find("name like ?1 and j = ?2", "%A%", 45).first()); + assertEquals(b, User.find("name like ?1 and b = ?2 and c = ?3 and l = ?4 and i = ?5", "%B%", false, true, 10000L, 34).first()); + assertNull(User.find("name like ?1 and b = ?2 and c = ?3 and l = ?4 and i = ?5", "%B%", false, true, 10000L, 32).first()); assertEquals(a, User.find("b is null").first()); assertEquals(b, User.find("b is not null").first()); } @@ -120,21 +120,21 @@ public void fullFinders() { User a = users.get(0); User b = users.get(1); // - assertEquals(a, User.find("from User where name = ?", "A").first()); - assertEquals(b, User.find("from User where name = ? and c = ?", "B", true).first()); - assertNull(User.find("from User where name = ? and c = ?", "B", false).first()); - assertEquals(a, User.find("from User where name like ? and j = ?", "%A%", 45).first()); - assertEquals(b, User.find("from User where name like ? and b = ? and c = ? and l = ? and i = ?", "%B%", false, true, 10000L, 34).first()); - assertNull(User.find("from User where name like ? and b = ? and c = ? and l = ? and i = ?", "%B%", false, true, 10000L, 32).first()); + assertEquals(a, User.find("from User where name = ?1", "A").first()); + assertEquals(b, User.find("from User where name = ?1 and c = ?2", "B", true).first()); + assertNull(User.find("from User where name = ?1 and c = ?2", "B", false).first()); + assertEquals(a, User.find("from User where name like ?1 and j = ?2", "%A%", 45).first()); + assertEquals(b, User.find("from User where name like ?1 and b = ?2 and c = ?3 and l = ?4 and i = ?5", "%B%", false, true, 10000L, 34).first()); + assertNull(User.find("from User where name like ?1 and b = ?2 and c = ?3 and l = ?4 and i = ?5", "%B%", false, true, 10000L, 32).first()); assertEquals(a, User.find("from User where b is null").first()); assertEquals(b, User.find("from User where b is not null").first()); - assertEquals(a, User.find("select u from User u where u.name = ?", "A").first()); - assertEquals(b, User.find("select u from User u where u.name = ? and u.c = ?", "B", true).first()); - assertNull(User.find("select u from User u where u.name = ? and u.c = ?", "B", false).first()); - assertEquals(a, User.find("select u from User u where u.name like ? and u.j = ?", "%A%", 45).first()); - assertEquals(b, User.find("select u from User u where u.name like ? and u.b = ? and u.c = ? and u.l = ? and u.i = ?", "%B%", false, true, 10000L, 34).first()); - assertNull(User.find("select u from User u where u.name like ? and u.b = ? and u.c = ? and u.l = ? and u.i = ?", "%B%", false, true, 10000L, 32).first()); + assertEquals(a, User.find("select u from User u where u.name = ?1", "A").first()); + assertEquals(b, User.find("select u from User u where u.name = ?1 and u.c = ?2", "B", true).first()); + assertNull(User.find("select u from User u where u.name = ?1 and u.c = ?2", "B", false).first()); + assertEquals(a, User.find("select u from User u where u.name like ?1 and u.j = ?2", "%A%", 45).first()); + assertEquals(b, User.find("select u from User u where u.name like ?1 and u.b = ?2 and u.c = ?3 and u.l = ?4 and u.i = ?5", "%B%", false, true, 10000L, 34).first()); + assertNull(User.find("select u from User u where u.name like ?1 and u.b = ?2 and u.c = ?3 and u.l = ?4 and u.i = ?5", "%B%", false, true, 10000L, 32).first()); assertEquals(a, User.find("select u from User u where u.b is null").first()); assertEquals(b, User.find("select u from User u where u.b is not null").first()); } @@ -214,5 +214,4 @@ public void testEquals() { assertFalse(userA.equals(a1)); // compare scalar key to array key assertFalse(a1.equals(userA)); // compare array key with scalar key } -} - +} \ No newline at end of file diff --git a/samples-and-tests/yabe/app/models/Post.java b/samples-and-tests/yabe/app/models/Post.java index c64f547e9c..eb6630c219 100644 --- a/samples-and-tests/yabe/app/models/Post.java +++ b/samples-and-tests/yabe/app/models/Post.java @@ -62,7 +62,7 @@ public Post tagItWith(String name) { public static List findTaggedWith(String tag) { return Post.find( - "select distinct p from Post p join p.tags as t where t.name = ?", + "select distinct p from Post p join p.tags as t where t.name = ?1", tag ).fetch(); }