diff --git a/querydsl-jpa/src/main/java/com/querydsl/jpa/HQLTemplates.java b/querydsl-jpa/src/main/java/com/querydsl/jpa/HQLTemplates.java index 20d216885..1f390de76 100644 --- a/querydsl-jpa/src/main/java/com/querydsl/jpa/HQLTemplates.java +++ b/querydsl-jpa/src/main/java/com/querydsl/jpa/HQLTemplates.java @@ -28,8 +28,9 @@ import com.querydsl.core.types.Ops; * HQLTemplates extends {@link JPQLTemplates} with Hibernate specific extensions * * @author tiwe - * + * @deprecated Most likely you want to use {@link Hibernate5Templates} instead */ +@Deprecated public class HQLTemplates extends JPQLTemplates { private static final QueryHandler QUERY_HANDLER; @@ -86,16 +87,22 @@ public class HQLTemplates extends JPQLTemplates { @Override public boolean wrapElements(Operator operator) { + // For example: JPaIntegration.docoExamples98_12 return wrapElements.contains(operator); } @Override public String getTypeForCast(Class cl) { - return typeNames.get(cl); + String typeName = typeNames.get(cl); + if (typeName == null) { + return super.getTypeForCast(cl); + } + return typeName; } @Override public String getExistsProjection() { + // TODO Required / supported just for Hibernate? return "1"; } diff --git a/querydsl-jpa/src/main/java/com/querydsl/jpa/Hibernate5Templates.java b/querydsl-jpa/src/main/java/com/querydsl/jpa/Hibernate5Templates.java new file mode 100644 index 000000000..54191e652 --- /dev/null +++ b/querydsl-jpa/src/main/java/com/querydsl/jpa/Hibernate5Templates.java @@ -0,0 +1,32 @@ +package com.querydsl.jpa; + +/** + * Hibernate5Templates extends {@link JPQLTemplates} with Hibernate specific extensions + * + * @author Jan-Willem Gmelig Meyling + * + */ +public class Hibernate5Templates extends HQLTemplates { + + public static final Hibernate5Templates DEFAULT = new Hibernate5Templates(); + + public Hibernate5Templates() { + } + + public Hibernate5Templates(char escape) { + super(escape); + } + + @Override + public boolean wrapConstant(Object constant) { + // HHH-6913 is fixed in 5.0, default to JPA behaviour + return false; + } + + @Override + public boolean isWithForOn() { + // Hibernate supports the on-clause since 5.0, and the ON clause is actually mandatory for entity joins + return true; + } + +} diff --git a/querydsl-jpa/src/main/java/com/querydsl/jpa/HibernateHandler.java b/querydsl-jpa/src/main/java/com/querydsl/jpa/HibernateHandler.java index 8787328d4..fcd730dbd 100644 --- a/querydsl-jpa/src/main/java/com/querydsl/jpa/HibernateHandler.java +++ b/querydsl-jpa/src/main/java/com/querydsl/jpa/HibernateHandler.java @@ -15,6 +15,7 @@ package com.querydsl.jpa; import java.util.Iterator; +import javax.persistence.PersistenceException; import javax.persistence.Query; import org.hibernate.ScrollMode; @@ -32,7 +33,7 @@ import com.querydsl.core.types.FactoryExpression; * @author tiwe * */ -class HibernateHandler implements QueryHandler { +public class HibernateHandler implements QueryHandler { @Override public void addEntity(Query query, String alias, Class type) { @@ -52,15 +53,15 @@ class HibernateHandler implements QueryHandler { @SuppressWarnings("unchecked") @Override public CloseableIterator iterate(Query query, FactoryExpression projection) { - if (query instanceof NativeQuery) { - NativeQuery hQuery = (NativeQuery) query; - ScrollableResults results = hQuery.scroll(ScrollMode.FORWARD_ONLY); + try { + org.hibernate.query.Query unwrappedQuery = query.unwrap(org.hibernate.query.Query.class); + ScrollableResults results = unwrappedQuery.scroll(ScrollMode.FORWARD_ONLY); CloseableIterator iterator = new ScrollableResultsIterator(results); if (projection != null) { iterator = new TransformingIterator(iterator, projection); } return iterator; - } else { + } catch (PersistenceException e) { Iterator iterator = query.getResultList().iterator(); if (projection != null) { return new TransformingIterator(iterator, projection); @@ -73,11 +74,11 @@ class HibernateHandler implements QueryHandler { @SuppressWarnings("deprecation") @Override public boolean transform(Query query, FactoryExpression projection) { - if (query instanceof NativeQuery) { + try { ResultTransformer transformer = new FactoryExpressionTransformer(projection); - query.unwrap(NativeQuery.class).setResultTransformer(transformer); + query.unwrap(org.hibernate.query.Query.class).setResultTransformer(transformer); return true; - } else { + } catch (PersistenceException e) { return false; } } diff --git a/querydsl-jpa/src/main/java/com/querydsl/jpa/impl/JPAProvider.java b/querydsl-jpa/src/main/java/com/querydsl/jpa/impl/JPAProvider.java index adaef041d..71fb86b0a 100644 --- a/querydsl-jpa/src/main/java/com/querydsl/jpa/impl/JPAProvider.java +++ b/querydsl-jpa/src/main/java/com/querydsl/jpa/impl/JPAProvider.java @@ -18,7 +18,13 @@ import java.util.Map; import javax.persistence.EntityManager; import com.google.common.collect.Maps; -import com.querydsl.jpa.*; +import com.querydsl.jpa.BatooTemplates; +import com.querydsl.jpa.DataNucleusTemplates; +import com.querydsl.jpa.EclipseLinkTemplates; +import com.querydsl.jpa.HQLTemplates; +import com.querydsl.jpa.Hibernate5Templates; +import com.querydsl.jpa.JPQLTemplates; +import com.querydsl.jpa.OpenJPATemplates; /** * {@code JPAProvider} provides detection of the JPA provider based on the EntityManager instance @@ -39,9 +45,23 @@ public final class JPAProvider { } static { + boolean hibernate5; + + try { + String version = Class.forName("org.hibernate.Session").getPackage().getImplementationVersion(); + String[] versionParts = version.split("\\."); + int major = Integer.parseInt(versionParts[0]); + hibernate5 = major >= 5; + } catch (ClassNotFoundException e) { + hibernate5 = false; + } + + JPQLTemplates hibernateTemplates = hibernate5 ? Hibernate5Templates.DEFAULT : HQLTemplates.DEFAULT; + addMapping("org.batoo.jpa.core.impl.manager.EntityManagerImpl", BatooTemplates.DEFAULT); - addMapping("org.hibernate.Session", HQLTemplates.DEFAULT); - addMapping("org.hibernate.ejb.HibernateEntityManager", HQLTemplates.DEFAULT); + addMapping("org.hibernate.Session", hibernateTemplates); + addMapping("org.hibernate.ejb.HibernateEntityManager", hibernateTemplates); + addMapping("org.hibernate.jpa.HibernateEntityManager", hibernateTemplates); addMapping("org.eclipse.persistence.jpa.JpaEntityManager", EclipseLinkTemplates.DEFAULT); addMapping("org.apache.openjpa.persistence.OpenJPAEntityManager", OpenJPATemplates.DEFAULT); addMapping("org.datanucleus.jpa.EntityManagerImpl", DataNucleusTemplates.DEFAULT); @@ -51,15 +71,23 @@ public final class JPAProvider { templatesByName.put("batoo", BatooTemplates.DEFAULT); templatesByName.put("eclipselink", EclipseLinkTemplates.DEFAULT); templatesByName.put("hibernate", HQLTemplates.DEFAULT); + templatesByName.put("hibernate5", Hibernate5Templates.DEFAULT); templatesByName.put("openjpa", OpenJPATemplates.DEFAULT); templatesByName.put("datanucleus", DataNucleusTemplates.DEFAULT); } public static JPQLTemplates getTemplates(EntityManager em) { - // detect by delegate for (Map.Entry, JPQLTemplates> entry : mappings.entrySet()) { - if (entry.getKey().isAssignableFrom(em.getDelegate().getClass())) { - return entry.getValue(); + Class entityManagerClass = entry.getKey(); + try { + if (entityManagerClass.isInstance(em.unwrap(entityManagerClass))) { + return entry.getValue(); + } + } catch (Exception e) { // The PersistenceException is wrapped in an InvocationException for EclipseLink + // detect by delegate + if (entityManagerClass.isAssignableFrom(em.getDelegate().getClass())) { + return entry.getValue(); + } } } // detect by properties diff --git a/querydsl-jpa/src/test/java/com/querydsl/jpa/HibernateHandlerTest.java b/querydsl-jpa/src/test/java/com/querydsl/jpa/HibernateHandlerTest.java index 6819f7048..8f02b61a6 100644 --- a/querydsl-jpa/src/test/java/com/querydsl/jpa/HibernateHandlerTest.java +++ b/querydsl-jpa/src/test/java/com/querydsl/jpa/HibernateHandlerTest.java @@ -22,9 +22,11 @@ import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; +import static org.hamcrest.Matchers.instanceOf; import static org.hibernate.ScrollMode.FORWARD_ONLY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; public class HibernateHandlerTest { @@ -97,10 +99,11 @@ public class HibernateHandlerTest { ScrollableResultsImplementor scrollableResultsImplementor = createMock(ScrollableResultsImplementor.class); FactoryExpression factoryExpression = createMock(FactoryExpression.class); - expect(nativeQuery.unwrap(NativeQuery.class)).andReturn(nativeQuery); + expect(nativeQuery.unwrap(org.hibernate.query.Query.class)).andReturn(nativeQuery); expect(nativeQuery.scroll(FORWARD_ONLY)).andReturn(scrollableResultsImplementor); + replay(nativeQuery); - assertEquals(TransformingIterator.class, hibernateHandler.iterate(nativeQuery, factoryExpression).getClass()); + assertThat(hibernateHandler.iterate(nativeQuery, factoryExpression), instanceOf(TransformingIterator.class)); } @Test @@ -109,6 +112,7 @@ public class HibernateHandlerTest { List queryResultList = createMock(List.class); Iterator iterator = createMock(Iterator.class); + expect(query.unwrap(org.hibernate.query.Query.class)).andThrow(new PersistenceException("Cannot unwrap Query")); expect(query.getResultList()).andReturn(queryResultList); expect(queryResultList.iterator()).andReturn(iterator); replay(query); @@ -123,6 +127,7 @@ public class HibernateHandlerTest { List queryResultList = createMock(List.class); Iterator iterator = createMock(Iterator.class); + expect(query.unwrap(org.hibernate.query.Query.class)).andThrow(new PersistenceException("Cannot unwrap Query")); expect(query.getResultList()).andReturn(queryResultList); expect(queryResultList.iterator()).andReturn(iterator); replay(query); @@ -134,7 +139,7 @@ public class HibernateHandlerTest { public void should_transform() { FactoryExpression projection = createMock(FactoryExpression.class); - expect(nativeQuery.unwrap(NativeQuery.class)).andReturn(nativeQuery); + expect(nativeQuery.unwrap(org.hibernate.query.Query.class)).andReturn(nativeQuery); expect(nativeQuery.setResultTransformer(anyObject(ResultTransformer.class))).andReturn(nativeQuery); replay(nativeQuery); diff --git a/querydsl-jpa/src/test/java/com/querydsl/jpa/JPABase.java b/querydsl-jpa/src/test/java/com/querydsl/jpa/JPABase.java index 4a14d6162..c3765498b 100644 --- a/querydsl-jpa/src/test/java/com/querydsl/jpa/JPABase.java +++ b/querydsl-jpa/src/test/java/com/querydsl/jpa/JPABase.java @@ -29,6 +29,7 @@ import javax.persistence.EntityManager; import javax.persistence.FlushModeType; import javax.persistence.LockModeType; +import com.querydsl.core.Tuple; import org.junit.ClassRule; import org.junit.Ignore; import org.junit.Rule; @@ -252,10 +253,10 @@ public class JPABase extends AbstractJPATest implements JPATest { @NoEclipseLink @NoBatooJPA public void createQuery() { - List rows = query().from(cat) + List rows = query().from(cat) .select(cat.id, cat.name).createQuery().getResultList(); - for (Object[] row : rows) { - assertEquals(2, row.length); + for (Tuple row : rows) { + assertEquals(2, row.size()); } } @@ -264,10 +265,10 @@ public class JPABase extends AbstractJPATest implements JPATest { @NoEclipseLink @NoBatooJPA public void createQuery2() { - List rows = query().from(cat) + List rows = query().from(cat) .select(cat.id, cat.name).createQuery().getResultList(); - for (Object[] row : rows) { - assertEquals(2, row.length); + for (Tuple row : rows) { + assertEquals(2, row.size()); } } diff --git a/querydsl-jpa/src/test/java/com/querydsl/jpa/JPAProviderTest.java b/querydsl-jpa/src/test/java/com/querydsl/jpa/JPAProviderTest.java index a84a08fa5..0afa1321a 100644 --- a/querydsl-jpa/src/test/java/com/querydsl/jpa/JPAProviderTest.java +++ b/querydsl-jpa/src/test/java/com/querydsl/jpa/JPAProviderTest.java @@ -38,7 +38,7 @@ public class JPAProviderTest { factory = Persistence.createEntityManagerFactory("h2"); em = factory.createEntityManager(); System.out.println(em.getDelegate().getClass()); - assertEquals(HQLTemplates.DEFAULT, JPAProvider.getTemplates(em)); + assertEquals(Hibernate5Templates.DEFAULT, JPAProvider.getTemplates(em)); } @Test @@ -55,7 +55,7 @@ public class JPAProviderTest { Thread.currentThread().getContextClassLoader(), new Class[]{EntityManager.class}, handler); - assertEquals(HQLTemplates.DEFAULT, JPAProvider.getTemplates(proxy)); + assertEquals(Hibernate5Templates.DEFAULT, JPAProvider.getTemplates(proxy)); } @Test diff --git a/querydsl-jpa/src/test/java/com/querydsl/jpa/JPAQueryFactoryTest.java b/querydsl-jpa/src/test/java/com/querydsl/jpa/JPAQueryFactoryTest.java index 6af8f9a98..e4d3d865f 100644 --- a/querydsl-jpa/src/test/java/com/querydsl/jpa/JPAQueryFactoryTest.java +++ b/querydsl-jpa/src/test/java/com/querydsl/jpa/JPAQueryFactoryTest.java @@ -76,7 +76,8 @@ public class JPAQueryFactoryTest { public void query3() { EasyMock.expect(mock.getEntityManagerFactory()).andReturn(factoryMock); EasyMock.expect(factoryMock.getProperties()).andReturn(properties); - EasyMock.expect(mock.getDelegate()).andReturn(mock).atLeastOnce(); + EasyMock.expect(mock.unwrap(EasyMock.anyObject(Class.class))).andReturn(mock).atLeastOnce(); + EasyMock.replay(mock, factoryMock); queryFactory3.query().from(QAnimal.animal); @@ -104,7 +105,7 @@ public class JPAQueryFactoryTest { public void delete3() { EasyMock.expect(mock.getEntityManagerFactory()).andReturn(factoryMock); EasyMock.expect(factoryMock.getProperties()).andReturn(properties); - EasyMock.expect(mock.getDelegate()).andReturn(mock).atLeastOnce(); + EasyMock.expect(mock.unwrap(EasyMock.anyObject(Class.class))).andReturn(mock).atLeastOnce(); EasyMock.replay(mock, factoryMock); assertNotNull(queryFactory3.delete(QAnimal.animal)); @@ -128,7 +129,7 @@ public class JPAQueryFactoryTest { public void update3() { EasyMock.expect(mock.getEntityManagerFactory()).andReturn(factoryMock); EasyMock.expect(factoryMock.getProperties()).andReturn(properties); - EasyMock.expect(mock.getDelegate()).andReturn(mock).atLeastOnce(); + EasyMock.expect(mock.unwrap(EasyMock.anyObject(Class.class))).andReturn(mock).atLeastOnce(); EasyMock.replay(mock, factoryMock); assertNotNull(queryFactory3.update(QAnimal.animal)); @@ -151,7 +152,7 @@ public class JPAQueryFactoryTest { public void insert3() { EasyMock.expect(mock.getEntityManagerFactory()).andReturn(factoryMock); EasyMock.expect(factoryMock.getProperties()).andReturn(properties); - EasyMock.expect(mock.getDelegate()).andReturn(mock).atLeastOnce(); + EasyMock.expect(mock.unwrap(EasyMock.anyObject(Class.class))).andReturn(mock).atLeastOnce(); EasyMock.replay(mock, factoryMock); assertNotNull(queryFactory3.insert(QAnimal.animal));