Câu hỏi này có phần liên quan đến Câu hỏi Vị trí Chú thích Hibernate .
Nhưng tôi muốn biết cái nào tốt hơn ? Truy cập thông qua các thuộc tính hoặc truy cập thông qua các lĩnh vực? Những lợi thế và bất lợi của mỗi là gì?
Câu hỏi này có phần liên quan đến Câu hỏi Vị trí Chú thích Hibernate .
Nhưng tôi muốn biết cái nào tốt hơn ? Truy cập thông qua các thuộc tính hoặc truy cập thông qua các lĩnh vực? Những lợi thế và bất lợi của mỗi là gì?
Câu trả lời:
Tôi thích người truy cập hơn, vì tôi có thể thêm một số logic kinh doanh cho người truy cập của mình bất cứ khi nào tôi cần. Đây là một ví dụ:
@Entity
public class Person {
@Column("nickName")
public String getNickName(){
if(this.name != null) return generateFunnyNick(this.name);
else return "John Doe";
}
}
Ngoài ra, nếu bạn ném các lib khác vào hỗn hợp (như một số lib chuyển đổi JSON hoặc BeanMapper hoặc Dozer hoặc lib ánh xạ / sao chép đậu khác dựa trên các thuộc tính getter / setter), bạn sẽ đảm bảo rằng lib được đồng bộ hóa với tính bền vững người quản lý (cả hai đều sử dụng getter / setter).
Có các đối số cho cả hai, nhưng hầu hết trong số chúng xuất phát từ một số yêu cầu người dùng nhất định "nếu bạn cần thêm logic cho" hoặc "xxxx phá vỡ đóng gói". Tuy nhiên, không ai thực sự bình luận về lý thuyết này, và đưa ra một lập luận hợp lý.
Hibernate / JPA thực sự đang làm gì khi nó tồn tại một đối tượng - tốt, nó vẫn tồn tại NHÀ NƯỚC của đối tượng. Điều đó có nghĩa là lưu trữ nó theo cách mà nó có thể dễ dàng sao chép.
Đóng gói là gì? Đóng gói có nghĩa là đóng gói dữ liệu (hoặc trạng thái) với giao diện mà ứng dụng / khách hàng có thể sử dụng để truy cập dữ liệu một cách an toàn - giữ cho dữ liệu nhất quán và hợp lệ.
Hãy nghĩ về điều này giống như MS Word. MS Word duy trì một mô hình của tài liệu trong bộ nhớ - tài liệu STATE. Nó trình bày một giao diện mà người dùng có thể sử dụng để sửa đổi tài liệu - một bộ nút, công cụ, lệnh bàn phím, v.v. Tuy nhiên, khi bạn chọn duy trì (Lưu) tài liệu đó, nó sẽ lưu trạng thái bên trong, chứ không phải tập hợp các phím bấm và nhấp chuột được sử dụng để tạo ra nó.
Lưu trạng thái bên trong của đối tượng KHÔNG phá vỡ đóng gói - nếu không, bạn không thực sự hiểu đóng gói nghĩa là gì và tại sao nó tồn tại. Nó giống như tuần tự hóa đối tượng thực sự.
Vì lý do này, TRONG CÁC TRƯỜNG HỢP NHẤT, thích hợp để duy trì các L FINH VỰC chứ không phải PHỤ KIỆN. Điều này có nghĩa là một đối tượng có thể được tạo lại chính xác từ cơ sở dữ liệu chính xác theo cách nó được lưu trữ. Nó không cần bất kỳ xác nhận nào, bởi vì điều này đã được thực hiện trên bản gốc khi nó được tạo và trước khi nó được lưu trữ trong cơ sở dữ liệu (trừ khi, Chúa cấm, bạn đang lưu trữ dữ liệu không hợp lệ trong DB !!!!). Tương tự như vậy, không cần phải tính toán các giá trị, vì chúng đã được tính toán trước khi đối tượng được lưu trữ. Đối tượng nên nhìn giống như cách nó đã làm trước khi nó được lưu. Trong thực tế, bằng cách thêm các công cụ bổ sung vào getters / setters, bạn thực sự làm tăng nguy cơ rằng bạn sẽ tạo lại một cái gì đó không phải là bản sao chính xác của bản gốc.
Tất nhiên, chức năng này đã được thêm vào vì một lý do. Có thể có một số trường hợp sử dụng hợp lệ để duy trì người truy cập, tuy nhiên, chúng thường sẽ rất hiếm. Một ví dụ có thể là bạn muốn tránh duy trì một giá trị được tính toán, mặc dù bạn có thể muốn đặt câu hỏi tại sao bạn không tính toán nó theo yêu cầu trong getter của giá trị, hoặc khởi tạo một cách lười biếng trong getter. Cá nhân tôi không thể nghĩ ra bất kỳ trường hợp sử dụng tốt nào và không có câu trả lời nào ở đây thực sự đưa ra câu trả lời "Kỹ thuật phần mềm".
Tôi thích truy cập trường hơn, vì theo cách đó tôi không bị buộc phải cung cấp getter / setter cho mỗi thuộc tính.
Một khảo sát nhanh qua Google cho thấy rằng quyền truy cập trường chiếm đa số (ví dụ: http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype ).
Tôi tin rằng truy cập trường là thành ngữ được đề xuất bởi Spring, nhưng tôi không thể tìm thấy tài liệu tham khảo để sao lưu.
Có một câu hỏi SO liên quan đã cố gắng đo lường hiệu suất và đi đến kết luận rằng "không có sự khác biệt".
Đây là một tình huống mà bạn phải sử dụng người truy cập tài sản. Hãy tưởng tượng bạn có một lớp trừu tượng CHUNG với nhiều điều tốt đẹp để thực hiện để kế thừa thành 8 lớp con cụ thể:
public abstract class Foo<T extends Bar> {
T oneThing;
T anotherThing;
// getters and setters ommited for brevity
// Lots and lots of implementation regarding oneThing and anotherThing here
}
Bây giờ chính xác làm thế nào bạn nên chú thích lớp này? Câu trả lời là BẠN KHÔNG THỂ chú thích nó với quyền truy cập trường hoặc thuộc tính vì bạn không thể chỉ định thực thể đích tại thời điểm này. Bạn phải chú thích các triển khai cụ thể. Nhưng vì các thuộc tính bền vững được khai báo trong siêu lớp này, bạn PHẢI sử dụng quyền truy cập thuộc tính trong các lớp con.
Truy cập trường không phải là một tùy chọn trong một ứng dụng với các siêu lớp chung chung trừu tượng.
abstract T getOneThing()
, và abstract void setOneThing(T thing)
, và sử dụng truy cập trường.
Tôi có xu hướng thích và sử dụng những người truy cập tài sản:
foo.getId()
mà không cần khởi tạo proxy (quan trọng khi sử dụng Hibernate, cho đến khi HHH-3718 được giải quyết).Hạn chế:
@Transient
xung quanh đó không.Điều đó thực sự phụ thuộc vào một trường hợp cụ thể - cả hai tùy chọn đều có sẵn vì một lý do. IMO nó sôi lên đến ba trường hợp:
Tôi đặc biệt khuyên bạn nên truy cập trường và KHÔNG chú thích trên getters (quyền truy cập thuộc tính) nếu bạn muốn làm bất cứ điều gì nhiều hơn trong setters thay vì chỉ đặt giá trị (ví dụ: Mã hóa hoặc tính toán).
Vấn đề với quyền truy cập thuộc tính là setters cũng được gọi khi đối tượng được tải. Điều này đã làm việc tốt với tôi trong nhiều tháng cho đến khi chúng tôi muốn giới thiệu mã hóa. Trong trường hợp sử dụng của chúng tôi, chúng tôi muốn mã hóa một trường trong setter và giải mã nó trong getter. Vấn đề bây giờ với quyền truy cập thuộc tính là khi Hibernate tải đối tượng, nó cũng đang gọi trình setter để điền vào trường và do đó đã mã hóa lại giá trị được mã hóa. Bài đăng này cũng đề cập đến điều này: Java Hibernate: Hành vi chức năng thiết lập thuộc tính khác nhau tùy thuộc vào người đang gọi nó
Điều này đã khiến tôi đau đầu cho đến khi tôi nhớ sự khác biệt giữa truy cập trường và quyền truy cập thuộc tính. Bây giờ tôi đã chuyển tất cả các chú thích của mình từ quyền truy cập thuộc tính sang quyền truy cập trường và hiện tại nó hoạt động tốt.
Tôi thích sử dụng truy cập trường vì những lý do sau:
Quyền truy cập thuộc tính có thể dẫn đến các lỗi rất khó chịu khi thực hiện bằng / hashCode và các trường tham chiếu trực tiếp (trái ngược với thông qua getters của chúng). Điều này là do proxy chỉ được khởi tạo khi truy cập getters và truy cập trường trực tiếp sẽ trả về null.
Quyền truy cập thuộc tính yêu cầu bạn chú thích tất cả các phương thức tiện ích (ví dụ: addChild / removeChild) là @Transient
.
Với quyền truy cập trường, chúng ta có thể ẩn trường @Version bằng cách không hiển thị một getter nào cả. Một getter cũng có thể dẫn đến việc thêm một setter, và version
trường không bao giờ được đặt thủ công (điều này có thể dẫn đến các vấn đề rất khó chịu). Tất cả gia tăng phiên bản phải được kích hoạt thông qua khóa rõ ràng OPTIMISTIC_FORCE_INCREMENT hoặc PESSIMISTIC_FORCE_INCREMENT .
version
trường thường hữu ích trong các tình huống sử dụng DTO thay vì các thực thể tách rời.
Tôi nghĩ rằng chú thích tài sản là tốt hơn bởi vì các trường cập nhật trực tiếp phá vỡ đóng gói, ngay cả khi ORM của bạn thực hiện nó.
Đây là một ví dụ tuyệt vời về nơi nó sẽ đốt cháy bạn: bạn có thể muốn các chú thích của mình cho trình xác nhận ngủ đông và sự kiên trì ở cùng một nơi (cả trường hoặc thuộc tính). Nếu bạn muốn kiểm tra các xác nhận hợp lệ của trình xác nhận ngủ đông được chú thích trên một trường, bạn không thể sử dụng một bản mô phỏng thực thể của mình để tách kiểm tra đơn vị của bạn thành chỉ trình xác nhận. Ôi.
Tôi tin rằng truy cập tài sản so với truy cập trường là khác nhau tinh tế liên quan đến khởi tạo lười biếng.
Hãy xem xét các ánh xạ sau cho 2 loại đậu cơ bản:
<hibernate-mapping package="org.nkl.model" default-access="field">
<class name="FieldBean" table="FIELD_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
<hibernate-mapping package="org.nkl.model" default-access="property">
<class name="PropBean" table="PROP_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
Và các bài kiểm tra đơn vị sau:
@Test
public void testFieldBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
FieldBean fb = new FieldBean("field");
Long id = (Long) session.save(fb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
fb = (FieldBean) session.load(FieldBean.class, id);
System.out.println(fb.getId());
tx.commit();
session.close();
}
@Test
public void testPropBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
PropBean pb = new PropBean("prop");
Long id = (Long) session.save(pb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
pb = (PropBean) session.load(PropBean.class, id);
System.out.println(pb.getId());
tx.commit();
session.close();
}
Bạn sẽ thấy sự khác biệt tinh tế trong các lựa chọn cần thiết:
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
FIELD_BEAN
(message, id)
values
(?, ?)
Hibernate:
select
fieldbean0_.id as id1_0_,
fieldbean0_.message as message1_0_
from
FIELD_BEAN fieldbean0_
where
fieldbean0_.id=?
0
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
PROP_BEAN
(message, id)
values
(?, ?)
1
Đó là, gọi điện fb.getId()
yêu cầu một lựa chọn, trong khi pb.getId()
không.
Hãy để tôi thử tóm tắt những lý do quan trọng nhất để chọn truy cập dựa trên trường. Nếu bạn muốn lặn sâu hơn, xin vui lòng đọc bài viết này trên blog của tôi: Chiến lược truy cập trong JPA và Hibernate - Cái nào tốt hơn, truy cập trường hoặc thuộc tính?
Truy cập dựa trên lĩnh vực cho đến nay là lựa chọn tốt hơn. Dưới đây là 5 lý do cho nó:
Lý do 1: Khả năng đọc mã của bạn tốt hơn
Nếu bạn sử dụng quyền truy cập dựa trên trường, bạn chú thích các thuộc tính thực thể với các chú thích ánh xạ của bạn. Bằng cách đặt định nghĩa của tất cả các thuộc tính thực thể ở đầu lớp, bạn sẽ có được cái nhìn tương đối nhỏ gọn về tất cả các thuộc tính và ánh xạ của chúng.
Lý do 2: Bỏ qua các phương thức getter hoặc setter mà ứng dụng của bạn không nên gọi
Một ưu điểm khác của truy cập dựa trên trường là nhà cung cấp kiên trì của bạn, ví dụ, Hibernate hoặc EclipseLink, không sử dụng các phương thức getter và setter của các thuộc tính thực thể của bạn. Điều đó có nghĩa là bạn không cần cung cấp bất kỳ phương thức nào không được sử dụng bởi mã doanh nghiệp của bạn. Đây thường là trường hợp cho các phương thức setter của các thuộc tính khóa chính hoặc các cột phiên bản được tạo. Nhà cung cấp kiên trì của bạn quản lý các giá trị của các thuộc tính này và bạn không nên đặt chúng theo chương trình.
Lý do 3: Triển khai linh hoạt các phương thức getter và setter
Vì nhà cung cấp kiên trì của bạn không gọi các phương thức getter và setter, nên họ không bị buộc phải thực hiện bất kỳ yêu cầu bên ngoài nào. Bạn có thể thực hiện các phương pháp này theo bất kỳ cách nào bạn muốn. Điều đó cho phép bạn thực hiện các quy tắc xác thực dành riêng cho doanh nghiệp, để kích hoạt logic nghiệp vụ bổ sung hoặc chuyển đổi thuộc tính thực thể thành một loại dữ liệu khác.
Ví dụ, bạn có thể sử dụng điều đó để bọc một liên kết hoặc thuộc tính tùy chọn vào Java Optional
.
Lý do 4: Không cần đánh dấu các phương thức tiện ích như @Transient
Một lợi ích khác của chiến lược truy cập dựa trên trường là bạn không cần chú thích các phương thức tiện ích của mình @Transient
. Chú thích này cho nhà cung cấp kiên trì của bạn rằng một phương thức hoặc thuộc tính không phải là một phần của trạng thái bền vững thực thể. Và bởi vì với quyền truy cập kiểu trường, trạng thái liên tục được xác định bởi các thuộc tính của thực thể của bạn, việc triển khai JPA của bạn bỏ qua tất cả các phương thức của thực thể.
Lý do 5: Tránh lỗi khi làm việc với proxy
Hibernate sử dụng proxy cho các hiệp hội được tìm nạp một cách lười biếng để nó có thể kiểm soát việc khởi tạo các hiệp hội này. Cách tiếp cận đó hoạt động tốt trong hầu hết các tình huống. Nhưng nó giới thiệu một cạm bẫy nguy hiểm nếu bạn sử dụng quyền truy cập dựa trên tài sản.
Nếu bạn sử dụng quyền truy cập dựa trên thuộc tính, Hibernate sẽ khởi tạo các thuộc tính của đối tượng proxy khi bạn gọi phương thức getter. Đó luôn là trường hợp nếu bạn sử dụng đối tượng proxy trong mã doanh nghiệp của mình. Nhưng khá nhiều triển khai bằng và hashCode truy cập trực tiếp vào các thuộc tính. Nếu đây là lần đầu tiên bạn truy cập bất kỳ thuộc tính proxy nào, các thuộc tính này vẫn chưa được khởi tạo.
Theo mặc định, các nhà cung cấp JPA truy cập các giá trị của các trường thực thể và ánh xạ các trường đó vào các cột cơ sở dữ liệu bằng cách sử dụng các phương thức truy cập thuộc tính Java (getter) và trình biến đổi (setter) của thực thể. Như vậy, tên và loại của các trường riêng trong một thực thể không quan trọng đối với JPA. Thay vào đó, JPA chỉ xem xét các tên và kiểu trả về của các bộ truy cập thuộc tính JavaBean. Bạn có thể thay đổi điều này bằng cách sử dụng @javax.persistence.Access
chú thích, cho phép bạn chỉ định rõ ràng phương pháp truy cập mà nhà cung cấp JPA nên sử dụng.
@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}
Các tùy chọn khả dụng cho enum AccessType là PROPERTY (mặc định) và FIELD. Với SỞ HỮU, nhà cung cấp nhận và đặt các giá trị trường bằng các phương thức thuộc tính JavaBean. FIELD làm cho nhà cung cấp nhận và đặt giá trị trường bằng cách sử dụng các trường đối tượng. Như một cách thực hành tốt nhất, bạn chỉ nên bám vào mặc định và sử dụng các thuộc tính JavaBean trừ khi bạn có lý do thuyết phục để làm khác.
Bạn có thể đặt các chú thích thuộc tính này trên các trường riêng hoặc các phương thức truy cập công khai. Nếu bạn sử dụng AccessType.PROPERTY
(mặc định) và chú thích các trường riêng thay vì các bộ truy cập JavaBean, tên trường phải khớp với tên thuộc tính JavaBean. Tuy nhiên, tên không phải khớp nếu bạn chú thích các bộ truy cập JavaBean. Tương tự, nếu bạn sử dụng AccessType.FIELD
và chú thích các bộ truy cập JavaBean thay vì các trường, tên trường cũng phải khớp với tên thuộc tính JavaBean. Trong trường hợp này, chúng không phải khớp nếu bạn chú thích các trường. Tốt nhất là chỉ nhất quán và chú thích các trình truy cập JavaBean cho AccessType.PROPERTY
và các trường cho
AccessType.FIELD
.
Điều quan trọng là bạn không bao giờ nên trộn lẫn các chú thích thuộc tính JPA và chú thích trường JPA trong cùng một thực thể. Làm như vậy dẫn đến hành vi không xác định và rất có thể gây ra lỗi.
Đó là một bản trình bày cũ nhưng Rod gợi ý rằng chú thích về quyền truy cập thuộc tính khuyến khích các mô hình miền thiếu máu và không nên là cách "mặc định" để chú thích.
Một điểm khác có lợi cho truy cập trường là nếu không, bạn buộc phải phơi bày setters cho các bộ sưu tập, đối với tôi, đó là một ý tưởng tồi khi thay đổi thể hiện bộ sưu tập liên tục thành một đối tượng không được quản lý bởi Hibernate chắc chắn sẽ phá vỡ tính nhất quán dữ liệu của bạn.
Vì vậy, tôi thích có các bộ sưu tập như các trường được bảo vệ khởi tạo thành các triển khai trống trong hàm tạo mặc định và chỉ hiển thị các biểu đồ của chúng. Hoạt động sau đó, chỉ được quản lý như clear()
, remove()
, removeAll()
vv đều có thể sẽ không bao giờ làm cho Hibernate không biết gì về sự thay đổi.
Tôi thích các lĩnh vực, nhưng tôi đã gặp phải một tình huống dường như buộc tôi phải đặt các chú thích trên getters.
Với việc triển khai JPA Hibernate, @Embedded
dường như không hoạt động trên các trường. Vì vậy, phải đi trên getter. Và một khi bạn đặt nó trên getter, thì các @Column
chú thích khác nhau cũng phải đi trên các getters. (Tôi nghĩ rằng Hibernate không muốn trộn các trường và getters ở đây.) Và một khi bạn đã đặt các @Column
getters trong một lớp, có lẽ sẽ có ý nghĩa khi làm điều đó xuyên suốt.
Tôi ủng hộ người truy cập trường. Mã sạch hơn nhiều. Tất cả các chú thích có thể được đặt trong một phần của một lớp và mã dễ đọc hơn nhiều.
Tôi đã tìm thấy một vấn đề khác với người truy cập thuộc tính: nếu bạn có các phương thức getXYZ trên lớp của bạn KHÔNG được chú thích là có liên quan đến các thuộc tính liên tục, hibernate tạo sql để cố lấy các thuộc tính đó, dẫn đến một số thông báo lỗi rất khó hiểu. Hai giờ lãng phí. Tôi đã không viết mã này; Tôi đã luôn sử dụng các bộ truy cập trường trong quá khứ và chưa bao giờ gặp phải vấn đề này.
Phiên bản ngủ đông được sử dụng trong ứng dụng này:
<!-- hibernate -->
<hibernate-core.version>3.3.2.GA</hibernate-core.version>
<hibernate-annotations.version>3.4.0.GA</hibernate-annotations.version>
<hibernate-commons-annotations.version>3.1.0.GA</hibernate-commons-annotations.version>
<hibernate-entitymanager.version>3.4.0.GA</hibernate-entitymanager.version>
Bạn nên chọn truy cập thông qua các trường trên truy cập thông qua các thuộc tính. Với các trường bạn có thể giới hạn dữ liệu gửi và nhận. Với thông qua các thuộc tính, bạn có thể gửi thêm dữ liệu dưới dạng máy chủ lưu trữ và đặt các mệnh giá G (nhà máy này đặt hầu hết các thuộc tính trong tổng số).
Tôi đã có cùng một câu hỏi liên quan đến accesstype trong chế độ ngủ đông và tìm thấy một số câu trả lời ở đây .
Tôi đã giải quyết việc khởi tạo lười biếng và truy cập trường ở đây Hibernate one-to-one: getId () mà không cần tìm nạp toàn bộ đối tượng
Chúng tôi đã tạo các thực thể và sử dụng các chú thích getter. Vấn đề chúng tôi gặp phải là đây: một số thực thể có các quy tắc phức tạp đối với một số thuộc tính liên quan đến thời điểm chúng có thể được cập nhật. Giải pháp là có một số logic nghiệp vụ trong mỗi setter xác định xem giá trị thực có thay đổi hay không và nếu có thì có nên cho phép thay đổi hay không. Tất nhiên, Hibernate luôn có thể đặt các thuộc tính, vì vậy chúng tôi đã kết thúc với hai nhóm setters. Khá xấu xí.
Đọc các bài viết trước, tôi cũng thấy rằng việc tham chiếu các thuộc tính từ bên trong thực thể có thể dẫn đến các vấn đề với các bộ sưu tập không tải.
Tóm lại, tôi sẽ nghiêng về chú thích các lĩnh vực trong tương lai.
Thông thường đậu là POJO, vì vậy dù sao họ cũng có người truy cập.
Vì vậy, câu hỏi không phải là "cái nào tốt hơn?", Mà đơn giản là "khi nào nên sử dụng truy cập trường?". Và câu trả lời là "khi bạn không cần setter / getter cho trường!".
tôi nghĩ về điều này và tôi chọn phương pháp accesor
tại sao?
bởi vì accesor của trường và methos là như nhau nhưng nếu sau này tôi cần một số logic trong trường tải, tôi lưu di chuyển tất cả các chú thích được đặt trong các trường
Trân trọng
Grubhart
Cả hai:
Thông số EJB3 yêu cầu bạn khai báo các chú thích về loại phần tử sẽ được truy cập, tức là phương thức getter nếu bạn sử dụng quyền truy cập thuộc tính, trường nếu bạn sử dụng quyền truy cập trường.
https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping
AccessType.PROPERTY: Việc triển khai kiên trì EJB sẽ tải trạng thái vào lớp của bạn thông qua các phương thức "setter" của JavaBean và lấy trạng thái từ lớp của bạn bằng các phương thức "getter" của JavaBean. Đây là mặc định.
AccessType.FIELD: Trạng thái được tải và truy xuất trực tiếp từ các trường của lớp bạn. Bạn không phải viết "getters" và "setters" của JavaBean.