3.1 บทนำ

สิ่งที่มีประโยชน์อีกอย่าง ที่ฉันอยากจะพูดถึงคือNativeQuery ดังที่คุณทราบแล้ว การใช้ NativeQuery คุณสามารถเขียนข้อความค้นหาใน SQL แบบเนทีฟได้ อย่างไรก็ตาม สิ่งที่น่าสนใจยิ่งกว่าคือ คุณไม่จำเป็นต้องใช้การแมปคลาสเมื่อได้รับผลลัพธ์คิวรี

ฉันจะแสดงให้คุณเห็นตัวอย่าง:

List<Object[]> persons = session.createNativeQuery("SELECT * FROM Person").list();

ในตัวอย่างนี้ เราจะไม่ผ่านคลาสที่ตรงกับแถวผลลัพธ์ของคิวรี แต่เราใช้อาร์เรย์ของออบเจกต์แทน

สิ่งนี้มีประโยชน์หากคุณต้องการเลือกเพียงไม่กี่คอลัมน์ในตาราง ตัวอย่าง:


List<Object[]> persons = session.createNativeQuery("SELECT id, name FROM Person").list();
 
for(Object[] person : persons) {
    Number id = (Number) person[0];
    String name = (String) person[1];
}

สิ่งนี้ค่อนข้างคล้ายกับแนวทาง JDBC เมื่อคุณได้รับวัตถุ ResultSet และอ่านข้อมูลจากแถวของมัน

อย่างไรก็ตาม Hibernate เสนอวิธีต่างๆ เพื่อทำให้สิ่งนี้น่าเชื่อถือมากขึ้น ตัวอย่างเช่น คุณสามารถระบุประเภทของคอลัมน์ที่คุณต้องการลบ ตัวอย่าง:


Query<Object[]> query = session.createNativeQuery("SELECT id, name FROM Person");
query.addScalar("id", StandardBasicTypes.LONG);
query.addScalar("name", StandardBasicTypes.STRING);
List<Object[]> persons = query.list();
 
for(Object[] person : persons) {
    Long id = (Long) person[0];
    String name = (String) person[1];
}

3.2 การแมปเอนทิตี

คุณยังสามารถระบุคลาสที่ Hibernate ควรใช้อย่างชัดเจนเมื่อแยกวิเคราะห์ผลลัพธ์ของNativeQuery สามารถทำได้หลายวิธี


Query<Person> query = session.createNativeQuery("SELECT * FROM Person")
    .addEntity(Person.class);
    .list();

และแน่นอน รูปแบบเก่าที่ดีที่คุณรู้จัก:


Query<Person> query = session.createNativeQuery("SELECT * FROM Person", Person.class).list();

วิธีแรกคือวิธี Hibernate ดั้งเดิม และวิธีที่สองคือวิธี JPA วิธีการของ JPA นั้นสะดวกและรัดกุมกว่า เนื่องจากมาตรฐานนี้ถูกคิดค้นขึ้นหลังจากที่ Hibernate มีอยู่เป็นเวลาหลายปี และไฮเบอร์เนตก็พัฒนาและถูกบังคับให้สนับสนุนแนวทางเก่าเพื่อรักษาความเข้ากันได้กับเวอร์ชันเก่า

ยังไงก็ตาม ด้วยแนวทางของมัน Hibernate ช่วยให้คุณไม่เชื่อมต่อคลาสเดียวกับการแมปผลลัพธ์ของคิวรี แต่มีหลายคลาส ตัวอย่าง:


List<Phone> results = session.createNativeQuery(
    "SELECT {ph.*}, {pr.*}" +
    "FROM Phone ph" +
    "JOIN Person pr ON ph.person_id = pr.id")
.addEntity("ph", Phone.class)
.addJoin("pr", "ph.person")
.list();
 
for (Phone. phone : results) {
           	assertNotNull( phone.getPerson().getName() );
}

สามารถใช้วิธีการนี้โดยใช้NativeQuery เพื่อเพิ่มความเร็วในการเลือกข้อมูลจากฐานข้อมูล หากคุณทราบว่าไม่ต้องการบางคอลัมน์ คุณสามารถเว้นไว้ในคำขอได้

คุณยังสามารถโหลดเอน ทิตีย่อยทั้งหมดในคราวเดียว แม้ว่าไฮเบอร์เนตต้องการใช้แคชหรือ กลไก LazyLoading นอกจากนี้ เอนทิตีย่อยของคุณอาจมีหลายคอลัมน์ในฐานข้อมูล และคุณสามารถเลือกได้เพียงบางคอลัมน์เท่านั้น

3.3 การทำแผนที่ DTO

ไฮเบอร์เนตยังอนุญาตให้คุณใช้คลาสที่ไม่ใช่เอนทิตีเพื่อแมปผลลัพธ์ คลาสที่ไม่มีคำอธิบายประกอบและไม่ได้แมปกับตารางใดๆ

ตัวอย่าง:

public class PersonSummaryDTO {
    private Number id;
    private String name;

    public Number getId() {
    	return id;
    }

    public void setId(Number id) {
    	this.id = id;
    }

    public String getName() {
    	return name;
    }

    public void setName(String name) {
    	this.name = name;
	}
}

List<PersonSummaryDTO> dtos = session.createNativeQuery(
    "SELECT p.id as \"id\", p.name as \"name\" FROM Person p")
.setResultTransformer(Transformers.aliasToBean(PersonSummaryDTO.class) )
.list();

เนื่องจากไม่มีคำอธิบายประกอบในคลาส PersonSummaryDTO ชื่อของคอลัมน์ในแบบสอบถาม SQL จะต้องตรงกับชื่อฟิลด์ของคลาส PersonSummaryDTO ทุกประการ

สิ่งนี้มีประโยชน์มากหากคุณกำลังอ่านข้อมูลจากฐานข้อมูลภายนอกที่แอปพลิเคชันของคุณเชื่อมต่อในโหมดอ่านอย่างเดียวเท่านั้น นั่นคือ คุณได้รับสิทธิ์เข้าถึงตารางที่มีมากกว่า 50 คอลัมน์ ซึ่งเก็บข้อมูลไว้ในรูปแบบที่ไม่ปกติเพื่อเพิ่มความเร็วในการเลือก

หรือสมมติว่ามีคนตัดสินใจจัดเก็บลำดับชั้นของชั้นเรียนไว้ในตารางเดียว และในห้าปีตารางนี้ก็เติบโตขึ้นมากจนปีศาจจะหักขาของเขา คุณต้องเลือกสองสามคอลัมน์จากตารางนี้ (รหัสและชื่อผู้ใช้) และมอบให้กับลูกค้า

ฉันคิดว่าคุณเข้าใจ แต่ถ้าคุณต้องการเจาะลึกในหัวข้อนี้ คุณสามารถอ่านเพิ่มเติมได้ที่ลิงค์:

แบบสอบถาม SQL ดั้งเดิม