โลกแห่งการพัฒนาสมัยใหม่เต็มไปด้วยข้อกำหนดต่างๆ ที่ออกแบบมาเพื่อทำให้ชีวิตง่ายขึ้น เมื่อรู้เครื่องมือแล้วคุณสามารถเลือกเครื่องมือที่เหมาะสมได้ คุณสามารถทำให้ชีวิตของคุณยากขึ้นได้โดยไม่รู้ การตรวจสอบนี้จะปกปิดความลับเหนือแนวคิดของ JPA - Java Persistence API ฉันหวังว่าหลังจากอ่านแล้วคุณคงอยากจะดำดิ่งลงสู่โลกลึกลับนี้ให้ลึกยิ่งขึ้น
การแนะนำ
อย่างที่เราทราบ งานหลักประการหนึ่งของโปรแกรมคือการจัดเก็บและประมวลผลข้อมูล ในสมัยก่อน ผู้คนเพียงจัดเก็บข้อมูลไว้ในไฟล์ แต่ทันทีที่จำเป็นต้องเข้าถึงการอ่านและแก้ไขพร้อมกัน เมื่อมีการโหลด (เช่น คำขอหลายรายการมาถึงพร้อมกัน) การจัดเก็บข้อมูลเพียงในไฟล์จะกลายเป็นปัญหา สำหรับข้อมูลเพิ่มเติมเกี่ยวกับปัญหาที่ฐานข้อมูลแก้ไขและวิธี ฉันแนะนำให้คุณอ่านบทความ “
โครงสร้างฐานข้อมูลอย่างไร ” ซึ่งหมายความว่าเราตัดสินใจจัดเก็บข้อมูลของเราไว้ในฐานข้อมูล เป็นเวลานานแล้วที่ Java สามารถทำงานกับฐานข้อมูลโดยใช้ JDBC API (การเชื่อมต่อฐานข้อมูล Java) คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ JDBC ได้ที่นี่: “
JDBC หรือจุดเริ่มต้นทั้งหมด ” แต่เวลาผ่านไปและนักพัฒนาแต่ละครั้งต้องเผชิญกับความจำเป็นในการเขียนประเภทเดียวกันและรหัส "การบำรุงรักษา" ที่ไม่จำเป็น (ที่เรียกว่ารหัส Boilerplate) สำหรับการดำเนินการเล็กน้อยในการบันทึกอ็อบเจ็กต์ Java ในฐานข้อมูลและในทางกลับกัน การสร้างอ็อบเจ็กต์ Java โดยใช้ข้อมูลจาก ฐานข้อมูล จากนั้น เพื่อแก้ไขปัญหาเหล่านี้ แนวคิด ORM ก็ถือกำเนิดขึ้น
ORM - การทำแผนที่เชิงวัตถุหรือแปลเป็นภาษารัสเซียในการทำแผนที่เชิงวัตถุสัมพันธ์ เป็นเทคโนโลยีการเขียนโปรแกรมที่เชื่อมโยงฐานข้อมูลกับแนวคิดของภาษาโปรแกรมเชิงวัตถุ เพื่อให้ง่ายขึ้น ORM คือการเชื่อมต่อระหว่างออบเจ็กต์ Java และบันทึกในฐานข้อมูล โดย
![JPA: เทคโนโลยีเบื้องต้น - 2]()
พื้นฐานแล้ว ORM นั้นเป็นแนวคิดที่ว่าออบเจ็กต์ Java สามารถแสดงเป็นข้อมูลในฐานข้อมูลได้ (และในทางกลับกัน) มันถูกรวบรวมไว้ในรูปแบบของข้อกำหนด JPA - Java Persistence API ข้อกำหนดนี้เป็นคำอธิบายของ Java API ที่แสดงแนวคิดนี้อยู่แล้ว ข้อกำหนดระบุเราว่าเราต้องจัดเตรียมเครื่องมือใดบ้าง (เช่น อินเทอร์เฟซใดที่เราสามารถทำงานได้) เพื่อที่จะทำงานตามแนวคิด ORM และวิธีการใช้เงินเหล่านี้ ข้อมูลจำเพาะไม่ได้อธิบายการใช้งานเครื่องมือ ซึ่งทำให้สามารถใช้การใช้งานที่แตกต่างกันสำหรับข้อกำหนดเดียวได้ คุณสามารถทำให้มันง่ายขึ้นและบอกว่าข้อกำหนดนั้นเป็นคำอธิบายของ API ข้อความของข้อกำหนด JPA สามารถพบได้บนเว็บไซต์ Oracle: "
JSR 338: JavaTM Persistence API " ดังนั้น ในการใช้ JPA เราจำเป็นต้องมีการดำเนินการบางอย่างซึ่งเราจะใช้เทคโนโลยี การใช้งาน JPA เรียกอีกอย่างว่าผู้ให้บริการ JPA หนึ่งในการใช้งาน JPA ที่โดดเด่นที่สุดคือ
Hibernate ข้าพเจ้าจึงเสนอให้พิจารณา
การสร้างโครงการ
เนื่องจาก JPA เป็นเรื่องเกี่ยวกับ Java เราจึงต้องมีโปรเจ็กต์ Java เราสามารถสร้างโครงสร้างไดเร็กทอรีด้วยตนเองและเพิ่มไลบรารีที่จำเป็นด้วยตนเอง แต่จะสะดวกกว่าและถูกต้องกว่ามากในการใช้ระบบสำหรับการประกอบโปรเจ็กต์อัตโนมัติ (กล่าวคือ โดยพื้นฐานแล้วนี่เป็นเพียงโปรแกรมที่จะจัดการแอสเซมบลีของโปรเจ็กต์ให้เรา สร้างไดเร็กทอรีเพิ่มไลบรารีที่จำเป็นใน classpath ฯลฯ .) หนึ่งในระบบดังกล่าวคือ Gradle คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ Gradle ได้ที่นี่: "
บทนำโดยย่อเกี่ยวกับ Gradle " ดังที่เราทราบ ฟังก์ชัน Gradle (เช่น สิ่งที่สามารถทำได้) ถูกนำมาใช้โดยใช้ Gradle Plugins ต่างๆ ลองใช้ Gradle และปลั๊กอิน "
Gradle Build Init Plugin " มารันคำสั่งกัน:
gradle init --type java-application
Gradle จะทำโครงสร้างไดเร็กทอรีที่จำเป็นสำหรับเราและสร้างคำอธิบายพื้นฐานของโปรเจ็กต์ในสคริปต์บิล
build.gradle
ด์ ดังนั้นเราจึงมีใบสมัคร เราต้องคิดถึงสิ่งที่เราต้องการอธิบายหรือสร้างโมเดลด้วยแอปพลิเคชันของเรา มาใช้เครื่องมือสร้างโมเดลกัน เช่น
app.quickdatabasediagrams.com ![JPA: เทคโนโลยีเบื้องต้น - 4]()
เราควรพูดว่าสิ่งที่เราอธิบายไปคือ "โมเดลโดเมน" ของเรา โดเมนคือ "สาขาวิชา" โดยทั่วไป โดเมนคือ “การครอบครอง” ในภาษาลาติน ในยุคกลาง เป็นชื่อที่ตั้งให้กับพื้นที่ที่กษัตริย์หรือขุนนางศักดินาเป็นเจ้าของ และในภาษาฝรั่งเศสก็กลายเป็นคำว่า "domaine" ซึ่งแปลว่า "พื้นที่" ดังนั้นเราจึงอธิบาย "โมเดลโดเมน" = "โมเดลหัวเรื่อง" ของเรา องค์ประกอบแต่ละอย่างของโมเดลนี้คือ "แก่นแท้" บางอย่างจากชีวิตจริง ในกรณีของเรา สิ่งเหล่านี้คือเอนทิตี: หมวดหมู่ (
Category
), หัวเรื่อง (
Topic
) มาสร้างแพ็คเกจแยกต่างหากสำหรับเอนทิตี เช่น ด้วยชื่อโมเดล และมาเพิ่มคลาส Java ที่อธิบายเอนทิตีที่นั่น ในโค้ด Java เอนทิตีดังกล่าวจะเป็น
POJO ปกติ ซึ่งอาจมีลักษณะดังนี้:
public class Category {
private Long id;
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
ลองคัดลอกเนื้อหาของชั้นเรียนและสร้างชั้นเรียนโดยการ
Topic
เปรียบเทียบ เขาจะแตกต่างเฉพาะในสิ่งที่เขารู้เกี่ยวกับประเภทที่เขาอยู่ ดังนั้น เรามาเพิ่ม
Topic
ฟิลด์หมวดหมู่และวิธีการทำงานกับฟิลด์นั้นในชั้นเรียน:
private Category category;
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
ตอนนี้เรามีแอปพลิเคชัน Java ที่มีโมเดลโดเมนเป็นของตัวเอง ตอนนี้ได้เวลาเริ่มเชื่อมต่อกับโครงการ JPA แล้ว
เพิ่ม JPA
อย่างที่เราจำได้ JPA หมายความว่าเราจะบันทึกบางสิ่งลงในฐานข้อมูล ดังนั้นเราจึงจำเป็นต้องมีฐานข้อมูล หากต้องการใช้การเชื่อมต่อฐานข้อมูลในโครงการของเรา เราจำเป็นต้องเพิ่มไลบรารีการพึ่งพาเพื่อเชื่อมต่อกับฐานข้อมูล อย่างที่เราจำได้ เราใช้ Gradle ซึ่งสร้างสคริปต์สำหรับ
build.gradle
เรา ในนั้นเราจะอธิบายการพึ่งพาที่โครงการของเราต้องการ การพึ่งพาคือไลบรารีเหล่านั้นซึ่งโค้ดของเราไม่สามารถทำงานได้ เริ่มต้นด้วยคำอธิบายของการพึ่งพาการเชื่อมต่อกับฐานข้อมูล เราทำสิ่งนี้ในลักษณะเดียวกับที่เราจะทำถ้าเราเพิ่งทำงานกับ JDBC:
dependencies {
implementation 'com.h2database:h2:1.4.199'
ตอนนี้เรามีฐานข้อมูลแล้ว ขณะนี้เราสามารถเพิ่มเลเยอร์ให้กับแอปพลิเคชันของเราที่รับผิดชอบในการแมปออบเจ็กต์ Java ของเราเข้ากับแนวคิดฐานข้อมูล (จาก Java ถึง SQL) ดังที่เราจำได้ เราจะใช้การดำเนินการตามข้อกำหนด JPA ที่เรียกว่า Hibernate สำหรับสิ่งนี้:
dependencies {
implementation 'com.h2database:h2:1.4.199'
implementation 'org.hibernate:hibernate-core:5.4.2.Final'
ตอนนี้เราต้องกำหนดค่า JPA หากเราอ่านข้อกำหนดและส่วน "8.1 Persistence Unit" เราจะรู้ว่า Persistence Unit เป็นการผสมผสานระหว่างการกำหนดค่า ข้อมูลเมตา และเอนทิตี และเพื่อให้ JPA ทำงานได้ คุณต้องอธิบาย Persistence Unit อย่างน้อยหนึ่งหน่วยในไฟล์คอนฟิกูเรชัน ซึ่งเรียกว่า
persistence.xml
. ตำแหน่งของมันถูกอธิบายไว้ในบทข้อมูลจำเพาะ "8.2 บรรจุภัณฑ์หน่วยการคงอยู่" ตามส่วนนี้ หากเรามีสภาพแวดล้อม Java SE เราต้องวางไว้ในรูทของไดเร็กทอรี META-INF
มาคัดลอกเนื้อหาจากตัวอย่างที่ให้ไว้ในข้อกำหนด JPA ใน
8.2.1 persistence.xml file
บท " ":
<persistence>
<persistence-unit name="JavaRush">
<description>Persistence Unit For test</description>
<class>hibernate.model.Category</class>
<class>hibernate.model.Topic</class>
</persistence-unit>
</persistence>
แต่นี่ยังไม่เพียงพอ เราต้องบอกว่าใครคือผู้ให้บริการ JPA ของเรา เช่น ผู้ที่ใช้ข้อกำหนด JPA:
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
ตอนนี้เรามาเพิ่มการตั้งค่า (
properties
) บางส่วน (เริ่มต้นด้วย
javax.persistence
) เป็นการกำหนดค่า JPA มาตรฐาน และอธิบายไว้ในข้อกำหนด JPA ในส่วน "คุณสมบัติ 8.2.1.9" การกำหนดค่าบางอย่างเป็นแบบเฉพาะของผู้ให้บริการ (ในกรณีของเรา จะส่งผลต่อ Hibernate ในฐานะผู้ให้บริการ Jpa บล็อกการตั้งค่าของเราจะมีลักษณะดังนี้:
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>
ตอนนี้เรามีการกำหนดค่าที่เข้ากันได้กับ JPA
persistence.xml
มีผู้ให้บริการ JPA Hibernate และมีฐานข้อมูล H2 และยังมี 2 คลาสที่เป็นโมเดลโดเมนของเรา ในที่สุดเรามาทำให้ทั้งหมดนี้ใช้งานได้ ในแค็ตตาล็อก
/test/java
Gradle ของเรากรุณาสร้างเทมเพลตสำหรับการทดสอบหน่วยและเรียกมันว่า AppTest มาใช้กันเถอะ ตามที่ระบุไว้ในบท "7.1 Persistence Contexts" ของข้อกำหนด JPA เอนทิตีในโลก JPA อาศัยอยู่ในพื้นที่ที่เรียกว่า Persistence Context แต่เราไม่ได้ทำงานโดยตรงกับ Persistence Context สำหรับสิ่งนี้ เราใช้
Entity Manager
หรือ "ผู้จัดการเอนทิตี" เขาคือผู้ที่รู้เกี่ยวกับบริบทและสิ่งที่อาศัยอยู่ที่นั่น เราโต้ตอบกับ
Entity Manager
'อ้อม สิ่งที่เหลืออยู่ก็คือต้องเข้าใจว่าเราจะหาสิ่งนี้ได้จากที่ไหน
Entity Manager
? ตามบท "7.2.2 การได้รับ Application-managed Entity Manager" ของข้อกำหนด JPA เราต้องใช้
EntityManagerFactory
. ดังนั้น เรามาลองใช้ข้อกำหนด JPA และยกตัวอย่างจากบท “7.3.2 การได้รับ Entity Manager Factory ในสภาพแวดล้อม Java SE” และจัดรูปแบบในรูปแบบของการทดสอบหน่วยอย่างง่าย:
@Test
public void shouldStartHibernate() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "JavaRush" );
EntityManager entityManager = emf.createEntityManager();
}
การทดสอบนี้จะแสดงข้อผิดพลาด "เวอร์ชัน XSD JPA Persence.xml ที่ไม่รู้จัก" แล้ว เหตุผลก็คือ
persistence.xml
คุณต้องระบุสคีมาที่จะใช้อย่างถูกต้อง ตามที่ระบุไว้ในข้อกำหนด JPA ในส่วน "8.3 Persistence.xml Schema":
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
version="2.2">
นอกจากนี้ลำดับขององค์ประกอบก็มีความสำคัญเช่นกัน จึง
provider
ต้องระบุก่อนที่จะแสดงรายการคลาส หลังจากนี้การทดสอบจะดำเนินการได้สำเร็จ เราได้เสร็จสิ้นการเชื่อมต่อ JPA โดยตรงแล้ว ก่อนที่เราจะไปต่อ เรามาคิดถึงการทดสอบที่เหลือกันดีกว่า การทดสอบแต่ละครั้งของเราจะต้อง
EntityManager
มี ตรวจสอบให้แน่ใจว่าการทดสอบแต่ละครั้งมีการทดสอบของตัวเอง
EntityManager
ตั้งแต่เริ่มต้นการดำเนินการ นอกจากนี้เรายังต้องการให้ฐานข้อมูลใหม่ทุกครั้ง เนื่องจากเราใช้ ตัว เลือก
inmemory
นี้ก็เพียงพอที่จะปิด
EntityManagerFactory
การสร้าง
Factory
เป็นการดำเนินการที่มีราคาแพง แต่สำหรับการทดสอบก็ถือว่าสมเหตุสมผล JUnit ช่วยให้คุณสามารถระบุวิธีการที่จะดำเนินการก่อน (ก่อน) และหลัง (หลัง) การดำเนินการของการทดสอบแต่ละครั้ง:
public class AppTest {
private EntityManager em;
@Before
public void init() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "JavaRush" );
em = emf.createEntityManager();
}
@After
public void close() {
em.getEntityManagerFactory().close();
em.close();
}
ตอนนี้ก่อนที่จะดำเนินการทดสอบใด ๆ จะมีการสร้างอันใหม่
EntityManagerFactory
ซึ่งจะเกี่ยวข้องกับการสร้างฐานข้อมูลใหม่เพราะ
hibernate.hbm2ddl.auto
มีความ
create
หมาย และจากโรงงานแห่งใหม่ เราก็จะได้โรงงาน
EntityManager
ใหม่
เอนทิตี
อย่างที่เราจำได้ ก่อนหน้านี้เราได้สร้างคลาสที่อธิบายโมเดลโดเมนของเรา เราได้กล่าวไปแล้วว่าสิ่งเหล่านี้คือ "สาระสำคัญ" ของเรา นี่คือเอนทิตีที่เราจะจัดการโดย
EntityManager
ใช้ มาเขียนการทดสอบง่ายๆ เพื่อบันทึกสาระสำคัญของหมวดหมู่:
@Test
public void shouldPersistCategory() {
Category cat = new Category();
cat.setTitle("new category");
em.persist(cat);
}
แต่การทดสอบนี้จะไม่ทำงานทันที เพราะ... เราจะได้รับข้อผิดพลาดต่างๆ ที่จะช่วยให้เราเข้าใจว่าเอนทิตีคืออะไร:
-
Unknown entity: hibernate.model.Category
ทำไม Hibernate ถึงไม่เข้าใจว่าCategory
มันคือ อะไร entity
? ประเด็นก็คือเอนทิตีต้องได้รับการอธิบายตามมาตรฐาน JPA
คลาสเอนทิตีจะต้องมีคำอธิบายประกอบด้วยคำอธิบายประกอบ@Entity
ดังที่ระบุไว้ในบท "2.1 คลาสเอนทิตี" ของข้อกำหนด JPA
-
No identifier specified for entity: hibernate.model.Category
เอนทิตีต้องมีตัวระบุเฉพาะที่สามารถใช้เพื่อแยกแยะระเบียนหนึ่งจากอีกระเบียนหนึ่งได้
ตามบท "2.4 คีย์หลักและเอกลักษณ์เอนทิตี" ของข้อกำหนด JPA "ทุกเอนทิตีต้องมีคีย์หลัก" กล่าวคือ ทุกเอนทิตีต้องมี "คีย์หลัก" คีย์หลักดังกล่าวจะต้องระบุโดยคำอธิบายประกอบ@Id
-
ids for this class must be manually assigned before calling save()
บัตรประจำตัวต้องมาจากที่ไหนสักแห่ง สามารถระบุได้ด้วยตนเองหรือสามารถรับได้โดยอัตโนมัติ
ดังนั้น ตามที่ระบุในบท "11.2.3.3 GeneratedValue" และ "11.1.20 GeneratedValue Annotation" เราจึงสามารถระบุคำอธิบายประกอบ@GeneratedValue
ได้
ดังนั้นเพื่อให้คลาสหมวดหมู่กลายเป็นเอนทิตี เราต้องทำการเปลี่ยนแปลงต่อไปนี้:
@Entity
public class Category {
@Id
@GeneratedValue
private Long id;
นอกจากนี้ คำอธิบายประกอบยัง ระบุ ว่า
@Id
ควรใช้อันใด
Access Type
คุณสามารถอ่านเพิ่มเติมเกี่ยวกับประเภทการเข้าถึงได้ในข้อกำหนด JPA ในหัวข้อ "2.3 ประเภทการเข้าถึง" สั้นๆ เลย เพราะ... เราระบุ
@Id
ไว้เหนือฟิลด์ ( ) จาก นั้น
field
ประเภทการเข้าถึงจะเป็นค่าเริ่มต้น
field-based
ไม่ใช่
property-based
ดังนั้นผู้ให้บริการ JPA จะอ่านและจัดเก็บค่าโดยตรงจากฟิลด์ หากเราวางไว้
@Id
เหนือทะเยอทะยานก็
property-based
จะมีการใช้การเข้าถึงเช่น ผ่าน getter และ setter เมื่อรันการทดสอบ เรายังเห็นว่าคำขอใดบ้างที่ถูกส่งไปยังฐานข้อมูล (ขอบคุณตัวเลือก
hibernate.show_sql
) แต่เมื่อบันทึกแล้ว เราไม่เห็น
insert
's เลย ปรากฎว่าเราไม่ได้บันทึกอะไรเลยจริง ๆ เหรอ? JPA ช่วยให้คุณสามารถซิงโครไนซ์บริบทการคงอยู่และฐานข้อมูลโดยใช้วิธีการ
flush
:
entityManager.flush();
แต่ถ้าเราดำเนินการตอนนี้ เราจะได้รับข้อผิดพลาด: ไม่มี
ธุรกรรมอยู่ระหว่างดำเนินการ และตอนนี้ก็ถึงเวลาเรียนรู้ว่า JPA ใช้ธุรกรรมอย่างไร
ธุรกรรม JPA
ดังที่เราจำได้ JPA มีพื้นฐานอยู่บนแนวคิดของบริบทการคงอยู่ นี่คือสถานที่ที่เอนทิตีอาศัยอยู่ และเราจัดการเอนทิตี
EntityManager
ผ่าน เมื่อเราดำเนินการคำสั่ง
persist
เราจะวางเอนทิตีไว้ในบริบท แม่นยำยิ่งขึ้น เราบอก
EntityManager
ว่าสิ่งนี้จำเป็นต้องทำ แต่บริบทนี้เป็นเพียงพื้นที่เก็บข้อมูลบางส่วนเท่านั้น บางครั้งเรียกว่า "แคชระดับแรก" ด้วยซ้ำ แต่จำเป็นต้องเชื่อมต่อกับฐานข้อมูล คำสั่ง
flush
ซึ่งก่อนหน้านี้ล้มเหลวโดยมีข้อผิดพลาด จะซิงโครไนซ์ข้อมูลจากบริบทการคงอยู่กับฐานข้อมูล แต่สิ่งนี้ต้องมีการขนส่งและการขนส่งนี้เป็นธุรกรรม ธุรกรรมใน JPA ได้รับการอธิบายไว้ในส่วน "7.5 ธุรกรรมการควบคุม" ของข้อกำหนด มี API พิเศษสำหรับการใช้ธุรกรรมใน JPA:
entityManager.getTransaction().begin();
entityManager.getTransaction().commit();
เราจำเป็นต้องเพิ่มการจัดการธุรกรรมให้กับโค้ดของเรา ซึ่งทำงานก่อนและหลังการทดสอบ:
@Before
public void init() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "JavaRush" );
em = emf.createEntityManager();
em.getTransaction().begin();
}
@After
public void close() {
if (em.getTransaction().isActive()) {
em.getTransaction().commit();
}
em.getEntityManagerFactory().close();
em.close();
}
หลังจากเพิ่มแล้ว เราจะเห็นนิพจน์ใน SQL ที่ไม่เคยมีมาก่อนในบันทึกการแทรก:
การเปลี่ยนแปลงที่สะสมใน
EntityManager
ธุรกรรมได้รับการคอมมิต (ยืนยันและบันทึก) ในฐานข้อมูล ตอนนี้เราลองค้นหาแก่นแท้ของเรา มาสร้างการทดสอบเพื่อค้นหาเอนทิตีด้วย ID ของมัน:
@Test
public void shouldFindCategory() {
Category cat = new Category();
cat.setTitle("test");
em.persist(cat);
Category result = em.find(Category.class, 1L);
assertNotNull(result);
}
ในกรณีนี้ เราจะได้รับเอนทิตีที่เราบันทึกไว้ก่อนหน้านี้ แต่เราจะไม่เห็นแบบสอบถาม SELECT ในบันทึก และทุกอย่างขึ้นอยู่กับสิ่งที่เราพูด: “ผู้จัดการเอนทิตี โปรดหาเอนทิตีหมวดหมู่ที่มี ID=1 ให้ฉันหน่อย” และผู้จัดการเอนทิตีจะดูในบริบทของมันก่อน (ใช้แคชประเภทหนึ่ง) และหากไม่พบก็จะเข้าไปดูในฐานข้อมูล ควรเปลี่ยน ID เป็น 2 (ไม่มีสิ่งนั้นเราบันทึกเพียง 1 อินสแตนซ์) และเราจะเห็นว่า
SELECT
คำขอปรากฏขึ้น เนื่องจากไม่พบเอนทิตีในบริบทและ
EntityManager
ฐานข้อมูลพยายามค้นหาเอนทิตี มีคำสั่งต่าง ๆ ที่เราสามารถใช้เพื่อควบคุมสถานะของเอนทิตีในบริบท การเปลี่ยนแปลงของเอนทิตีจากสถานะหนึ่งไปอีกสถานะหนึ่งเรียกว่า วงจรชีวิตของเอนทิตี -
lifecycle
.
วงจรชีวิตของเอนทิตี
วงจรชีวิตของเอนทิตีอธิบายไว้ในข้อกำหนด JPA ในบท "3.2 วงจรชีวิตของอินสแตนซ์เอนทิตี" เพราะ เอนทิตีอาศัยอยู่ในบริบทและถูกควบคุมโดย
EntityManager
จากนั้นพวกเขาบอกว่าเอนทิตีถูกควบคุม กล่าวคือ จัดการ มาดูช่วงชีวิตของเอนทิตี:
Category cat = new Category();
cat.setTitle("new category");
entityManager.persist(cat);
entityManager.getTransaction().begin();
entityManager.getTransaction().commit();
entityManager.detach(cat);
Category managed = entityManager.merge(cat);
entityManager.remove(managed);
และนี่คือไดอะแกรมที่จะรวมเข้าด้วยกัน:
การทำแผนที่
ใน JPA เราสามารถอธิบายความสัมพันธ์ของเอนทิตีระหว่างกัน โปรดจำไว้ว่าเราได้ดูความสัมพันธ์ของเอนทิตีระหว่างกันแล้วเมื่อเราจัดการกับโมเดลโดเมนของเรา จากนั้นเราใช้ ทรัพยากร
Quickdatabasediagrams.com :
การสร้างการเชื่อมต่อระหว่างเอนทิตีเรียกว่าการแมปหรือการเชื่อมโยง (การแมปการเชื่อมโยง) ประเภทของสมาคมที่สามารถสร้างได้โดยใช้ JPA มีดังต่อไปนี้:
ลองดูที่เอนทิตี
Topic
ที่อธิบายหัวข้อ เราจะพูดอะไรเกี่ยวกับทัศนคติ
Topic
ต่อ
Category
? หลายคน
Topic
จะอยู่ในหมวดหมู่เดียว
ManyToOne
ดังนั้น เราจึงจำเป็นต้องมีสมาคม มาแสดงความสัมพันธ์นี้ใน JPA:
@ManyToOne
@JoinColumn(name = "category_id")
private Category category;
หากต้องการจดจำคำอธิบายประกอบที่จะใส่ คุณสามารถจำไว้ว่าส่วนสุดท้ายมีหน้าที่รับผิดชอบในฟิลด์ที่ระบุคำอธิบายประกอบด้านบน
ToOne
- ตัวอย่างเฉพาะ
ToMany
- คอลเลกชัน ตอนนี้การเชื่อมต่อของเราเป็นแบบทางเดียว มาทำให้มันเป็นการสื่อสารสองทางกันเถอะ มาเพิ่ม
Category
ความรู้เกี่ยวกับทุกคน
Topic
ที่อยู่ในหมวดหมู่นี้กันดีกว่า ต้องลงท้ายด้วยเพราะ
ToMany
เรามีรายการ
Topic
นั่นคือทัศนคติ "ต่อหลายหัวข้อ" คำถามยังคงอยู่ -
OneToMany
หรือ
ManyToMany
:
คำตอบที่ดีในหัวข้อเดียวกันสามารถอ่านได้ที่นี่: "
อธิบาย ORM oneToMany, manyToMany ความสัมพันธ์เหมือนฉันอายุห้าขวบ " หากหมวดหมู่มีความเกี่ยวข้องกับ
ToMany
หัวข้อ แต่ละหัวข้อเหล่านี้สามารถมีได้เพียงหมวดหมู่เดียวเท่านั้น ก็จะเป็นมิ
One
ฉะนั้น
Many
ดังนั้น
Category
รายการหัวข้อทั้งหมดจะเป็นดังนี้:
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "topic_id")
private Set<Topic> topics = new HashSet<>();
และอย่าลืม
Category
เขียน getter เพื่อรับรายการหัวข้อทั้งหมด:
public Set<Topic> getTopics() {
return this.topics;
}
ความสัมพันธ์แบบสองทิศทางเป็นสิ่งที่ยากมากในการติดตามโดยอัตโนมัติ ดังนั้น JPA จึงส่งต่อความรับผิดชอบนี้ให้กับนักพัฒนา สิ่งนี้มีความหมายสำหรับเราคือ เมื่อเราสร้าง
Topic
ความสัมพันธ์เอนทิตีด้วย
Category
เราต้องตรวจสอบให้แน่ใจว่าข้อมูลมีความสอดคล้องกัน ทำได้ง่ายๆ:
public void setCategory(Category category) {
category.getTopics().add(this);
this.category = category;
}
มาเขียนการทดสอบง่ายๆ เพื่อตรวจสอบ:
@Test
public void shouldPersistCategoryAndTopics() {
Category cat = new Category();
cat.setTitle("test");
Topic topic = new Topic();
topic.setTitle("topic");
topic.setCategory(cat);
em.persist(cat);
}
การทำแผนที่เป็นหัวข้อที่แยกจากกันทั้งหมด วัตถุประสงค์ของการทบทวนนี้คือเพื่อทำความเข้าใจวิธีการบรรลุผลดังกล่าว คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการทำแผนที่ได้ที่นี่:
เจพีคิวแอล
JPA แนะนำเครื่องมือที่น่าสนใจ - การสืบค้นในภาษา Java Persistence Query ภาษานี้คล้ายกับ SQL แต่ใช้โมเดลอ็อบเจ็กต์ Java แทนตาราง SQL ลองดูตัวอย่าง:
@Test
public void shouldPerformQuery() {
Category cat = new Category();
cat.setTitle("query");
em.persist(cat);
Query query = em.createQuery("SELECT c from Category c WHERE c.title = 'query'");
assertNotNull(query.getSingleResult());
}
ดังที่เราเห็น ในแบบสอบถาม เราใช้การอ้างอิงถึงเอนทิตี
Category
ไม่ใช่ตาราง และในด้านของเอนทิตีนี้
title
ด้วย JPQL มีคุณสมบัติที่มีประโยชน์มากมายและสมควรได้รับบทความของตัวเอง รายละเอียดเพิ่มเติมสามารถพบได้ในการตรวจสอบ:
API เกณฑ์
และสุดท้ายนี้ ฉันอยากจะพูดถึง Criteria API JPA แนะนำเครื่องมือสร้างแบบสอบถามแบบไดนามิก ตัวอย่างการใช้ Criteria API:
@Test
public void shouldFindWithCriteriaAPI() {
Category cat = new Category();
em.persist(cat);
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Category> query = cb.createQuery(Category.class);
Root<Category> c = query.from(Category.class);
query.select(c);
List<Category> resultList = em.createQuery(query).getResultList();
assertEquals(1, resultList.size());
}
ตัวอย่างนี้เทียบเท่ากับการดำเนินการตามคำขอ "
SELECT c FROM Category c
"
Criteria APIเป็นเครื่องมือที่มีประสิทธิภาพ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ที่นี่:
บทสรุป
ดังที่เราเห็น JPA มีคุณสมบัติและเครื่องมือมากมาย แต่ละคนต้องใช้ประสบการณ์และความรู้ แม้จะอยู่ในกรอบการทบทวน JPA ก็ไม่สามารถพูดถึงทุกสิ่งได้ ไม่ต้องพูดถึงการดำน้ำแบบละเอียด แต่ฉันหวังว่าหลังจากอ่านแล้ว มันชัดเจนมากขึ้นว่า ORM และ JPA คืออะไร ทำงานอย่างไร และทำอะไรได้บ้าง สำหรับของว่างฉันเสนอวัสดุต่างๆ:
#เวียเชสลาฟ
GO TO FULL VERSION