mirror of https://github.com/apache/openjpa.git
OPENJPA-2631: Fix for CriteriaBuilder issue with an @EmbeddedId that contains more than one field. Ported 2.1.x commit to trunk
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1750038 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
5c2bab9cb1
commit
41827d794d
|
@ -18,6 +18,10 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.openjpa.jdbc.kernel.exps;
|
package org.apache.openjpa.jdbc.kernel.exps;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||||
|
import org.apache.openjpa.jdbc.meta.FieldMapping;
|
||||||
import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
||||||
import org.apache.openjpa.jdbc.sql.Select;
|
import org.apache.openjpa.jdbc.sql.Select;
|
||||||
|
|
||||||
|
@ -62,8 +66,31 @@ class EqualExpression
|
||||||
new FilterValueImpl(sel, ctx, bstate.state1, val1),
|
new FilterValueImpl(sel, ctx, bstate.state1, val1),
|
||||||
new FilterValueImpl(sel, ctx, bstate.state2, val2));
|
new FilterValueImpl(sel, ctx, bstate.state2, val2));
|
||||||
} else {
|
} else {
|
||||||
int len = java.lang.Math.min(val1.length(sel, ctx,
|
int lenVal1 = val1.length(sel, ctx, bstate.state1);
|
||||||
bstate.state1), val2.length(sel, ctx, bstate.state2));
|
int lenVal2 = val2.length(sel, ctx, bstate.state2);
|
||||||
|
int len = java.lang.Math.min(lenVal1, lenVal2);
|
||||||
|
|
||||||
|
// OPENJPA-2631: Detect and handle slightly differently the
|
||||||
|
// case where a composite PK is in use. When an equals comparison
|
||||||
|
// is created by CriteriaBuilder, and the comparison is done against
|
||||||
|
// an entity with a composite PK, 'val2' can be either a:
|
||||||
|
// 1) Lit - in this case a Lit is hard coded to return a length of 1.
|
||||||
|
// 2) Param - in this case the metadata is null so length will return 1.
|
||||||
|
// Given this, first look to see if lenVal1 is greater than lenVal2.
|
||||||
|
if (lenVal1 > lenVal2) {
|
||||||
|
// If here, lets get the metadata from val1 and see if its PK
|
||||||
|
// is an embeddable. If so, the length (val1Len) will be the
|
||||||
|
// size of the number of colunns in the PK. Use this length
|
||||||
|
// in order to create an equal expression with the right number
|
||||||
|
// of 'AND' statementes.
|
||||||
|
ClassMapping cm = (ClassMapping) val1.getMetaData();
|
||||||
|
FieldMapping[] fmsPK = cm.getPrimaryKeyFieldMappings();
|
||||||
|
|
||||||
|
if (fmsPK[0].isEmbedded()) {
|
||||||
|
len = lenVal1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
buf.append(" AND ");
|
buf.append(" AND ");
|
||||||
|
|
|
@ -116,10 +116,11 @@ public class Lit
|
||||||
public void appendTo(Select sel, ExpContext ctx, ExpState state,
|
public void appendTo(Select sel, ExpContext ctx, ExpState state,
|
||||||
SQLBuffer sql, int index) {
|
SQLBuffer sql, int index) {
|
||||||
LitExpState lstate = (LitExpState) state;
|
LitExpState lstate = (LitExpState) state;
|
||||||
if (lstate.otherLength > 1)
|
if (lstate.otherLength > 1) {
|
||||||
sql.appendValue(((Object[]) lstate.sqlValue)[index],
|
sql.appendValue(((Object[]) lstate.sqlValue)[index], lstate.getColumn(index));
|
||||||
lstate.getColumn(index));
|
// OPENJPA-2631: Return so as not to go into sql.appendValue a second time below.
|
||||||
else if (_isRaw) {
|
return;
|
||||||
|
} else if (_isRaw) {
|
||||||
int parseType = getParseType();
|
int parseType = getParseType();
|
||||||
if (parseType == Literal.TYPE_ENUM) {
|
if (parseType == Literal.TYPE_ENUM) {
|
||||||
StringBuilder value = new StringBuilder();
|
StringBuilder value = new StringBuilder();
|
||||||
|
|
|
@ -253,6 +253,32 @@ public class ClassMapping
|
||||||
sm = (OpenJPAStateManager) pc.pcGetStateManager();
|
sm = (OpenJPAStateManager) pc.pcGetStateManager();
|
||||||
if (sm == null) {
|
if (sm == null) {
|
||||||
ret = getValueFromUnmanagedInstance(obj, cols, true);
|
ret = getValueFromUnmanagedInstance(obj, cols, true);
|
||||||
|
|
||||||
|
// OPENJPA-2631 start
|
||||||
|
// Check to see if we are dealing with a Embeddable pk. If the PK is an Embeddable, AND IFF the
|
||||||
|
// columns in the Embeddable are greater than 1, we are dealing with a composite primary
|
||||||
|
// key, and as such 'ret' will be an instance of the embeddable, NOT the individual PK values.
|
||||||
|
// Given this, we need to dig deeper and get the individual values of the embeddable key.
|
||||||
|
// On the other hand, if the embeddable only contains one column, 'ret' will be the value of
|
||||||
|
// that column and as such no further digging is necessary.
|
||||||
|
FieldMapping[] fmsPK = this.getPrimaryKeyFieldMappings();
|
||||||
|
List<FieldMapping> fms = getFieldMappings(cols, true);
|
||||||
|
|
||||||
|
// Note that if we are dealing with an embeddable that is an EmbeddableId, the fms.size will
|
||||||
|
// always be 1 (since an EmbeddableId is slightly opaque, we don't have an fms for each field).
|
||||||
|
// If on the other hand we are dealing with an embeddable that is an @IdClass, fms.size will be the
|
||||||
|
// number columns in the @IdClass. Furthermore, when dealing with @IdClass, 'ret' will already
|
||||||
|
// properly contain the column values, therefore no further processing is needed.
|
||||||
|
if (fmsPK[0].isEmbedded() && cols.length > 1 && fms.size() == 1) {
|
||||||
|
// OK, we know this PK is an embeddable. So get the individual field values.
|
||||||
|
Object[] tmpRet = new Object[cols.length];
|
||||||
|
for (int i = 0; i < cols.length; i++) {
|
||||||
|
Joinable join = this.assertJoinable(cols[i]);
|
||||||
|
tmpRet[i] = join.getJoinValue(ret, cols[i], store);
|
||||||
|
}
|
||||||
|
ret = tmpRet;
|
||||||
|
}
|
||||||
|
// OPENJPA-2631 end
|
||||||
} else if (sm.isDetached()) {
|
} else if (sm.isDetached()) {
|
||||||
obj = store.getContext().find(sm.getObjectId(), false, null);
|
obj = store.getContext().find(sm.getObjectId(), false, null);
|
||||||
sm = store.getContext().getStateManager(obj);
|
sm = store.getContext().getStateManager(obj);
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.openjpa.persistence.embed.compositepk;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import javax.persistence.EmbeddedId;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "SUBJECT")
|
||||||
|
public class Subject implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -2529380440548731281L;
|
||||||
|
|
||||||
|
@EmbeddedId
|
||||||
|
private SubjectKey key;
|
||||||
|
|
||||||
|
public Subject() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubjectKey getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(SubjectKey key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((key == null) ? 0 : key.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
Subject other = (Subject) obj;
|
||||||
|
if (key == null) {
|
||||||
|
if (other.key != null)
|
||||||
|
return false;
|
||||||
|
} else if (!key.equals(other.key))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.openjpa.persistence.embed.compositepk;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class SubjectIdClass implements Serializable {
|
||||||
|
|
||||||
|
private Integer subjectNummer;
|
||||||
|
|
||||||
|
private String subjectTypeCode;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((subjectNummer == null) ? 0 : subjectNummer.hashCode());
|
||||||
|
result = prime * result + ((subjectTypeCode == null) ? 0 : subjectTypeCode.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
SubjectIdClass other = (SubjectIdClass) obj;
|
||||||
|
if (subjectNummer == null) {
|
||||||
|
if (other.subjectNummer != null)
|
||||||
|
return false;
|
||||||
|
} else if (!subjectNummer.equals(other.subjectNummer))
|
||||||
|
return false;
|
||||||
|
if (subjectTypeCode == null) {
|
||||||
|
if (other.subjectTypeCode != null)
|
||||||
|
return false;
|
||||||
|
} else if (!subjectTypeCode.equals(other.subjectTypeCode))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.openjpa.persistence.embed.compositepk;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Embeddable;
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
public class SubjectKey implements Serializable {
|
||||||
|
private static final long serialVersionUID = 3714506425307136262L;
|
||||||
|
|
||||||
|
@Column(name = "SUBJECTNUMMER")
|
||||||
|
private Integer subjectNummer;
|
||||||
|
|
||||||
|
@Column(name = "CODE_SUBJECTTYPE")
|
||||||
|
private String subjectTypeCode;
|
||||||
|
|
||||||
|
public SubjectKey() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubjectKey(Integer subjectNummer, String subjectTypeCode) {
|
||||||
|
this.subjectNummer = subjectNummer;
|
||||||
|
this.subjectTypeCode = subjectTypeCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSubjectNummer() {
|
||||||
|
return subjectNummer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubjectNummer(Integer subjectNummer) {
|
||||||
|
this.subjectNummer = subjectNummer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubjectTypeCode() {
|
||||||
|
return subjectTypeCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubjectType(String subjectType) {
|
||||||
|
this.subjectTypeCode = subjectType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((subjectNummer == null) ? 0 : subjectNummer.hashCode());
|
||||||
|
result = prime * result + ((subjectTypeCode == null) ? 0 : subjectTypeCode.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
SubjectKey other = (SubjectKey) obj;
|
||||||
|
if (subjectNummer == null) {
|
||||||
|
if (other.subjectNummer != null)
|
||||||
|
return false;
|
||||||
|
} else if (!subjectNummer.equals(other.subjectNummer))
|
||||||
|
return false;
|
||||||
|
if (subjectTypeCode == null) {
|
||||||
|
if (other.subjectTypeCode != null)
|
||||||
|
return false;
|
||||||
|
} else if (!subjectTypeCode.equals(other.subjectTypeCode))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.openjpa.persistence.embed.compositepk;
|
||||||
|
|
||||||
|
import javax.persistence.metamodel.SingularAttribute;
|
||||||
|
|
||||||
|
@javax.persistence.metamodel.StaticMetamodel
|
||||||
|
(value=org.apache.openjpa.persistence.embed.compositepk.SubjectKey.class)
|
||||||
|
@javax.annotation.Generated
|
||||||
|
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Fri Feb 05 14:31:20 MST 2016")
|
||||||
|
public class SubjectKey_ {
|
||||||
|
public static volatile SingularAttribute<SubjectKey,Integer> subjectNummer;
|
||||||
|
public static volatile SingularAttribute<SubjectKey,String> subjectTypeCode;
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.openjpa.persistence.embed.compositepk;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.IdClass;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "SUBJECT2")
|
||||||
|
@IdClass(SubjectIdClass.class)
|
||||||
|
public class SubjectWithIdClass implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 8038887700493762410L;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "SUBJECTNUMMER")
|
||||||
|
private Integer subjectNummer;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "CODE_SUBJECTTYPE")
|
||||||
|
private String subjectTypeCode;
|
||||||
|
|
||||||
|
public SubjectWithIdClass() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubjectNummer(Integer subjectNummer) {
|
||||||
|
this.subjectNummer = subjectNummer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSubjectNummer() {
|
||||||
|
return subjectNummer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubjectTypeCode(String subjectTypeCode) {
|
||||||
|
this.subjectTypeCode = subjectTypeCode;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubjectTypeCode() {
|
||||||
|
return subjectTypeCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((subjectNummer == null) ? 0 : subjectNummer.hashCode());
|
||||||
|
result = prime * result + ((subjectTypeCode == null) ? 0 : subjectTypeCode.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
SubjectWithIdClass other = (SubjectWithIdClass) obj;
|
||||||
|
if (subjectNummer == null) {
|
||||||
|
if (other.subjectNummer != null)
|
||||||
|
return false;
|
||||||
|
} else if (!subjectNummer.equals(other.subjectNummer))
|
||||||
|
return false;
|
||||||
|
if (subjectTypeCode == null) {
|
||||||
|
if (other.subjectTypeCode != null)
|
||||||
|
return false;
|
||||||
|
} else if (!subjectTypeCode.equals(other.subjectTypeCode))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.openjpa.persistence.embed.compositepk;
|
||||||
|
|
||||||
|
import javax.persistence.metamodel.SingularAttribute;
|
||||||
|
|
||||||
|
@javax.persistence.metamodel.StaticMetamodel
|
||||||
|
(value=org.apache.openjpa.persistence.embed.compositepk.SubjectWithIdClass.class)
|
||||||
|
@javax.annotation.Generated
|
||||||
|
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Fri Feb 05 14:31:20 MST 2016")
|
||||||
|
public class SubjectWithIdClass_ {
|
||||||
|
public static volatile SingularAttribute<SubjectWithIdClass,Integer> subjectNummer;
|
||||||
|
public static volatile SingularAttribute<SubjectWithIdClass,String> subjectTypeCode;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.openjpa.persistence.embed.compositepk;
|
||||||
|
|
||||||
|
import javax.persistence.metamodel.SingularAttribute;
|
||||||
|
|
||||||
|
@javax.persistence.metamodel.StaticMetamodel
|
||||||
|
(value=org.apache.openjpa.persistence.embed.compositepk.Subject.class)
|
||||||
|
@javax.annotation.Generated
|
||||||
|
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Fri Feb 05 14:31:20 MST 2016")
|
||||||
|
public class Subject_ {
|
||||||
|
public static volatile SingularAttribute<Subject,SubjectKey> key;
|
||||||
|
}
|
|
@ -0,0 +1,538 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.openjpa.persistence.embed.compositepk;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.EntityTransaction;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.ParameterExpression;
|
||||||
|
import javax.persistence.criteria.Predicate;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
|
||||||
|
import junit.framework.Assert;
|
||||||
|
|
||||||
|
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
||||||
|
|
||||||
|
public class TestCompositePrimaryKeys extends SingleEMFTestCase {
|
||||||
|
|
||||||
|
// NOTE: There are 3 aspects to the fix to OPENJPA-2631, each being tested in some manner in the test
|
||||||
|
// methods below. The 3 aspects of the fix are:
|
||||||
|
//
|
||||||
|
// 1) Fix in ClassMapping which resolves the reported ClassCastEx.
|
||||||
|
// 2) After #1, things progressed further, but for some CriteriaBuilder tests incorrect SQL was created as follows:
|
||||||
|
// 2.1) An equals expression was created for only one of the columns in the composite PK. To
|
||||||
|
// resolve this a fix was made to class EqualExpression.
|
||||||
|
// 2.2) An extra parameter marker (?) was added to the SQL. To resolve this a fix was made to class Lit.
|
||||||
|
|
||||||
|
protected EntityManager em;
|
||||||
|
private EntityTransaction tx;
|
||||||
|
|
||||||
|
public void setUp() {
|
||||||
|
super.setUp(DROP_TABLES, Subject.class, SubjectKey.class, SubjectWithIdClass.class, Topic.class);
|
||||||
|
|
||||||
|
em = emf.createEntityManager();
|
||||||
|
tx = em.getTransaction();
|
||||||
|
tx.begin();
|
||||||
|
createData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OpenJPA handles this test just fine with or without the fixes of OPENJPA-2631.
|
||||||
|
*/
|
||||||
|
public void testFindUsingFindOnSubjectKey() {
|
||||||
|
|
||||||
|
Subject s = createSubject();
|
||||||
|
|
||||||
|
Subject s2 = em.find(Subject.class, s.getKey());
|
||||||
|
|
||||||
|
verifySubject(s, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OpenJPA handles this test just fine with or without the fixes of OPENJPA-2631. This works,
|
||||||
|
* compared to other tests, because a select is performed on the key class' fields.
|
||||||
|
*/
|
||||||
|
public void testFindUsingEqualsOnObjectJPQL() {
|
||||||
|
Subject s = createSubject();
|
||||||
|
|
||||||
|
TypedQuery<Subject> query = em.createQuery("select distinct s from Subject s where " +
|
||||||
|
"s.key.subjectNummer = :subjectNummer AND s.key.subjectTypeCode = " +
|
||||||
|
":subjectTypeCode", Subject.class);
|
||||||
|
query.setParameter("subjectNummer", s.getKey().getSubjectNummer());
|
||||||
|
query.setParameter("subjectTypeCode", s.getKey().getSubjectTypeCode());
|
||||||
|
|
||||||
|
Subject s2 = query.getSingleResult();
|
||||||
|
|
||||||
|
verifySubject(s, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Just like the previous test, OpenJPA handles this test just fine with or without the
|
||||||
|
* fixes of OPENJPA-2631. This works, compared to other tests, because a select is
|
||||||
|
* performed on the key class' fields. This slight difference in this test compared to the
|
||||||
|
* previous test is that it traverses from Topic to the SubjectKey fields.
|
||||||
|
*/
|
||||||
|
public void testFindUsingJPQLEqualsOnSubjectKeyAttributes() {
|
||||||
|
|
||||||
|
Subject s = createSubject();
|
||||||
|
|
||||||
|
TypedQuery<Topic> query = em.createQuery("select distinct t from Topic t where t.subject.key.subjectNummer = " +
|
||||||
|
":subjectNummer AND t.subject.key.subjectTypeCode = :subjectTypeCode", Topic.class);
|
||||||
|
query.setParameter("subjectNummer", s.getKey().getSubjectNummer());
|
||||||
|
query.setParameter("subjectTypeCode", s.getKey().getSubjectTypeCode());
|
||||||
|
Topic topic = query.getSingleResult();
|
||||||
|
|
||||||
|
verifyResults(topic, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This test results in an EXPECTED exception:
|
||||||
|
*
|
||||||
|
* ArgumentException: An error occurred while parsing the query filter 'select distinct g from Topic g where
|
||||||
|
* t.subject.key = :subjectKey'. Error message: JPQL query does not support conditional expression over embeddable
|
||||||
|
* class. JPQL string: "key".
|
||||||
|
*
|
||||||
|
* The message in the exception tells it all. Per the spec, you can not do a compare on embeddables.
|
||||||
|
*/
|
||||||
|
public void testFindUsingJPQLEqualsOnSubjectKey() {
|
||||||
|
try {
|
||||||
|
em.createQuery("select distinct t from Topic t where t.subject.key = :subjectKey");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// An exception is EXPECTED!
|
||||||
|
Assert.assertTrue(t.getMessage().contains("does not support conditional expression"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prior to the fix #1 (see notes above), this fails on OJ with:
|
||||||
|
*
|
||||||
|
* java.lang.ClassCastException: org.apache.openjpa.persistence.embed.compositepk.SubjectKey cannot be cast to
|
||||||
|
* [Ljava.lang.Object;]
|
||||||
|
* at org.apache.openjpa.jdbc.kernel.exps.Param.appendTo(Param.java:149)
|
||||||
|
*
|
||||||
|
* With fix #1, this test works fine.
|
||||||
|
*/
|
||||||
|
public void testFindSubjectUsingJPQLEqualsOnSubject() {
|
||||||
|
|
||||||
|
Subject s = createSubject();
|
||||||
|
|
||||||
|
TypedQuery<Subject> query = em.createQuery("select s from Subject s where s = :subject", Subject.class);
|
||||||
|
query.setParameter("subject", s);
|
||||||
|
Subject s2 = query.getSingleResult();
|
||||||
|
|
||||||
|
verifySubject(s, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prior to the fix #1 (see notes above), this fails on OJ with:
|
||||||
|
*
|
||||||
|
* java.lang.ClassCastException: org.apache.openjpa.persistence.embed.compositepk.SubjectKey cannot be cast to
|
||||||
|
* [Ljava.lang.Object;]
|
||||||
|
* at org.apache.openjpa.jdbc.kernel.exps.Param.appendTo(Param.java:149)
|
||||||
|
*
|
||||||
|
* With fix #1, this test works fine.
|
||||||
|
*/
|
||||||
|
public void testFindUsingNamedQuery() {
|
||||||
|
|
||||||
|
Subject s = createSubject();
|
||||||
|
|
||||||
|
TypedQuery<Topic> q = em.createNamedQuery("bySubject", Topic.class);
|
||||||
|
|
||||||
|
q.setParameter("subject", s);
|
||||||
|
|
||||||
|
Topic topic = q.getSingleResult();
|
||||||
|
|
||||||
|
verifyResults(topic, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prior to the fix #1 (see notes above), this fails on OJ with:
|
||||||
|
*
|
||||||
|
* java.lang.ClassCastException: org.apache.openjpa.persistence.embed.compositepk.SubjectKey cannot be cast to
|
||||||
|
* [Ljava.lang.Object;]
|
||||||
|
* at org.apache.openjpa.jdbc.kernel.exps.Param.appendTo(Param.java:149)
|
||||||
|
*
|
||||||
|
* With fix #1, this test works fine.
|
||||||
|
*/
|
||||||
|
public void testFindUsingJPQLEqualsOnSubject() {
|
||||||
|
|
||||||
|
Subject s = createSubject();
|
||||||
|
|
||||||
|
TypedQuery<Topic> query =
|
||||||
|
em.createQuery("select distinct t from Topic t where t.subject = :subject", Topic.class);
|
||||||
|
query.setParameter("subject", s);
|
||||||
|
Topic topic = query.getSingleResult();
|
||||||
|
|
||||||
|
verifyResults(topic, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prior to the fix #1 (see notes above), this fails on OJ with:
|
||||||
|
*
|
||||||
|
* java.lang.ClassCastException: org.apache.openjpa.persistence.embed.compositepk.SubjectKey cannot be cast to
|
||||||
|
* [Ljava.lang.Object;]
|
||||||
|
* at org.apache.openjpa.jdbc.kernel.exps.Param.appendTo(Param.java:149)
|
||||||
|
*
|
||||||
|
* With fix #1, the CCEx is avoided/resolved. However, we then got an incorrectly generated SQL as follows:
|
||||||
|
*
|
||||||
|
* SELECT t0.SUBJECTNUMMER, t0.CODE_SUBJECTTYPE FROM SUBJECT t0 WHERE (t0.SUBJECTNUMMER = ?)
|
||||||
|
* optimize for 1 row [params=(int) 1]
|
||||||
|
*
|
||||||
|
* Notice that 't0.CODE_SUBJECTTYPE' is missing. With fix #2.1 this issue is resolved.
|
||||||
|
*
|
||||||
|
* The thing to note (which is different than the test 'findSubjectUsingCriteriaBuilderEquals' below) is that
|
||||||
|
* the Subject is treated as an OpenJPA 'Parameter' (see changes in EqualExpression). The test
|
||||||
|
* 'findSubjectUsingCriteriaBuilderEquals' below causes the Subject to be treated as a Lit. There is
|
||||||
|
* a bug in both cases, with an additional bug for the 'Lit' case.
|
||||||
|
*/
|
||||||
|
public void testFindSubjectUsingCriteriaBuilderEqualsAndParameter() {
|
||||||
|
|
||||||
|
Subject s = createSubject();
|
||||||
|
|
||||||
|
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<Subject> cq = builder.createQuery(Subject.class);
|
||||||
|
|
||||||
|
Root<Subject> subjectRoot = cq.from(Subject.class);
|
||||||
|
cq.select(subjectRoot);
|
||||||
|
|
||||||
|
ParameterExpression<Subject> param1 = builder.parameter(Subject.class, "subject");
|
||||||
|
Predicate subjectPredicate = builder.equal(subjectRoot, param1);
|
||||||
|
|
||||||
|
cq.where(subjectPredicate);
|
||||||
|
|
||||||
|
TypedQuery<Subject> query = em.createQuery(cq);
|
||||||
|
query.setParameter("subject", s);
|
||||||
|
|
||||||
|
Subject s2 = query.getSingleResult();
|
||||||
|
|
||||||
|
verifySubject(s, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prior to the fix #1 (see notes above), this fails on OJ with:
|
||||||
|
*
|
||||||
|
* Caused by: java.lang.ClassCastException: org.apache.openjpa.persistence.embed.compositepk.SubjectKey
|
||||||
|
* cannot be cast to [Ljava.lang.Object;
|
||||||
|
* at org.apache.openjpa.jdbc.kernel.exps.Lit.appendTo(Lit.java:120)
|
||||||
|
*
|
||||||
|
* Notice the exception this time is in 'Lit'. Previous CCEx for the other tests have been in Param.
|
||||||
|
* With fix #1, the CCEx is avoided/resolved. However, we then got an incorrectly generated SQL as follows:
|
||||||
|
*
|
||||||
|
* SELECT t0.SUBJECTNUMMER, t0.CODE_SUBJECTTYPE FROM SUBJECT t0 WHERE (t0.SUBJECTNUMMER = ??)
|
||||||
|
* optimize for 1 row [params=(int) 1, (String) Type]
|
||||||
|
*
|
||||||
|
* Notice that 't0.CODE_SUBJECTTYPE' is missing, and there are two parameter markers. With fix #2.1 and
|
||||||
|
* #2.2, this issue is resolved.
|
||||||
|
*
|
||||||
|
* The other thing to note (which is different than the test 'findSubjectUsingCriteriaBuilderEqualsAndParameter'
|
||||||
|
* above) is that the Subject is treated as an OpenJPA 'Lit' (see changes in EqualExpression). The test
|
||||||
|
* 'findSubjectUsingCriteriaBuilderEqualsAndParameter' above treats the Subject as a Parameter. There is a bug in
|
||||||
|
* both cases, with an additional bug for the 'Lit' case.
|
||||||
|
*/
|
||||||
|
public void testFindSubjectUsingCriteriaBuilderEquals() {
|
||||||
|
|
||||||
|
Subject s = createSubject();
|
||||||
|
|
||||||
|
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<Subject> cq = builder.createQuery(Subject.class);
|
||||||
|
|
||||||
|
Root<Subject> subjectRoot = cq.from(Subject.class);
|
||||||
|
cq.select(subjectRoot);
|
||||||
|
|
||||||
|
Predicate subjectPredicate = builder.equal(subjectRoot, s);
|
||||||
|
|
||||||
|
// Before the fix of JIRA OPENJPA-2631, the following was a way to fix/work around the issue, in
|
||||||
|
// other words, selecting the individual fields of the PK worked fine....I'll leave this here but
|
||||||
|
// commented out for history sake:
|
||||||
|
// Predicate subjectPredicate1 = builder.equal(subjectRoot.get(Subject_.key).get(SubjectKey_.subjectNummer),
|
||||||
|
// subject.getKey().getSubjectNummer());
|
||||||
|
// Predicate subjectPredicate2 = builder.equal(subjectRoot.get(Subject_.key).get(SubjectKey_.subjectTypeCode),
|
||||||
|
// subject.getKey().getSubjectTypeCode());
|
||||||
|
// Predicate subjectPredicate = builder.and(subjectPredicate1,subjectPredicate2);
|
||||||
|
|
||||||
|
cq.where(subjectPredicate);
|
||||||
|
|
||||||
|
TypedQuery<Subject> query = em.createQuery(cq);
|
||||||
|
|
||||||
|
Subject s2 = query.getSingleResult();
|
||||||
|
|
||||||
|
verifySubject(s, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For comparison, this test does the same CriteriaBuilder code on Topic (an entity
|
||||||
|
* with a single PK) as was done in the previous test to make sure it works.
|
||||||
|
*/
|
||||||
|
public void testFindTopicUsingCriteriaBuilderEquals() {
|
||||||
|
|
||||||
|
Topic t = new Topic();
|
||||||
|
t.setId(5);
|
||||||
|
|
||||||
|
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<Topic> cq = builder.createQuery(Topic.class);
|
||||||
|
|
||||||
|
Root<Topic> topicRoot = cq.from(Topic.class);
|
||||||
|
cq.select(topicRoot);
|
||||||
|
|
||||||
|
Predicate topicPredicate = builder.equal(topicRoot, t);
|
||||||
|
cq.where(topicPredicate);
|
||||||
|
|
||||||
|
TypedQuery<Topic> query = em.createQuery(cq);
|
||||||
|
|
||||||
|
Topic topic = query.getSingleResult();
|
||||||
|
|
||||||
|
verifyResults(topic, createSubject());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prior to the fix #1 (see notes above), this fails on OJ with:
|
||||||
|
*
|
||||||
|
* Caused by: java.lang.ClassCastException: org.apache.openjpa.persistence.embed.compositepk.SubjectKey
|
||||||
|
* cannot be cast to [Ljava.lang.Object;
|
||||||
|
* at org.apache.openjpa.jdbc.kernel.exps.Lit.appendTo(Lit.java:120)
|
||||||
|
*
|
||||||
|
* Notice the exception this time is in 'Lit'. Previous CCEx for the other tests have been in Param.
|
||||||
|
* With fix #1, the CCEx is avoided/resolved. However, we then got an incorrectly generated SQL as follows:
|
||||||
|
*
|
||||||
|
* SELECT t0.ID, t1.SUBJECTNUMMER, t1.CODE_SUBJECTTYPE FROM TOPIC t0 LEFT OUTER JOIN SUBJECT t1 ON
|
||||||
|
* t0.SUBJECT_SUBJECTNUMMER =
|
||||||
|
* t1.SUBJECTNUMMER AND t0.SUBJECT_CODE_SUBJECTTYPE = t1.CODE_SUBJECTTYPE WHERE (t0.SUBJECT_SUBJECTNUMMER = ??)
|
||||||
|
* optimize for 1 row [params=(int) 1, (String) Type]
|
||||||
|
*
|
||||||
|
* Notice that 't0.CODE_SUBJECTTYPE' is missing, and there are two parameter markers. With fix #2.1 and
|
||||||
|
* #2.2, this issue is resolved.
|
||||||
|
*/
|
||||||
|
public void testFindUsingCriteriaBuilderEquals() {
|
||||||
|
|
||||||
|
Subject s = createSubject();
|
||||||
|
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<Topic> cq = builder.createQuery(Topic.class);
|
||||||
|
|
||||||
|
Root<Topic> topic = cq.from(Topic.class);
|
||||||
|
cq.select(topic).distinct(true);
|
||||||
|
|
||||||
|
Predicate topicPredicate = builder.equal(topic.get("subject"), s);
|
||||||
|
cq.where(topicPredicate);
|
||||||
|
|
||||||
|
TypedQuery<Topic> query = em.createQuery(cq);
|
||||||
|
Topic t = query.getSingleResult();
|
||||||
|
|
||||||
|
verifyResults(t, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prior to the fix #1 (see notes above), this fails on OJ with:
|
||||||
|
*
|
||||||
|
* Caused by: java.lang.ClassCastException: org.apache.openjpa.persistence.embed.compositepk.SubjectKey
|
||||||
|
* cannot be cast to [Ljava.lang.Object;
|
||||||
|
* at org.apache.openjpa.jdbc.kernel.exps.InExpression.orContains(InExpression.java:178)
|
||||||
|
*
|
||||||
|
* Notice this time the CCEx occurs in InExpression. With fix #1 the issue is resolved.
|
||||||
|
*/
|
||||||
|
public void testFindUsingJPQLInClauseOnSubject() {
|
||||||
|
Subject s = createSubject();
|
||||||
|
SubjectKey key = new SubjectKey(999, "Bla");
|
||||||
|
Subject s2 = new Subject();
|
||||||
|
s2.setKey(key);
|
||||||
|
|
||||||
|
List<Subject> subjectList = new ArrayList<Subject>();
|
||||||
|
subjectList.add(s);
|
||||||
|
subjectList.add(s2);
|
||||||
|
|
||||||
|
TypedQuery<Topic> query = em.createQuery(
|
||||||
|
"select distinct t from Topic t where t.subject in :subjectList", Topic.class);
|
||||||
|
query.setParameter("subjectList", subjectList);
|
||||||
|
Topic t = query.getSingleResult();
|
||||||
|
|
||||||
|
verifyResults(t, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prior to the fix #1 (see notes above), this fails on OJ with:
|
||||||
|
*
|
||||||
|
* Caused by: java.lang.ClassCastException: org.apache.openjpa.persistence.embed.compositepk.SubjectKey
|
||||||
|
* cannot be cast to [Ljava.lang.Object;
|
||||||
|
* at org.apache.openjpa.jdbc.kernel.exps.Lit.appendTo(Lit.java:120)
|
||||||
|
*
|
||||||
|
* Notice the exception this time is in 'Lit'. Previous CCEx for the other tests have been in Param.
|
||||||
|
*
|
||||||
|
* With fix #1, the CCEx is avoided/resolved. However, we then got an incorrectly generated SQL as follows:
|
||||||
|
*
|
||||||
|
* SELECT t0.ID, t1.SUBJECTNUMMER, t1.CODE_SUBJECTTYPE FROM TOPIC t0 LEFT OUTER JOIN SUBJECT t1 ON
|
||||||
|
* t0.SUBJECT_SUBJECTNUMMER =
|
||||||
|
* t1.SUBJECTNUMMER AND t0.SUBJECT_CODE_SUBJECTTYPE = t1.CODE_SUBJECTTYPE WHERE (t0.SUBJECT_SUBJECTNUMMER = ??)
|
||||||
|
* optimize for 1 row [params=(int) 1, (String) Type]
|
||||||
|
*
|
||||||
|
* Notice that 't0.CODE_SUBJECTTYPE' is missing, and there are two parameter markers. With fix #2.1 and
|
||||||
|
* #2.2, this issue is resolved.
|
||||||
|
*/
|
||||||
|
public void testFindUsingCriteriaBuilderInClauseOnSubject() {
|
||||||
|
|
||||||
|
Subject s = createSubject();
|
||||||
|
SubjectKey key = new SubjectKey(999, "Bla");
|
||||||
|
Subject s2 = new Subject();
|
||||||
|
s2.setKey(key);
|
||||||
|
|
||||||
|
List<Subject> subjectList = new ArrayList<Subject>();
|
||||||
|
subjectList.add(s);
|
||||||
|
subjectList.add(s2);
|
||||||
|
|
||||||
|
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<Topic> cq = builder.createQuery(Topic.class);
|
||||||
|
|
||||||
|
Root<Topic> topic = cq.from(Topic.class);
|
||||||
|
cq.select(topic).distinct(true);
|
||||||
|
|
||||||
|
Predicate subjectInSubjectList = topic.get(Topic_.subject).in(subjectList);
|
||||||
|
cq.where(subjectInSubjectList);
|
||||||
|
|
||||||
|
TypedQuery<Topic> query = em.createQuery(cq);
|
||||||
|
Topic t = query.getSingleResult();
|
||||||
|
|
||||||
|
verifyResults(t, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This test works fine with or without the fixes. This was added as a comparison to the case
|
||||||
|
* where an @EmbeddedId is used. In other words, this query selects a Subject which uses
|
||||||
|
* a @IdClass (still considered an embeddable in OpenJPA).
|
||||||
|
*/
|
||||||
|
public void testFindUsingJPQLEqualsOnSubjectWithIdClass() {
|
||||||
|
SubjectWithIdClass s = new SubjectWithIdClass();
|
||||||
|
s.setSubjectNummer(1);
|
||||||
|
s.setSubjectTypeCode("Type");
|
||||||
|
|
||||||
|
TypedQuery<SubjectWithIdClass> query =
|
||||||
|
em.createQuery("select s from SubjectWithIdClass s where s = :subject", SubjectWithIdClass.class);
|
||||||
|
|
||||||
|
query.setParameter("subject", s);
|
||||||
|
SubjectWithIdClass s2 = query.getSingleResult();
|
||||||
|
|
||||||
|
|
||||||
|
Assert.assertNotNull(s2);
|
||||||
|
Assert.assertEquals(s.getSubjectNummer(), s2.getSubjectNummer());
|
||||||
|
Assert.assertEquals(s.getSubjectTypeCode(), s2.getSubjectTypeCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For this test, the CCEx is actually never hit with or without the fixes. However, incorrect
|
||||||
|
* SQL was generated as follows:
|
||||||
|
*
|
||||||
|
* SELECT t0.SUBJECTNUMMER, t0.CODE_SUBJECTTYPE FROM SUBJECT2 t0 WHERE
|
||||||
|
* (t0.SUBJECTNUMMER = ??) optimize for 1 row [params=(int) 1, (String) Type]}
|
||||||
|
*
|
||||||
|
* Notice that 't0.CODE_SUBJECTTYPE' is missing, and there is an extra parameter marker. With
|
||||||
|
* fix #2.1 and #2.2 this issue is resolved.
|
||||||
|
*/
|
||||||
|
public void testFindUsingCriteriaBuilderOnSubjectWithIdClass() {
|
||||||
|
SubjectWithIdClass s = new SubjectWithIdClass();
|
||||||
|
s.setSubjectNummer(1);
|
||||||
|
s.setSubjectTypeCode("Type");
|
||||||
|
|
||||||
|
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<SubjectWithIdClass> cq = builder.createQuery(SubjectWithIdClass.class);
|
||||||
|
|
||||||
|
Root<SubjectWithIdClass> subjectRoot = cq.from(SubjectWithIdClass.class);
|
||||||
|
cq.select(subjectRoot);
|
||||||
|
|
||||||
|
Predicate subjectPredicate = builder.equal(subjectRoot, s);
|
||||||
|
|
||||||
|
cq.where(subjectPredicate);
|
||||||
|
|
||||||
|
TypedQuery<SubjectWithIdClass> query = em.createQuery(cq);
|
||||||
|
|
||||||
|
SubjectWithIdClass s2 = query.getSingleResult();
|
||||||
|
|
||||||
|
Assert.assertNotNull(s2);
|
||||||
|
Assert.assertEquals(s.getSubjectNummer(), s2.getSubjectNummer());
|
||||||
|
Assert.assertEquals(s.getSubjectTypeCode(), s2.getSubjectTypeCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void createData(){
|
||||||
|
Subject s = new Subject();
|
||||||
|
SubjectKey sk = new SubjectKey();
|
||||||
|
sk.setSubjectNummer(1);
|
||||||
|
sk.setSubjectType("Type2");
|
||||||
|
s.setKey(sk);
|
||||||
|
em.persist(s);
|
||||||
|
|
||||||
|
s = new Subject();
|
||||||
|
sk = new SubjectKey();
|
||||||
|
sk.setSubjectNummer(1);
|
||||||
|
sk.setSubjectType("Type");
|
||||||
|
s.setKey(sk);
|
||||||
|
em.persist(s);
|
||||||
|
|
||||||
|
Topic t = new Topic();
|
||||||
|
t.setId(5);
|
||||||
|
t.setSubject(s);
|
||||||
|
em.persist(t);
|
||||||
|
|
||||||
|
SubjectWithIdClass swic = new SubjectWithIdClass();
|
||||||
|
swic.setSubjectNummer(1);
|
||||||
|
swic.setSubjectTypeCode("Type");
|
||||||
|
em.persist(swic);
|
||||||
|
|
||||||
|
swic = new SubjectWithIdClass();
|
||||||
|
swic.setSubjectNummer(1);
|
||||||
|
swic.setSubjectTypeCode("Type2");
|
||||||
|
em.persist(swic);
|
||||||
|
|
||||||
|
em.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Subject createSubject() {
|
||||||
|
SubjectKey key = new SubjectKey(1, "Type");
|
||||||
|
Subject result = new Subject();
|
||||||
|
result.setKey(key);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void verifyResults(Topic topic, Subject s) {
|
||||||
|
Assert.assertNotNull(topic);
|
||||||
|
Assert.assertEquals(new Integer(5), topic.getId());
|
||||||
|
Subject s2 = topic.getSubject();
|
||||||
|
verifySubject(s, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void verifySubject(Subject expected, Subject actual) {
|
||||||
|
Assert.assertNotNull(expected);
|
||||||
|
Assert.assertEquals(expected.getKey().getSubjectNummer(), actual.getKey().getSubjectNummer());
|
||||||
|
Assert.assertEquals(expected.getKey().getSubjectTypeCode(), actual.getKey().getSubjectTypeCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tearDown() {
|
||||||
|
if (tx != null && tx.isActive()) {
|
||||||
|
tx.rollback();
|
||||||
|
tx = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (em != null && em.isOpen()) {
|
||||||
|
em.close();
|
||||||
|
em = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.openjpa.persistence.embed.compositepk;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.NamedQuery;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "TOPIC")
|
||||||
|
@NamedQuery(name = "bySubject", query = "SELECT t FROM Topic t WHERE t.subject = :subject")
|
||||||
|
public class Topic {
|
||||||
|
@Id
|
||||||
|
@Column(name = "ID")
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
|
||||||
|
// @JoinColumns({ @JoinColumn(table = "TOPIC", name = "SUBJECTNUMMER", referencedColumnName = "SUBJECTNUMMER"),
|
||||||
|
// @JoinColumn(table = "TOPIC", name = "CODE_SUBJECTTYPE", referencedColumnName = "CODE_SUBJECTTYPE") })
|
||||||
|
private Subject subject;
|
||||||
|
|
||||||
|
public Topic() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Subject getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubject(Subject subject) {
|
||||||
|
this.subject = subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
||||||
|
result = prime * result + ((subject == null) ? 0 : subject.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
Topic other = (Topic) obj;
|
||||||
|
if (id == null) {
|
||||||
|
if (other.id != null)
|
||||||
|
return false;
|
||||||
|
} else if (!id.equals(other.id))
|
||||||
|
return false;
|
||||||
|
if (subject == null) {
|
||||||
|
if (other.subject != null)
|
||||||
|
return false;
|
||||||
|
} else if (!subject.equals(other.subject))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.openjpa.persistence.embed.compositepk;
|
||||||
|
|
||||||
|
import javax.persistence.metamodel.SingularAttribute;
|
||||||
|
|
||||||
|
@javax.persistence.metamodel.StaticMetamodel
|
||||||
|
(value=org.apache.openjpa.persistence.embed.compositepk.Topic.class)
|
||||||
|
@javax.annotation.Generated
|
||||||
|
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Fri Feb 05 14:31:20 MST 2016")
|
||||||
|
public class Topic_ {
|
||||||
|
public static volatile SingularAttribute<Topic,Integer> id;
|
||||||
|
public static volatile SingularAttribute<Topic,Subject> subject;
|
||||||
|
}
|
Loading…
Reference in New Issue