ฉันได้พบวิธีแก้ไขปัญหานี้สองสามข้อ
ใช้หน่วยงานที่แมป (JPA 2.0)
การใช้ JPA 2.0 นั้นไม่สามารถแมปเคียวรีดั้งเดิมกับ POJO ได้สามารถทำได้กับเอนทิตีเท่านั้น
ตัวอย่างเช่น
Query query = em.createNativeQuery("SELECT name,age FROM jedi_table", Jedi.class);
@SuppressWarnings("unchecked")
List<Jedi> items = (List<Jedi>) query.getResultList();
แต่ในกรณีนี้Jedi
ต้องเป็นคลาสเอนทิตีที่แมป
อีกทางเลือกหนึ่งในการหลีกเลี่ยงคำเตือนที่ไม่ถูกตรวจสอบที่นี่คือการใช้เคียวรีเนทีฟที่มีชื่อ ดังนั้นถ้าเราประกาศเคียวรีดั้งเดิมในเอนทิตี
@NamedNativeQuery(
name="jedisQry",
query = "SELECT name,age FROM jedis_table",
resultClass = Jedi.class)
จากนั้นเราสามารถทำได้:
TypedQuery<Jedi> query = em.createNamedQuery("jedisQry", Jedi.class);
List<Jedi> items = query.getResultList();
สิ่งนี้ปลอดภัยกว่า แต่เรายังถูก จำกัด ให้ใช้เอนทิตีที่แมป
การทำแผนที่ด้วยตนเอง
วิธีแก้ปัญหาที่ฉันทดลองเล็กน้อย (ก่อนการมาถึงของ JPA 2.1) กำลังทำแผนที่กับตัวสร้าง POJO โดยใช้การสะท้อนเล็กน้อย
public static <T> T map(Class<T> type, Object[] tuple){
List<Class<?>> tupleTypes = new ArrayList<>();
for(Object field : tuple){
tupleTypes.add(field.getClass());
}
try {
Constructor<T> ctor = type.getConstructor(tupleTypes.toArray(new Class<?>[tuple.length]));
return ctor.newInstance(tuple);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
วิธีการนี้โดยทั่วไปใช้อาร์เรย์ tuple (ตามที่ส่งคืนโดยการสืบค้นดั้งเดิม) และแมปกับคลาส POJO ที่จัดไว้ให้โดยค้นหา Constructor ที่มีจำนวนฟิลด์เท่ากันและเป็นประเภทเดียวกัน
จากนั้นเราสามารถใช้วิธีการที่สะดวกสบายเช่น:
public static <T> List<T> map(Class<T> type, List<Object[]> records){
List<T> result = new LinkedList<>();
for(Object[] record : records){
result.add(map(type, record));
}
return result;
}
public static <T> List<T> getResultList(Query query, Class<T> type){
@SuppressWarnings("unchecked")
List<Object[]> records = query.getResultList();
return map(type, records);
}
และเราสามารถใช้เทคนิคนี้ดังนี้:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table");
List<Jedi> jedis = getResultList(query, Jedi.class);
JPA 2.1 ด้วย @SqlResultSetMapping
ด้วยการมาถึงของ JPA 2.1 เราสามารถใช้คำอธิบายประกอบ @SqlResultSetMapping เพื่อแก้ไขปัญหา
เราจำเป็นต้องประกาศการแมปชุดผลลัพธ์บางแห่งในเอนทิตี:
@SqlResultSetMapping(name="JediResult", classes = {
@ConstructorResult(targetClass = Jedi.class,
columns = {@ColumnResult(name="name"), @ColumnResult(name="age")})
})
แล้วเราก็ทำ:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table", "JediResult");
@SuppressWarnings("unchecked")
List<Jedi> samples = query.getResultList();
แน่นอนในกรณีนี้Jedi
ไม่จำเป็นต้องเป็นเอนทิตีที่แมป มันอาจเป็น POJO ปกติ
ใช้การแมป XML
ฉันเป็นหนึ่งในผู้ที่พบว่าการเพิ่มการ@SqlResultSetMapping
บุกรุกเหล่านี้ในเอนทิตี้ของฉันและฉันไม่ชอบคำจำกัดความของการสืบค้นที่มีชื่อภายในเอนทิตีดังนั้นฉันก็ทำทั้งหมดนี้ในMETA-INF/orm.xml
ไฟล์:
<named-native-query name="GetAllJedi" result-set-mapping="JediMapping">
<query>SELECT name,age FROM jedi_table</query>
</named-native-query>
<sql-result-set-mapping name="JediMapping">
<constructor-result target-class="org.answer.model.Jedi">
<column name="name" class="java.lang.String"/>
<column name="age" class="java.lang.Integer"/>
</constructor-result>
</sql-result-set-mapping>
และนั่นคือคำตอบทั้งหมดที่ฉันรู้ สองอันสุดท้ายเป็นวิธีเหมาะสมที่สุดหากเราสามารถใช้ JPA 2.1