Skip to content

Commit

Permalink
[#1811] Don't generate root for intermediate from node of correlated …
Browse files Browse the repository at this point in the history
…join path
  • Loading branch information
beikov committed Oct 5, 2023
1 parent 2854cd3 commit 64fa0a9
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1017,15 +1017,20 @@ private JoinResult correlate(JoinResult result, String rootAlias, Expression cor
if (!aliasManager.isAliasAvailable(alias)) {
alias = aliasManager.generateRootAlias(alias);
}
String baseAlias = addRoot(result.baseNode.getEntityType(), alias, false);
JoinNode joinNode = ((JoinAliasInfo) aliasManager.getAliasInfo(baseAlias)).getJoinNode();
joinNode.getAliasInfo().setImplicit(true);
Predicate correlationPredicate = expressionFactory.createBooleanExpression(createCorrelationPredicate(result.baseNode.getEntityType(), result.baseNode.getAliasExpression(), baseAlias), false);
JoinAliasInfo rootAliasInfo = new JoinAliasInfo(alias, alias, true, true, aliasManager);
JoinNode joinNode = JoinNode.createEntityJoinNode(result.baseNode, JoinType.LEFT, result.baseNode.getEntityType(), rootAliasInfo, false);
result.baseNode.addEntityJoin(joinNode);
rootAliasInfo.setJoinNode(joinNode);
explicitJoinNodes.add(joinNode);
// register root alias in aliasManager
aliasManager.registerAliasInfo(rootAliasInfo);

Predicate correlationPredicate = expressionFactory.createBooleanExpression(createCorrelationPredicate(result.baseNode.getEntityType(), result.baseNode.getAliasExpression(), alias), false);
correlationPredicate.accept(joinVisitor);
joinNode.setOnPredicate(new CompoundPredicate(CompoundPredicate.BooleanOperator.AND, correlationPredicate));
if (implicit || !(correlatedAttributeExpr instanceof ArrayExpression)) {
PathExpression pathExpression = new PathExpression();
pathExpression.getExpressions().add(new PropertyExpression(baseAlias));
pathExpression.getExpressions().add(new PropertyExpression(alias));
if (correlatedAttributeExpr instanceof PathExpression) {
pathExpression.getExpressions().addAll(((PathExpression) correlatedAttributeExpr).getExpressions());
} else {
Expand Down Expand Up @@ -2031,6 +2036,14 @@ private boolean renderCorrelationJoinPath(StringBuilder sb, JoinNode joinBase, J
}
whereConjuncts.add(whereSb.toString());
return true;
} else if (!externalRepresentation && !node.isLateral()) {
sb.append(joinBase.getEntityType().getName());
sb.append(" _synthetic_");
sb.append(node.getAlias());
sb.append(" JOIN _synthetic_");
sb.append(node.getAlias());
sb.append('.').append(correlationPath);
return true;
}
} else {
boolean renderAlias = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright 2014 - 2023 Blazebit.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.blazebit.persistence.view.testsuite.limit;

import com.blazebit.persistence.PaginatedCriteriaBuilder;
import com.blazebit.persistence.testsuite.base.jpa.category.NoDatanucleus;
import com.blazebit.persistence.testsuite.base.jpa.category.NoEclipselink;
import com.blazebit.persistence.testsuite.base.jpa.category.NoH2;
import com.blazebit.persistence.testsuite.base.jpa.category.NoHibernate42;
import com.blazebit.persistence.testsuite.base.jpa.category.NoHibernate43;
import com.blazebit.persistence.testsuite.base.jpa.category.NoHibernate50;
import com.blazebit.persistence.testsuite.base.jpa.category.NoHibernate51;
import com.blazebit.persistence.testsuite.base.jpa.category.NoMySQLOld;
import com.blazebit.persistence.testsuite.base.jpa.category.NoOpenJPA;
import com.blazebit.persistence.testsuite.entity.Document;
import com.blazebit.persistence.testsuite.entity.Person;
import com.blazebit.persistence.testsuite.tx.TxVoidWork;
import com.blazebit.persistence.view.ConfigurationProperties;
import com.blazebit.persistence.view.EntityView;
import com.blazebit.persistence.view.EntityViewManager;
import com.blazebit.persistence.view.EntityViewSetting;
import com.blazebit.persistence.view.IdMapping;
import com.blazebit.persistence.view.Limit;
import com.blazebit.persistence.view.Mapping;
import com.blazebit.persistence.view.testsuite.AbstractEntityViewTest;
import com.blazebit.persistence.view.testsuite.limit.model.DocumentLimitView;
import com.blazebit.persistence.view.testsuite.limit.model.PersonLimitJoinExpressionView;
import com.blazebit.persistence.view.testsuite.limit.model.PersonLimitJoinView;
import com.blazebit.persistence.view.testsuite.limit.model.PersonLimitMultisetView;
import com.blazebit.persistence.view.testsuite.limit.model.PersonLimitSelectView;
import com.blazebit.persistence.view.testsuite.limit.model.PersonLimitSubselectView;
import com.blazebit.persistence.view.testsuite.limit.model.PersonLimitView;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import javax.persistence.EntityManager;
import java.util.List;

import static org.junit.Assert.assertEquals;

/**
*
* @author Christian Beikov
* @since 1.5.0
*/
public class LimitOneToManyTest extends AbstractEntityViewTest {

@Override
public void setUpOnce() {
cleanDatabase();
transactional(new TxVoidWork() {
@Override
public void work(EntityManager em) {
Person o1 = new Person("pers1");
Document doc1 = new Document("doc1", o1);
doc1.setAge(10);
Document doc2 = new Document("doc2", o1);
doc2.setAge(5);
Document doc3 = new Document("doc3", o1);
doc3.setAge(10);

em.persist(o1);
em.persist(doc1);
em.persist(doc2);
em.persist(doc3);
}
});
}

@Test
@Category({ NoMySQLOld.class, NoHibernate42.class, NoHibernate43.class, NoHibernate50.class, NoHibernate51.class, NoEclipselink.class, NoDatanucleus.class, NoOpenJPA.class })
// We need a left entity join for this so Hibernate < 5.1 can't be used
// MySQL before 8 didn't support lateral and also don't support correlated LIMIT subqueries in quantified predicates
// EclipseLink doesn't support subqueries in functions which is required for LIMIT
// Datanucleus fails because of a NPE?
// OpenJPA has no function support
public void testLimitJoin() {
EntityViewManager evm = build(SimplePersonView.class, PersonView.class);
PaginatedCriteriaBuilder<PersonView> paginatedCriteriaBuilder = evm.applySetting(
EntityViewSetting.create(PersonView.class, 0, 1),
cbf.create(em, Person.class, "p").orderByDesc("p.id")
);
List<? extends PersonView> list = paginatedCriteriaBuilder.getResultList();
assertEquals(1, list.size());
// assertEquals(1, list.get(0).getOwnedDocuments().size());
// assertEquals("doc2", list.get(0).getOwnedDocuments().get(0).getName());
}

@EntityView(Person.class)
interface SimplePersonView {
@IdMapping
Long getId();
}

@EntityView(Person.class)
interface PersonView extends SimplePersonView {
@Mapping("partnerDocument.contacts")
@Limit(limit = "1", order = "id desc")
SimplePersonView getBiggest();

}
}

0 comments on commit 64fa0a9

Please sign in to comment.