5

Chúng tôi sử dụng kế thừa bảng đơn cho mỗi bảng trong ứng dụng của chúng tôi. Điều này cho phép các phiên bản khác nhau của cùng một ngăn xếp ứng dụng hoạt động với cùng một DAO trong khi các thực thể của chúng có thể hơi khác nhau có khả năng chứa thông tin duy nhất cho cá thể đó. Một lớp trừu tượng xác định cấu trúc bảng cơ bản và một phần mở rộng định nghĩa các cột bổ sung, nếu cần thiết bằng cách dụ rằng:Tôi có thể loại bỏ cột phân biệt đối xử trong kế thừa bảng đơn Hibernate không?

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

ứng dụng A:

@Entity 
public class ClientSimple extends Client { 
    private String name; 
    // getter, setter 
} 

ứng dụng B:

@Entity 
public class ClientAdvanced extends Client { 
    private String description; 
    // getter, setter 
} 

Bây giờ một DAO có thể làm việc với Client đối tượng cho ứng dụng A và B nhưng ứng dụng B có thể xác định thông tin bổ sung cho đối tượng máy khách của nó mà có thể được đọc bởi một phương thức quản lý duy nhất e để ứng dụng B:

ứng dụng A:

Client client = new ClientSimple(); 
clientDao.save(client); 

ứng dụng B:

Client client = new ClientAdvanced(); 
clientDao.save(client); 

Đáng tiếc là điều này có nghĩa là một cột DTYPE trong mỗi bảng (hoặc bất kỳ tên khác mà tôi có thể chọn) . Có cách nào để loại bỏ điều này không? Chúng tôi không cần nó và nó sử dụng lên không gian DB ...

Cảm ơn!


EDIT

quan trọng cần lưu ý: @MappedSuperclass sẽ không hoạt động. Chúng tôi đang sử dụng QueryDSL làm lớp trừu tượng HQL của chúng tôi. Điều này yêu cầu các lớp Kiểu truy vấn được tạo tự động cho kiểu lưu truy vấn. Tuy nhiên, những điều này sẽ chỉ được tạo chính xác nếu lớp trừu tượng được chú thích với @Entity.

Đây là neccessairy vì chúng ta muốn truy vấn so với lớp trừu tượng Client trong khi sự thật truy vấn ClientSimple trong ứng dụng A và ClientAdvanced trong ứng dụng B:

Vì vậy, trong bất kỳ ứng dụng này sẽ làm việc:

query.where(QClient.client.name.equals("something"); 

và trong ứng dụng B, điều này sẽ hoạt động:

query.where(QClientSimple.client.description.equals("something else"); 

EDIT2 - đun sôi xuống

Dường như để đun sôi xuống này: Tôi có thể cấu hình ngủ đông vào thời điểm triển khai để thiết lập kiểu phân biệt cho một thực thể inhertited đến một giá trị cố định. Vì vậy, với ví dụ của tôi, Client sẽ luôn là ClientSimple trong một ứng dụng và ClientAdvanced trong trường hợp khác để tôi không phải lưu trữ thông tin đó trong cơ sở dữ liệu?

Như tôi đã nói: Mỗi ứng dụng sẽ là một phiên bản của ngăn xếp ứng dụng cơ sở. Mỗi ứng dụng có thể định nghĩa các cột bổ sung cho cơ sở dữ liệu cục bộ của chúng, nhưng TẤT CẢ các đối tượng sẽ có cùng kiểu cho cá thể đó, vì vậy chúng tôi đảm bảo rằng phân biệt đối xử luôn làm cho nó dư thừa trong cơ sở dữ liệu và một trường hợp sử dụng cho cấu hình ngủ đông.

+0

Bạn đang sử dụng phiên bản Querydsl nào? –

+0

Chúng tôi đang sử dụng QueryDSL 2.2.3 nhưng có thể cập nhật nếu các phiên bản mới hơn hỗ trợ các siêu lớp được ánh xạ làm mục tiêu truy vấn: như sau: '@MappedSuperclass @Table lớp trừu tượng công khai Client ...' + '@Entity public class ClientSimple mở rộng Client' == > tạo các loại truy vấn ... ==> truy vấn: 'QClient.client.name' – Pete

+1

Nó không được hỗ trợ trực tiếp, nhưng bạn có thể thêm một vé cho nó trên GitHub. Nó dễ dàng thực hiện. –

Trả lời

1

Nếu bạn không bao giờ cần sử dụng cả hai ClientSimpleClientAdvanced trong cùng một ứng dụng, bạn có thể khai báo Client@MappedSuperclass thay vì @Entity.

+0

Thật không may là không thể. Chúng tôi đang sử dụng truy vấn DSL là lớp trừu tượng HQL của chúng tôi. Điều này đòi hỏi các lớp trừu tượng được chú thích @Entity để truy vấn có thể chạy với lớp trừu tượng trong khi thực tế truy vấn phần mở rộng. Tôi sẽ chỉnh sửa bài đăng của mình để phản ánh giới hạn này. – Pete

1

Trong Hibernate, một bảng phân cấp lớp sẽ luôn cần một cột phân biệt đối xử để phân biệt giữa các thực thể vì tất cả các lớp trong một cấu trúc phân cấp được lưu trữ trong một bảng. Dưới đây là ví dụ về Hibernate Single Table per Class Hierarchy.

Nhưng bạn có thể muốn xem xét một kế hoạch Hierarchy khác nhau như dưới đây:

Hibernate Single Table per Subclass

Ưu

  • Sử dụng hệ thống cấp bậc này, không đòi hỏi những thay đổi phức tạp để các cơ sở dữ liệu lược đồ khi một lớp cha đơn được sửa đổi.
  • Nó hoạt động tốt với cấu trúc phân cấp nông.

Nhược

  • Khi hệ thống cấp bậc phát triển, nó có thể dẫn đến hiệu suất kém.
  • Số lượng tham gia cần thiết để xây dựng một phân lớp cũng tăng lên.

Hibernate Single Table per Concrete class

Ưu

  • Đây là phương pháp đơn giản nhất của bản đồ thừa kế để thực hiện.

Nhược

  • dữ liệu thats thuộc về một tầng lớp phụ huynh đang nằm rải rác trên một số bảng lớp con, đại diện cho lớp bê tông.
  • Cấu trúc phân cấp này không được khuyến nghị cho hầu hết các trường hợp.
  • Thay đổi một lớp cha mẹ được phản ánh đến số lượng lớn các bảng
  • Một truy vấn diễn đạt bằng những từ ngữ của tầng lớp phụ huynh có thể gây ra một số lượng lớn các lựa chọn hoạt động

tôi sẽ đề nghị bạn để có một xem bảng đơn trên mỗi sơ đồ phân lớp.Mặc dù tôi không chắc chắn về yêu cầu chính xác của bạn. Nhưng điều này có thể hữu ích.

+0

Bạn hoàn toàn chắc chắn không có cách nào để viết một số deserializer tùy chỉnh? Tôi không phải là một chuyên gia ngủ đông, nhưng tôi đặt cược nếu chúng ta nhìn vào cách ngủ đông chuyển đổi ResultSet thành đối tượng, có một cách để tùy chỉnh nó theo cách mà Pete muốn. –

+0

Cảm ơn các liên kết viralpatel, đọc tốt nhưng tiếc là nó không thực sự giúp đỡ. Có lẽ điều này không thể được thực hiện chỉ với chú thích, như gợi ý mograbi chàng ...?! – Pete

+0

Hi Guy, Vâng chắc chắn rằng bạn có thể ghi đè lên nhiều chức năng mặc định của các khung công tác mở như Hibernate. Nhưng vấn đề với cái này (Bảng cho mỗi phân cấp) là dữ liệu của bạn được lưu trữ trong một bảng. Và phải có một cột để xác định hàng dữ liệu nào thuộc về lớp nào trong phân cấp. –

7

Tôi biết, đây là một câu hỏi rất cũ, nhưng tôi đã gặp sự cố này gần đây và điều này có thể hữu ích cho ai đó.

Điều này có thể được thực hiện bằng cách sử dụng chú thích @DiscriminatorFormula của Hibernate. Mô tả sau đây dựa trên cuốn sách Java Persistence with Hibernate, mục 5.1.3; phần có liên quan bắt đầu từ trang đoạn cuối cùng trên trang 202.

Với @DiscriminatorFormula bạn có thể cung cấp một tuyên bố SQL xác định giá trị của discriminator khi lấy các hàng có liên quan từ cơ sở dữ liệu. Trong trường hợp của bạn, nó sẽ phải là một chuỗi đơn giản để đánh giá một số giá trị được chọn tùy ý. Để làm việc này, bạn cần quyết định tên sẽ được sử dụng cho thực thể Client của bạn. Giả sử bạn chọn 'GenericClient' làm tên của entity. Đây là tên sẽ xuất hiện trong một chú thích @Entity làm giá trị của thuộc tính name. Vì vậy, ví dụ hoàn chỉnh, trong trường hợp của bạn sẽ trông giống như sau.

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
@DiscriminatorFormula("'GenericClient'") // *1* 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

// Application A 
@Entity 
@DiscriminatorValue("GenericClient") // *2* 
public class SimpleClient extends Client { 
    // ... 
} 


// Application B 
@Entity 
@DiscriminatorValue("GenericClient") // *3* 
public class AdvancedClient extends Client { 
    // ... 
} 

Dòng được biểu thị bởi '' là một phần của đoạn SQL mà sẽ luôn luôn trở lại 'GenericClient' như giá trị của nó. Các subclasses của Client phải luôn được chú thích bằng @DiscriminatorValue("GenericClient"). Điều này có nghĩa là khi Hibernate lấy các hàng từ DB, loại đối tượng sẽ được xây dựng sẽ luôn là lớp con cụ thể của Client.

Nếu gói nơi lớp con của Client cư trú, và tên của lớp con được cố định:

Trong trường hợp đó, @DiscriminatorValue("GenericClient") trên lớp con sẽ không được yêu cầu, tất cả các bạn sẽ cần phải làm là:

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
@DiscriminatorFormula("'com.example.fixed.path.FixedSubClassName'") 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

Các lớp con sẽ không cần bất kỳ chú thích nào. Mã số discriminator-value mặc định là entity-name, chính nó mặc định là tên lớp đủ điều kiện.

Lưu ý: Các SQL tuyên bố bên @DiscriminatorFormula() thể được bất kỳ SQL tuyên bố hợp lệ cho máy chủ DB nhắm mục tiêu của bạn.