OpenJPA 1.2.2, WebSphere 7 (Java EE 5)
I'm trying to map a table to a POJO (Not entity !) using a named-native-query defined in orm.xml:
<named-native-query name="myNativeQuery" result-class="foo.bar.PojoBean">
<query>
SELECT col_one AS colOne, col_two AS colTwo, col_three AS colThree
FROM myTable WHERE id = 42
</query>
</named-native-query>
The PojoBean is a final class with String getter/setters and empty-argument constructor:
public final class PojoBean implements java.io.Serializable {
public PojoBean() {
}
public String getColOne() { ... }
public void setColOne(String colOne) { ... }
...
}
When I create the query:
EntityManager myEntityManager = ...
myEntityManager.createNamedQuery("myNativeQuery");
I got an exception (Formatted for readability):
<openjpa-1.2.2-r422266:898935 nonfatal user error>
org.apache.openjpa.persistence.ArgumentException:
Result type "class foo.bar.PojoBean" does not have any public fields or setter methods for the projection or aggregate result element "null",
nor does it have a generic put(Object,Object) method that can be used,
nor does it have a public constructor that takes the types null.
What does it means ?
More info:
Thank you,
You may want to make sure that the class PojoBean in the deployed JAR actually has the public setters & getters.
I got the same exception. The problem in my case was, that in the persistence.xml the persistence-unit name was wrong. In this case, the excpetion does not exactly point in the correct direction...
The problem is that openJPA does not auto-map underscore_names to cameCaseName, so you have to provide to translate in order to fill your POJO.
In your case col_one => colOne, but openJPA does not know that.
What I did was to implement the generic setter that assigns the value to corresponding field via reflection, with a little help from Google Guava for string transformation:
public void put(Object field, Object value) {
try {
String fieldName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, field.toString());
Field f = this.getClass().getDeclaredField(fieldName);
f.set(this, value);
}
catch(Exception e) {
logger.error("ERROR: " + e.getMessage());
}
}
I must say that I don't like this solution very much, but it work's really good. Is there a way to implement some interface or use an @Annotation to wire this up?
Another way is with an abstract class that every pojo extends. In that case you should go for the setter method because the field could be private.
public abstract class JpaPutter {
public void put(Object field, Object value) {
try {
String methodName = "set" + CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, field.toString());
this.getClass().getMethod(methodName, value.getClass()).invoke(this, value);
}
catch(Exception e) {
throw new RuntimeException(e);
}
}
}