Skip to content

Commit a2c6776

Browse files
committed
HHH-11981 - Fix QueryException thrown for association queries using EntitiesModifiedAtRevision queries.
1 parent 2977d8f commit a2c6776

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed

hibernate-envers/src/main/java/org/hibernate/envers/query/internal/impl/EntitiesModifiedAtRevisionQuery.java

+8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package org.hibernate.envers.query.internal.impl;
88

9+
import java.util.Collection;
910
import java.util.List;
1011

1112
import org.hibernate.envers.boot.internal.EnversService;
@@ -15,6 +16,8 @@
1516
import org.hibernate.envers.query.criteria.AuditCriterion;
1617
import org.hibernate.query.Query;
1718

19+
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.REVISION_PARAMETER;
20+
1821
/**
1922
* In comparison to {@link EntitiesAtRevisionQuery} this query returns an empty collection if an entity
2023
* of a certain type has not been changed in a given revision.
@@ -74,6 +77,11 @@ public List list() {
7477
}
7578

7679
Query query = buildQuery();
80+
// add named parameter (used for ValidityAuditStrategy and association queries)
81+
Collection<String> params = query.getParameterMetadata().getNamedParameterNames();
82+
if ( params.contains( REVISION_PARAMETER ) ) {
83+
query.setParameter( REVISION_PARAMETER, revision );
84+
}
7785
List queryResult = query.list();
7886
return applyProjections( queryResult, revision );
7987
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.envers.test.integration.query;
8+
9+
import java.util.List;
10+
11+
import javax.persistence.Entity;
12+
import javax.persistence.Id;
13+
import javax.persistence.ManyToOne;
14+
import javax.persistence.criteria.JoinType;
15+
16+
import org.hibernate.envers.Audited;
17+
import org.hibernate.envers.query.AuditEntity;
18+
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
19+
import org.hibernate.envers.test.Priority;
20+
import org.junit.Test;
21+
22+
import org.hibernate.testing.TestForIssue;
23+
24+
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
25+
import static org.junit.Assert.assertEquals;
26+
27+
/**
28+
* @author Chris Cranford
29+
*/
30+
@TestForIssue(jiraKey = "HHH-11981")
31+
public class AssociationEntitiesModifiedQueryTest extends BaseEnversJPAFunctionalTestCase {
32+
@Entity(name = "TemplateType")
33+
@Audited(withModifiedFlag = true)
34+
public static class TemplateType {
35+
@Id
36+
private Integer id;
37+
private String name;
38+
39+
TemplateType() {
40+
this( null, null );
41+
}
42+
43+
TemplateType(Integer id, String name) {
44+
this.id = id;
45+
this.name = name;
46+
}
47+
48+
public Integer getId() {
49+
return id;
50+
}
51+
52+
public void setId(Integer id) {
53+
this.id = id;
54+
}
55+
56+
public String getName() {
57+
return name;
58+
}
59+
60+
public void setName(String name) {
61+
this.name = name;
62+
}
63+
}
64+
65+
@Entity(name = "Template")
66+
@Audited(withModifiedFlag = true)
67+
public static class Template {
68+
@Id
69+
private Integer id;
70+
private String name;
71+
@ManyToOne
72+
private TemplateType templateType;
73+
74+
Template() {
75+
this( null, null, null );
76+
}
77+
78+
Template(Integer id, String name, TemplateType type) {
79+
this.id = id;
80+
this.name = name;
81+
this.templateType = type;
82+
}
83+
84+
public Integer getId() {
85+
return id;
86+
}
87+
88+
public void setId(Integer id) {
89+
this.id = id;
90+
}
91+
92+
public String getName() {
93+
return name;
94+
}
95+
96+
public void setName(String name) {
97+
this.name = name;
98+
}
99+
100+
public TemplateType getTemplateType() {
101+
return templateType;
102+
}
103+
104+
public void setTemplateType(TemplateType templateType) {
105+
this.templateType = templateType;
106+
}
107+
}
108+
109+
@Override
110+
protected Class<?>[] getAnnotatedClasses() {
111+
return new Class<?>[] { TemplateType.class, Template.class };
112+
}
113+
114+
@Test
115+
@Priority(10)
116+
public void initData() {
117+
// Revision 1
118+
doInJPA( this::entityManagerFactory, entityManager -> {
119+
final TemplateType type1 = new TemplateType( 1, "Type1" );
120+
final TemplateType type2 = new TemplateType( 2, "Type2" );
121+
final Template template = new Template( 1, "Template1", type1 );
122+
entityManager.persist( type1 );
123+
entityManager.persist( type2 );
124+
entityManager.persist( template );
125+
} );
126+
127+
// Revision 2
128+
doInJPA( this::entityManagerFactory, entityManager -> {
129+
final TemplateType type = entityManager.find( TemplateType.class, 2 );
130+
final Template template = entityManager.find( Template.class, 1 );
131+
template.setTemplateType( type );
132+
entityManager.merge( template );
133+
} );
134+
135+
// Revision 3
136+
doInJPA( this::entityManagerFactory, entityManager -> {
137+
final Template template = entityManager.find( Template.class, 1 );
138+
entityManager.remove( template );
139+
} );
140+
}
141+
142+
@Test
143+
public void testEntitiesModifiedAtRevision1WithAssociationQueries() {
144+
doInJPA( this::entityManagerFactory, entityManager -> {
145+
List results = getEntitiesModifiedAtRevisionUsingAssociationQueryResults( 1 );
146+
assertEquals( 1, results.size() );
147+
assertEquals( "Type1", ( (TemplateType) results.get( 0 ) ).getName() );
148+
} );
149+
}
150+
151+
@Test
152+
public void testEntitiesModifiedAtRevision2WithAssociationQueries() {
153+
doInJPA( this::entityManagerFactory, entityManager -> {
154+
List results = getEntitiesModifiedAtRevisionUsingAssociationQueryResults( 2 );
155+
assertEquals( 1, results.size() );
156+
assertEquals( "Type2", ( (TemplateType) results.get( 0 ) ).getName() );
157+
} );
158+
}
159+
160+
@Test
161+
public void testEntitiesModifiedAtRevision3WithAssociationQueries() {
162+
doInJPA( this::entityManagerFactory, entityManager -> {
163+
List results = getEntitiesModifiedAtRevisionUsingAssociationQueryResults( 3 );
164+
assertEquals( 0, results.size() );
165+
} );
166+
}
167+
168+
private List getEntitiesModifiedAtRevisionUsingAssociationQueryResults(Number revision) {
169+
// Without fix HHH-11981, throw org.hibernate.QueryException - Parameter not bound : revision
170+
return getAuditReader().createQuery()
171+
.forEntitiesModifiedAtRevision( Template.class, revision )
172+
.traverseRelation( "templateType", JoinType.INNER )
173+
.addProjection( AuditEntity.selectEntity( false ) )
174+
.up()
175+
.add( AuditEntity.property( "templateType" ).hasChanged() )
176+
.getResultList();
177+
}
178+
}

0 commit comments

Comments
 (0)