Thế giới phát triển hiện đại có đầy đủ các thông số kỹ thuật khác nhau được thiết kế để giúp cuộc sống dễ dàng hơn. Biết các công cụ, bạn có thể chọn một công cụ phù hợp. Nếu không biết, bạn có thể khiến cuộc sống của mình trở nên khó khăn hơn. Đánh giá này sẽ vén bức màn bí mật về khái niệm JPA - Java Persistence API. Tôi hy vọng rằng sau khi đọc bạn sẽ muốn đi sâu hơn nữa vào thế giới bí ẩn này.
Giới thiệu
Như chúng ta đã biết, một trong những nhiệm vụ chính của chương trình là lưu trữ và xử lý dữ liệu. Ngày xưa, mọi người chỉ lưu trữ dữ liệu trong các tập tin. Nhưng ngay khi cần quyền truy cập đọc và chỉnh sửa đồng thời, khi có tải (tức là có nhiều yêu cầu đến cùng lúc), việc lưu trữ dữ liệu đơn giản trong tệp sẽ trở thành một vấn đề. Để biết thêm thông tin về những vấn đề mà cơ sở dữ liệu giải quyết và cách giải quyết, tôi khuyên bạn nên đọc bài viết “
Cơ sở dữ liệu được cấu trúc như thế nào ”. Điều này có nghĩa là chúng tôi quyết định lưu trữ dữ liệu của mình trong cơ sở dữ liệu. Từ lâu, Java đã có thể làm việc với cơ sở dữ liệu bằng API JDBC (Kết nối cơ sở dữ liệu Java). Bạn có thể đọc thêm về JDBC tại đây: “
JDBC hoặc nơi tất cả bắt đầu .” Nhưng thời gian trôi qua và các nhà phát triển mỗi lần đều phải đối mặt với nhu cầu viết cùng một loại và mã “bảo trì” không cần thiết (còn gọi là mã Boilerplate) cho các hoạt động tầm thường là lưu các đối tượng Java trong cơ sở dữ liệu và ngược lại, tạo các đối tượng Java bằng cách sử dụng dữ liệu từ cơ sở dữ liệu. Và sau đó, để giải quyết những vấn đề này, một khái niệm như ORM đã ra đời.
ORM - Ánh xạ quan hệ đối tượng hoặc được dịch sang ánh xạ quan hệ đối tượng tiếng Nga. Nó là một công nghệ lập trình liên kết cơ sở dữ liệu với các khái niệm về ngôn ngữ lập trình hướng đối tượng. Để đơn giản hóa, ORM là kết nối giữa các đối tượng Java và các bản ghi trong cơ sở dữ liệu:
ORM về cơ bản là khái niệm rằng một đối tượng Java có thể được biểu diễn dưới dạng dữ liệu trong cơ sở dữ liệu (và ngược lại). Nó được thể hiện dưới dạng đặc tả JPA - Java Persistence API. Đặc tả này đã là một mô tả về API Java thể hiện khái niệm này. Thông số kỹ thuật cho chúng tôi biết những công cụ nào chúng tôi phải được cung cấp (tức là những giao diện nào chúng tôi có thể làm việc thông qua) để hoạt động theo khái niệm ORM. Và cách sử dụng số tiền này. Đặc tả không mô tả việc thực hiện các công cụ. Điều này giúp có thể sử dụng các cách triển khai khác nhau cho một thông số kỹ thuật. Bạn có thể đơn giản hóa nó và nói rằng đặc tả là mô tả về API. Văn bản của đặc tả JPA có thể được tìm thấy trên trang web của Oracle: "
JSR 338: JavaTM Persistence API ". Do đó, để sử dụng JPA, chúng tôi cần một số triển khai mà chúng tôi sẽ sử dụng công nghệ này. Việc triển khai JPA còn được gọi là Nhà cung cấp JPA. Một trong những triển khai JPA đáng chú ý nhất là
Hibernate . Vì vậy, tôi đề nghị xem xét nó.
Tạo một dự án
Vì JPA là về Java nên chúng ta sẽ cần một dự án Java. Chúng tôi có thể tự tạo cấu trúc thư mục theo cách thủ công và tự thêm các thư viện cần thiết. Nhưng sẽ thuận tiện và chính xác hơn nhiều khi sử dụng các hệ thống để tự động hóa việc lắp ráp các dự án (tức là về bản chất, đây chỉ là một chương trình sẽ quản lý việc lắp ráp các dự án cho chúng ta. Tạo thư mục, thêm các thư viện cần thiết vào đường dẫn lớp, v.v. .). Một hệ thống như vậy là Gradle. Bạn có thể đọc thêm về Gradle tại đây: "
Giới thiệu tóm tắt về Gradle ". Như chúng ta đã biết, chức năng của Gradle (tức là những việc nó có thể làm) được triển khai bằng nhiều Plugin cho Gradle khác nhau. Hãy sử dụng Gradle và plugin "
Gradle Build init Plugin ". Hãy chạy lệnh:
gradle init --type java-application
Gradle sẽ thực hiện cấu trúc thư mục cần thiết cho chúng ta và tạo mô tả khai báo cơ bản về dự án trong tập lệnh xây dựng
build.gradle
. Vì vậy, chúng tôi có một ứng dụng. Chúng ta cần suy nghĩ về những gì chúng ta muốn mô tả hoặc mô hình hóa bằng ứng dụng của mình. Hãy sử dụng một số công cụ lập mô hình, ví dụ:
app.quickdatabasediagrams.com Ở đây cần nói rằng những gì chúng tôi đã mô tả là “mô hình miền” của chúng tôi. Tên miền là một “lĩnh vực chủ đề”. Nói chung, tên miền trong tiếng Latin là “sở hữu”. Vào thời Trung cổ, đây là tên được đặt cho những khu vực thuộc sở hữu của các vị vua hoặc lãnh chúa phong kiến. Và trong tiếng Pháp, nó trở thành từ "domaine", dịch đơn giản là "khu vực". Vì vậy, chúng tôi đã mô tả “mô hình miền” = “mô hình chủ đề” của mình. Mỗi phần tử của mô hình này là một loại “bản chất” nào đó, một thứ gì đó từ cuộc sống thực. Trong trường hợp của chúng tôi, đây là các thực thể: Danh mục (
Category
), Chủ đề (
Topic
). Hãy tạo một gói riêng cho các thực thể, ví dụ như với mô hình tên. Và hãy thêm các lớp Java mô tả các thực thể vào đó. Trong mã Java, các thực thể như vậy là
POJO thông thường , có thể trông giống như sau:
public class Category {
private Long id;
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Hãy sao chép nội dung của lớp và tạo một lớp bằng cách tương tự
Topic
. Anh ta sẽ chỉ khác nhau ở những gì anh ta biết về loại mà anh ta thuộc về. Do đó, hãy thêm
Topic
trường danh mục và các phương thức để làm việc với nó vào lớp:
private Category category;
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
Bây giờ chúng ta có một ứng dụng Java có mô hình miền riêng. Bây giờ là lúc bắt đầu kết nối với dự án JPA.
Thêm JPA
Vì vậy, như chúng ta nhớ, JPA có nghĩa là chúng ta sẽ lưu thứ gì đó vào cơ sở dữ liệu. Vì vậy, chúng ta cần một cơ sở dữ liệu. Để sử dụng kết nối cơ sở dữ liệu trong dự án của chúng tôi, chúng tôi cần thêm thư viện phụ thuộc để kết nối với cơ sở dữ liệu. Như chúng tôi nhớ, chúng tôi đã sử dụng Gradle để tạo tập lệnh xây dựng cho chúng tôi
build.gradle
. Trong đó chúng tôi sẽ mô tả các phần phụ thuộc mà dự án của chúng tôi cần. Phần phụ thuộc là những thư viện mà nếu không có thì mã của chúng tôi không thể hoạt động được. Hãy bắt đầu với phần mô tả sự phụ thuộc vào việc kết nối với cơ sở dữ liệu. Chúng tôi thực hiện điều này giống như cách chúng tôi sẽ làm nếu chúng tôi chỉ làm việc với JDBC:
dependencies {
implementation 'com.h2database:h2:1.4.199'
Bây giờ chúng tôi có một cơ sở dữ liệu. Bây giờ chúng ta có thể thêm một lớp vào ứng dụng của mình, lớp này chịu trách nhiệm ánh xạ các đối tượng Java của chúng ta vào các khái niệm cơ sở dữ liệu (từ Java sang SQL). Như chúng ta nhớ, chúng ta sẽ sử dụng cách triển khai đặc tả JPA có tên Hibernate cho việc này:
dependencies {
implementation 'com.h2database:h2:1.4.199'
implementation 'org.hibernate:hibernate-core:5.4.2.Final'
Bây giờ chúng ta cần cấu hình JPA. Nếu chúng ta đọc thông số kỹ thuật và phần "Đơn vị bền vững 8.1", chúng ta sẽ biết rằng Đơn vị bền vững là một loại kết hợp giữa cấu hình, siêu dữ liệu và thực thể. Và để JPA hoạt động, bạn cần mô tả ít nhất một Persistence Unit trong tệp cấu hình, được gọi là
persistence.xml
. Vị trí của nó được mô tả trong chương thông số kỹ thuật "8.2 Đóng gói đơn vị bền vững". Theo phần này, nếu chúng ta có môi trường Java SE thì phải đặt nó vào thư mục gốc của thư mục META-INF.
Hãy sao chép nội dung từ ví dụ được đưa ra trong đặc tả JPA trong
8.2.1 persistence.xml file
chương " ":
<persistence>
<persistence-unit name="JavaRush">
<description>Persistence Unit For test</description>
<class>hibernate.model.Category</class>
<class>hibernate.model.Topic</class>
</persistence-unit>
</persistence>
Nhưng điều này là không đủ. Chúng tôi cần cho biết Nhà cung cấp JPA của chúng tôi là ai, tức là. người thực hiện đặc tả JPA:
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
Bây giờ hãy thêm cài đặt (
properties
). Một số trong số chúng (bắt đầu bằng
javax.persistence
) là cấu hình JPA tiêu chuẩn và được mô tả trong đặc tả JPA trong phần "thuộc tính 8.2.1.9". Một số cấu hình dành riêng cho nhà cung cấp (trong trường hợp của chúng tôi, chúng ảnh hưởng đến Hibernate với tư cách là Nhà cung cấp Jpa. Khối cài đặt của chúng tôi sẽ trông như thế này:
<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>
Bây giờ chúng ta đã có config tương thích với JPA
persistence.xml
, có Hibernate nhà cung cấp JPA và có cơ sở dữ liệu H2, ngoài ra còn có 2 lớp là mô hình miền của chúng ta. Cuối cùng chúng ta hãy làm cho tất cả điều này hoạt động. Trong danh mục
/test/java
, Gradle của chúng tôi đã tạo một mẫu cho các bài kiểm tra Đơn vị và gọi nó là AppTest. Hãy sử dụng nó. Như đã nêu trong chương "7.1 Bối cảnh liên tục" của đặc tả JPA, các thực thể trong thế giới JPA sống trong một không gian được gọi là Bối cảnh liên tục. Nhưng chúng tôi không trực tiếp làm việc với Persistence Context. Đối với điều này, chúng tôi sử dụng
Entity Manager
hoặc "người quản lý thực thể". Chính anh ta là người biết về bối cảnh và những thực thể sống ở đó. Chúng tôi tương tác với
Entity Manager
'om. Sau đó, tất cả những gì còn lại là hiểu chúng ta có thể lấy cái này ở đâu
Entity Manager
? Theo chương "7.2.2 Lấy Trình quản lý thực thể được quản lý ứng dụng" của đặc tả JPA, chúng ta phải sử dụng
EntityManagerFactory
. Do đó, chúng ta hãy trang bị cho mình đặc tả JPA và lấy một ví dụ từ chương “7.3.2 Lấy một nhà máy quản lý thực thể trong môi trường Java SE” và định dạng nó dưới dạng một bài kiểm tra Đơn vị đơn giản:
@Test
public void shouldStartHibernate() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "JavaRush" );
EntityManager entityManager = emf.createEntityManager();
}
Thử nghiệm này sẽ hiển thị lỗi "Phiên bản XSD JPA Persence.xml không được nhận dạng". Lý do là
persistence.xml
bạn cần xác định chính xác lược đồ sẽ sử dụng, như đã nêu trong đặc tả JPA ở phần “8.3 Persence.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">
Ngoài ra, thứ tự của các yếu tố là quan trọng. Vì vậy,
provider
nó phải được chỉ định trước khi liệt kê các lớp. Sau đó, thử nghiệm sẽ chạy thành công. Chúng ta đã hoàn thành việc kết nối JPA trực tiếp. Trước khi tiếp tục, chúng ta hãy nghĩ về các bài kiểm tra còn lại. Mỗi bài kiểm tra của chúng tôi sẽ yêu cầu
EntityManager
. Hãy đảm bảo rằng mỗi bài kiểm tra đều có bài kiểm tra riêng
EntityManager
khi bắt đầu thực hiện. Ngoài ra, chúng tôi muốn cơ sở dữ liệu luôn mới. Do chúng tôi sử dụng
inmemory
tùy chọn nên chỉ cần đóng
EntityManagerFactory
. Sáng tạo
Factory
là một hoạt động tốn kém. Nhưng đối với các bài kiểm tra thì điều đó là hợp lý. JUnit cho phép bạn chỉ định các phương thức sẽ được thực thi trước (Trước) và sau (Sau) khi thực hiện mỗi bài kiểm tra:
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();
}
Bây giờ, trước khi thực hiện bất kỳ thử nghiệm nào, một thử nghiệm mới sẽ được tạo
EntityManagerFactory
, điều này sẽ đòi hỏi phải tạo cơ sở dữ liệu mới, bởi vì
hibernate.hbm2ddl.auto
có ý nghĩa
create
. Và từ nhà máy mới, chúng ta sẽ có một nhà máy mới
EntityManager
.
Thực thể
Như chúng tôi nhớ, trước đây chúng tôi đã tạo các lớp mô tả mô hình miền của mình. Chúng tôi đã nói rằng đây là những “bản chất” của chúng tôi. Đây là Thực thể mà chúng tôi sẽ quản lý bằng cách sử dụng
EntityManager
. Hãy viết một bài kiểm tra đơn giản để lưu lại bản chất của một danh mục:
@Test
public void shouldPersistCategory() {
Category cat = new Category();
cat.setTitle("new category");
em.persist(cat);
}
Nhưng bài kiểm tra này sẽ không có kết quả ngay lập tức, bởi vì... chúng tôi sẽ nhận được nhiều lỗi khác nhau giúp chúng tôi hiểu thực thể là gì:
-
Unknown entity: hibernate.model.Category
Tại sao Hibernate không hiểu Category
đây là gì entity
? Vấn đề là các thực thể phải được mô tả theo tiêu chuẩn JPA.
Các lớp thực thể phải được chú thích bằng chú thích @Entity
, như đã nêu trong chương "2.1 Lớp thực thể" của đặc tả JPA.
-
No identifier specified for entity: hibernate.model.Category
Các thực thể phải có một mã định danh duy nhất có thể được sử dụng để phân biệt bản ghi này với bản ghi khác.
Theo chương "2.4 Khóa chính và nhận dạng thực thể" của đặc tả JPA, "Mọi thực thể phải có khóa chính", tức là. Mọi thực thể đều phải có một "khóa chính". Khóa chính như vậy phải được chỉ định bằng chú thích@Id
-
ids for this class must be manually assigned before calling save()
ID phải đến từ đâu đó. Nó có thể được chỉ định thủ công hoặc có thể được lấy tự động.
Do đó, như đã chỉ ra trong các chương "11.2.3.3 GenerationdValue" và "11.1.20 GenerationdValue Annotation", chúng ta có thể chỉ định chú thích @GeneratedValue
.
Vì vậy, để lớp danh mục trở thành một thực thể, chúng ta phải thực hiện những thay đổi sau:
@Entity
public class Category {
@Id
@GeneratedValue
private Long id;
Ngoài ra, chú thích
@Id
còn cho biết nên sử dụng cái nào
Access Type
. Bạn có thể đọc thêm về loại truy cập trong đặc tả JPA, trong phần "2.3 Loại truy cập". Nói một cách ngắn gọn, bởi vì... chúng ta đã chỉ định
@Id
ở trên trường (
field
), thì loại truy cập sẽ là mặc định
field-based
chứ không phải
property-based
. Do đó, nhà cung cấp JPA sẽ đọc và lưu trữ giá trị trực tiếp từ các trường. Nếu chúng tôi đặt
@Id
phía trên getter thì
property-based
quyền truy cập sẽ được sử dụng, tức là. thông qua getter và setter. Khi chạy thử nghiệm, chúng ta cũng xem được những yêu cầu nào được gửi tới cơ sở dữ liệu (nhờ vào tùy chọn
hibernate.show_sql
). Nhưng khi lưu lại thì chúng ta không thấy cái nào cả
insert
. Hóa ra chúng ta thực sự không lưu được gì cả? JPA cho phép bạn đồng bộ hóa bối cảnh lưu giữ và cơ sở dữ liệu bằng phương thức
flush
:
entityManager.flush();
Nhưng nếu chúng ta thực hiện nó ngay bây giờ, chúng ta sẽ gặp lỗi:
không có giao dịch nào đang diễn ra . Và bây giờ là lúc tìm hiểu về cách JPA sử dụng các giao dịch.
Giao dịch JPA
Như chúng ta nhớ, JPA dựa trên khái niệm bối cảnh bền vững. Đây là nơi các thực thể sinh sống. Và chúng tôi quản lý các thực thể thông qua
EntityManager
. Khi chúng ta thực thi lệnh
persist
, chúng ta đặt thực thể vào ngữ cảnh. Chính xác hơn, chúng tôi nói với
EntityManager
bạn rằng việc này cần phải được thực hiện. Nhưng bối cảnh này chỉ là một khu vực lưu trữ nào đó. Nó thậm chí đôi khi còn được gọi là "bộ nhớ đệm cấp một". Nhưng nó cần được kết nối với cơ sở dữ liệu. Lệnh
flush
trước đây bị lỗi do lỗi, đồng bộ hóa dữ liệu từ bối cảnh lưu giữ lâu dài với cơ sở dữ liệu. Nhưng điều này đòi hỏi phải có sự vận chuyển và sự vận chuyển này là một giao dịch. Các giao dịch trong JPA được mô tả trong phần "7.5 Kiểm soát giao dịch" của thông số kỹ thuật. Có một API đặc biệt để sử dụng các giao dịch trong JPA:
entityManager.getTransaction().begin();
entityManager.getTransaction().commit();
Chúng tôi cần thêm quản lý giao dịch vào mã của mình, mã này chạy trước và sau khi kiểm tra:
@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();
}
Sau khi thêm, chúng ta sẽ thấy trong nhật ký chèn một biểu thức trong SQL mà trước đây không có:
Những thay đổi tích lũy trong
EntityManager
giao dịch đã được cam kết (xác nhận và lưu) trong cơ sở dữ liệu. Bây giờ chúng ta hãy cố gắng tìm ra bản chất của chúng ta. Hãy tạo một thử nghiệm để tìm kiếm một thực thể theo ID của nó:
@Test
public void shouldFindCategory() {
Category cat = new Category();
cat.setTitle("test");
em.persist(cat);
Category result = em.find(Category.class, 1L);
assertNotNull(result);
}
Trong trường hợp này, chúng tôi sẽ nhận được thực thể mà chúng tôi đã lưu trước đó, nhưng chúng tôi sẽ không thấy các truy vấn CHỌN trong nhật ký. Và mọi thứ đều dựa trên những gì chúng tôi nói: “Người quản lý thực thể, vui lòng tìm cho tôi thực thể Danh mục có ID=1.” Và trình quản lý thực thể trước tiên sẽ xem xét ngữ cảnh của nó (sử dụng một loại bộ đệm) và chỉ khi không tìm thấy nó, nó mới đi tìm trong cơ sở dữ liệu. Cần thay đổi ID thành 2 (không có trường hợp nào như vậy, chúng tôi chỉ lưu 1 trường hợp) và chúng tôi sẽ thấy
SELECT
yêu cầu xuất hiện. Bởi vì không tìm thấy thực thể nào trong ngữ cảnh và
EntityManager
cơ sở dữ liệu đang cố gắng tìm kiếm một thực thể nên có nhiều lệnh khác nhau mà chúng ta có thể sử dụng để kiểm soát trạng thái của một thực thể trong ngữ cảnh. Sự chuyển đổi của một thực thể từ trạng thái này sang trạng thái khác được gọi là vòng đời của thực thể -
lifecycle
.
Vòng đời thực thể
Vòng đời của các thực thể được mô tả trong đặc tả JPA ở chương "3.2 Vòng đời của thực thể thực thể". Bởi vì các thực thể sống trong một bối cảnh và được kiểm soát bởi
EntityManager
, khi đó họ nói rằng các thực thể đó được kiểm soát, tức là. được quản lý. Chúng ta hãy nhìn vào các giai đoạn trong vòng đời của một thực thể:
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);
Và đây là sơ đồ để củng cố nó:
Lập bản đồ
Trong JPA chúng ta có thể mô tả mối quan hệ của các thực thể với nhau. Hãy nhớ rằng chúng ta đã xem xét mối quan hệ của các thực thể với nhau khi xử lý mô hình miền của mình. Sau đó, chúng tôi sử dụng tài nguyên
quickdatabasediagrams.com :
Việc thiết lập kết nối giữa các thực thể được gọi là ánh xạ hoặc liên kết (Assocation Mappings). Các loại liên kết có thể được thiết lập bằng JPA được trình bày dưới đây:
Hãy nhìn vào một thực thể
Topic
mô tả một chủ đề. Chúng ta có thể nói gì về thái độ
Topic
đối với
Category
? Nhiều người
Topic
sẽ thuộc về một loại. Vì vậy, chúng ta cần một hiệp hội
ManyToOne
. Hãy thể hiện mối quan hệ này trong JPA:
@ManyToOne
@JoinColumn(name = "category_id")
private Category category;
Để nhớ nên đặt chú thích nào, bạn có thể nhớ rằng phần cuối cùng chịu trách nhiệm về trường phía trên mà chú thích được chỉ định.
ToOne
- trường hợp cụ thể
ToMany
- bộ sưu tập. Bây giờ kết nối của chúng tôi là một chiều. Hãy biến nó thành một cuộc giao tiếp hai chiều. Hãy bổ sung thêm
Category
kiến thức về tất cả những người
Topic
thuộc danh mục này. Nó phải kết thúc bằng
ToMany
, vì chúng ta có một danh sách
Topic
. Đó là thái độ “Đối với nhiều” chủ đề. Câu hỏi vẫn còn -
OneToMany
hoặc
ManyToMany
:
Bạn có thể đọc câu trả lời hay về cùng chủ đề tại đây: "
Giải thích mối quan hệ ORM oneToMany, manyToMany giống như tôi năm ". Nếu một danh mục có mối liên hệ với
ToMany
các chủ đề thì mỗi chủ đề này chỉ có thể có một danh mục, nếu
One
không thì
Many
. Vì vậy,
Category
danh sách tất cả các chủ đề sẽ trông như thế này:
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "topic_id")
private Set<Topic> topics = new HashSet<>();
Và về cơ bản đừng quên
Category
viết một getter để có danh sách tất cả các chủ đề:
public Set<Topic> getTopics() {
return this.topics;
}
Mối quan hệ hai chiều là điều rất khó theo dõi một cách tự động. Vì vậy, JPA chuyển trách nhiệm này cho nhà phát triển. Điều này có ý nghĩa đối với chúng tôi là khi chúng tôi thiết lập
Topic
mối quan hệ thực thể với
Category
, chúng tôi phải tự đảm bảo tính nhất quán của dữ liệu. Điều này được thực hiện đơn giản:
public void setCategory(Category category) {
category.getTopics().add(this);
this.category = category;
}
Hãy viết một bài kiểm tra đơn giản để kiểm tra:
@Test
public void shouldPersistCategoryAndTopics() {
Category cat = new Category();
cat.setTitle("test");
Topic topic = new Topic();
topic.setTitle("topic");
topic.setCategory(cat);
em.persist(cat);
}
Lập bản đồ là một chủ đề hoàn toàn riêng biệt. Mục đích của việc xem xét này là để hiểu các phương tiện đạt được điều này. Bạn có thể đọc thêm về lập bản đồ ở đây:
JPQL
JPA giới thiệu một công cụ thú vị - truy vấn bằng Ngôn ngữ truy vấn liên tục Java. Ngôn ngữ này tương tự như SQL nhưng sử dụng mô hình đối tượng Java thay vì bảng SQL. Hãy xem một ví dụ:
@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());
}
Như chúng ta có thể thấy, trong truy vấn, chúng ta đã sử dụng tham chiếu đến một thực thể
Category
chứ không phải bảng. Và cả trên lĩnh vực của thực thể này
title
. JPQL cung cấp nhiều tính năng hữu ích và xứng đáng có bài viết riêng. Thông tin chi tiết có thể được tìm thấy trong đánh giá:
API tiêu chí
Và cuối cùng, tôi muốn đề cập đến API Tiêu chí. JPA giới thiệu một công cụ xây dựng truy vấn động. Ví dụ về việc sử dụng API tiêu chí:
@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());
}
Ví dụ này tương đương với việc thực hiện yêu cầu "
SELECT c FROM Category c
".
API tiêu chí là một công cụ mạnh mẽ. Bạn có thể đọc thêm về nó ở đây:
Phần kết luận
Như chúng ta có thể thấy, JPA cung cấp một số lượng lớn các tính năng và công cụ. Mỗi người trong số họ đòi hỏi kinh nghiệm và kiến thức. Ngay cả trong khuôn khổ cuộc rà soát của JPA cũng không thể đề cập hết được, chưa nói đến việc lặn chi tiết. Nhưng tôi hy vọng rằng sau khi đọc nó, bạn sẽ hiểu rõ hơn ORM và JPA là gì, nó hoạt động như thế nào và có thể làm gì với nó. Chà, đối với một bữa ăn nhẹ, tôi cung cấp nhiều nguyên liệu khác nhau:
#Viacheslav
GO TO FULL VERSION