diff --git a/core/src/main/java/google/registry/model/index/ForeignKeyIndex.java b/core/src/main/java/google/registry/model/index/ForeignKeyIndex.java index b4d4d386c..9ed7960b7 100644 --- a/core/src/main/java/google/registry/model/index/ForeignKeyIndex.java +++ b/core/src/main/java/google/registry/model/index/ForeignKeyIndex.java @@ -228,8 +228,7 @@ public abstract class ForeignKeyIndex extends BackupGroup tm().transact( () -> jpaTm() - .getEntityManager() - .createQuery( + .query( CriteriaQueryBuilder.create(clazz) .whereFieldIsIn(property, foreignKeys) .build()) diff --git a/core/src/main/java/google/registry/model/reporting/HistoryEntryDao.java b/core/src/main/java/google/registry/model/reporting/HistoryEntryDao.java index a312e68c6..fa888f033 100644 --- a/core/src/main/java/google/registry/model/reporting/HistoryEntryDao.java +++ b/core/src/main/java/google/registry/model/reporting/HistoryEntryDao.java @@ -164,8 +164,7 @@ public class HistoryEntryDao { private static Stream loadHistoryObjectFromSqlByRegistrars( Class historyClass, ImmutableCollection registrarIds) { return jpaTm() - .getEntityManager() - .createQuery( + .query( CriteriaQueryBuilder.create(historyClass) .whereFieldIsIn("clientId", registrarIds) .build()) @@ -189,7 +188,7 @@ public class HistoryEntryDao { return ImmutableList.sortedCopyOf( Comparator.comparing(HistoryEntry::getModificationTime), - jpaTm().getEntityManager().createQuery(criteriaQuery).getResultList()); + jpaTm().query(criteriaQuery).getResultList()); } private static Class getHistoryClassFromParent( @@ -216,8 +215,7 @@ public class HistoryEntryDao { Class historyClass, DateTime afterTime, DateTime beforeTime) { CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder(); return jpaTm() - .getEntityManager() - .createQuery( + .query( CriteriaQueryBuilder.create(historyClass) .where("modificationTime", criteriaBuilder::greaterThanOrEqualTo, afterTime) .where("modificationTime", criteriaBuilder::lessThanOrEqualTo, beforeTime) diff --git a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManager.java b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManager.java index bace8c141..eefc20950 100644 --- a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManager.java +++ b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManager.java @@ -19,6 +19,7 @@ import java.util.function.Supplier; import javax.persistence.EntityManager; import javax.persistence.Query; import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaQuery; /** Sub-interface of {@link TransactionManager} which defines JPA related methods. */ public interface JpaTransactionManager extends TransactionManager { @@ -34,6 +35,9 @@ public interface JpaTransactionManager extends TransactionManager { */ TypedQuery query(String sqlString, Class resultClass); + /** Creates a JPA SQU query for the given criteria query. */ + TypedQuery query(CriteriaQuery criteriaQuery); + /** * Creates a JPA SQL query for the given query string. * diff --git a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java index 51bec35a3..e4ae4981d 100644 --- a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java +++ b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java @@ -69,6 +69,7 @@ import javax.persistence.PersistenceException; import javax.persistence.Query; import javax.persistence.TemporalType; import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaQuery; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.SingularAttribute; import org.joda.time.DateTime; @@ -127,6 +128,11 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager { return new DetachingTypedQuery(getEntityManager().createQuery(sqlString, resultClass)); } + @Override + public TypedQuery query(CriteriaQuery criteriaQuery) { + return new DetachingTypedQuery(getEntityManager().createQuery(criteriaQuery)); + } + @Override public Query query(String sqlString) { return getEntityManager().createQuery(sqlString); diff --git a/core/src/main/java/google/registry/rdap/RdapDomainSearchAction.java b/core/src/main/java/google/registry/rdap/RdapDomainSearchAction.java index ed64d50e6..6f8766bd0 100644 --- a/core/src/main/java/google/registry/rdap/RdapDomainSearchAction.java +++ b/core/src/main/java/google/registry/rdap/RdapDomainSearchAction.java @@ -589,8 +589,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase { cursorString.get()); } jpaTm() - .getEntityManager() - .createQuery(queryBuilder.build()) + .query(queryBuilder.build()) .getResultStream() .filter(this::isAuthorized) .forEach( diff --git a/core/src/main/java/google/registry/rdap/RdapSearchActionBase.java b/core/src/main/java/google/registry/rdap/RdapSearchActionBase.java index 5cf4d5225..c01d470f2 100644 --- a/core/src/main/java/google/registry/rdap/RdapSearchActionBase.java +++ b/core/src/main/java/google/registry/rdap/RdapSearchActionBase.java @@ -202,11 +202,7 @@ public abstract class RdapSearchActionBase extends RdapActionBase { desiredRegistrar.get()); } List queryResult = - jpaTm() - .getEntityManager() - .createQuery(builder.build()) - .setMaxResults(querySizeLimit) - .getResultList(); + jpaTm().query(builder.build()).setMaxResults(querySizeLimit).getResultList(); if (checkForVisibility) { return filterResourcesByVisibility(queryResult, querySizeLimit); } else { diff --git a/core/src/test/java/google/registry/persistence/transaction/CriteriaQueryBuilderTest.java b/core/src/test/java/google/registry/persistence/transaction/CriteriaQueryBuilderTest.java index 728d6d260..e2ea4a339 100644 --- a/core/src/test/java/google/registry/persistence/transaction/CriteriaQueryBuilderTest.java +++ b/core/src/test/java/google/registry/persistence/transaction/CriteriaQueryBuilderTest.java @@ -59,8 +59,7 @@ class CriteriaQueryBuilderTest { .transact( () -> jpaTm() - .getEntityManager() - .createQuery( + .query( CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class) .build()) .getResultList())) @@ -77,10 +76,11 @@ class CriteriaQueryBuilderTest { CriteriaQuery query = CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class) .where( - "data", jpaTm().getEntityManager().getCriteriaBuilder()::equal, + "data", + jpaTm().getEntityManager().getCriteriaBuilder()::equal, "zztz") .build(); - return jpaTm().getEntityManager().createQuery(query).getResultList(); + return jpaTm().query(query).getResultList(); }); assertThat(result).containsExactly(entity2); } @@ -96,7 +96,7 @@ class CriteriaQueryBuilderTest { .where( "data", jpaTm().getEntityManager().getCriteriaBuilder()::like, "a%") .build(); - return jpaTm().getEntityManager().createQuery(query).getResultList(); + return jpaTm().query(query).getResultList(); }); assertThat(result).containsExactly(entity3); } @@ -112,7 +112,7 @@ class CriteriaQueryBuilderTest { .where( "data", jpaTm().getEntityManager().getCriteriaBuilder()::like, "%a%") .build(); - return jpaTm().getEntityManager().createQuery(query).getResultList(); + return jpaTm().query(query).getResultList(); }); assertThat(result).containsExactly(entity1, entity3).inOrder(); } @@ -132,7 +132,7 @@ class CriteriaQueryBuilderTest { .where( "data", jpaTm().getEntityManager().getCriteriaBuilder()::like, "%t%") .build(); - return jpaTm().getEntityManager().createQuery(query).getResultList(); + return jpaTm().query(query).getResultList(); }); assertThat(result).containsExactly(entity1); } @@ -147,7 +147,7 @@ class CriteriaQueryBuilderTest { CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class) .whereFieldIsIn("data", ImmutableList.of("aaa", "bbb")) .build(); - return jpaTm().getEntityManager().createQuery(query).getResultList(); + return jpaTm().query(query).getResultList(); }); assertThat(result).containsExactly(entity3).inOrder(); } @@ -162,7 +162,7 @@ class CriteriaQueryBuilderTest { CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class) .whereFieldIsIn("data", ImmutableList.of("aaa", "bbb", "data")) .build(); - return jpaTm().getEntityManager().createQuery(query).getResultList(); + return jpaTm().query(query).getResultList(); }); assertThat(result).containsExactly(entity1, entity3).inOrder(); } @@ -179,7 +179,7 @@ class CriteriaQueryBuilderTest { .where( "data", jpaTm().getEntityManager().getCriteriaBuilder()::like, "%a%") .build(); - return jpaTm().getEntityManager().createQuery(query).getResultList(); + return jpaTm().query(query).getResultList(); }); assertThat(result).containsExactly(entity3, entity1).inOrder(); } @@ -194,7 +194,7 @@ class CriteriaQueryBuilderTest { CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class) .orderByDesc("data") .build(); - return jpaTm().getEntityManager().createQuery(query).getResultList(); + return jpaTm().query(query).getResultList(); }); assertThat(result).containsExactly(entity2, entity1, entity3).inOrder(); } diff --git a/core/src/test/java/google/registry/persistence/transaction/JpaTransactionManagerImplTest.java b/core/src/test/java/google/registry/persistence/transaction/JpaTransactionManagerImplTest.java index 9530b5601..0548df9f5 100644 --- a/core/src/test/java/google/registry/persistence/transaction/JpaTransactionManagerImplTest.java +++ b/core/src/test/java/google/registry/persistence/transaction/JpaTransactionManagerImplTest.java @@ -585,6 +585,29 @@ class JpaTransactionManagerImplTest { .contains("Inserted/updated object reloaded: "); } + @Test + void cqQuery_detaches() { + jpaTm().transact(() -> jpaTm().insertAll(moreEntities)); + jpaTm() + .transact( + () -> + assertThat( + jpaTm() + .getEntityManager() + .contains( + jpaTm() + .query( + CriteriaQueryBuilder.create(TestEntity.class) + .where( + "name", + jpaTm().getEntityManager().getCriteriaBuilder() + ::equal, + "entity1") + .build()) + .getSingleResult())) + .isFalse()); + } + @Test void loadAfterPut_fails() { assertThat(