2009-05-25 16 views
11

(lưu ý: Tôi khá quen thuộc với Java, nhưng không phải với Hibernate hoặc JPA - chưa :))JPA: Làm cách nào để chỉ định tên bảng tương ứng với một lớp trong thời gian chạy?

Tôi muốn viết một ứng dụng nói chuyện với cơ sở dữ liệu DB2/400 thông qua JPA và bây giờ tôi có có thể nhận được tất cả các mục trong bảng và liệt kê chúng vào System.out (sử dụng MyEclipse để đảo ngược kỹ sư). Tôi hiểu rằng các chú giải @Table kết quả trong tên được biên dịch tĩnh với lớp, nhưng tôi cần để có thể làm việc với một bảng nơi tên và lược đồ được cung cấp trong thời gian chạy (defintion của họ là như nhau, nhưng chúng tôi có nhiều chúng).

Rõ ràng đây không phải là SO dễ dàng để làm, và tôi sẽ đánh giá cao một gợi ý.

Tôi hiện đã chọn Hibernate làm nhà cung cấp JPA, vì nó có thể xử lý các bảng cơ sở dữ liệu này không được nén.

Vì vậy, câu hỏi là, làm thế nào tôi có thể ở thời gian chạy cho Hibernate thực hiện JPA rằng lớp A tương ứng với bảng cơ sở dữ liệu B?

(chỉnh sửa: một tableName ghi đè() trong Hibernate NamingStrategy có thể cho phép tôi để làm việc xung quanh giới hạn nội tại này, nhưng tôi vẫn muốn một nhà cung cấp giải pháp JPA thuyết bất khả tri)

+0

Bạn có cần làm việc với nhiều bảng (với cùng một JPA @Entity) trong mỗi lần thực hiện chương trình hay chỉ là một bảng cho mỗi lần thực hiện? – mtpettyp

+0

Có khả năng bất kỳ số lượng bảng nào - điều này rất có thể sẽ là một ứng dụng web –

Trả lời

14

Bạn cần phải sử dụng XML version của cấu hình thay vì chú thích. Bằng cách đó bạn có thể tự động tạo XML khi chạy.

Hoặc có thể một cái gì đó như Dynamic JPA bạn sẽ quan tâm?

Tôi nghĩ cần phải làm rõ thêm các vấn đề với vấn đề này.

Câu hỏi đầu tiên là: là tập hợp các bảng có thể lưu trữ một thực thể không? Bởi điều này tôi có nghĩa là bạn không tự động tạo ra các bảng trong thời gian chạy và muốn liên kết các thực thể với chúng. Trường hợp này yêu cầu ba bảng được biết tại thời gian biên dịch. Nếu đó là trường hợp bạn có thể có thể sử dụng thừa kế JPA. Tài liệu OpenJPA nêu chi tiết chiến lược kế thừa table per class.

Ưu điểm của phương pháp này là JPA thuần túy. Tuy nhiên, các bảng phải được biết đến và bạn không thể dễ dàng thay đổi bảng nào mà một đối tượng đã cho được lưu trữ (nếu đó là yêu cầu cho bạn), giống như các đối tượng trong các hệ thống OO thường không thay đổi lớp hoặc nhập.

Nếu bạn muốn điều này thực sự năng động và di chuyển các thực thể giữa các bảng (về cơ bản) thì tôi không chắc JPA là công cụ phù hợp với bạn. An awful lot of magic đi vào làm cho công việc JPA bao gồm thời gian tải dệt (thiết bị đo đạc) và thường là một hoặc nhiều cấp độ bộ nhớ đệm. Trình quản lý thực thể cần gì hơn để ghi lại các thay đổi và xử lý các cập nhật của các đối tượng được quản lý. Không có cơ sở dễ dàng nào mà tôi biết để hướng dẫn người quản lý thực thể rằng một thực thể nhất định phải được lưu trữ trong một bảng hoặc một tổ chức khác.

Thao tác di chuyển như vậy sẽ ngầm yêu cầu xóa từ một bảng và chèn vào bảng khác. Nếu có các thực thể con thì điều này sẽ khó khăn hơn. Không phải không thể nhớ được bạn nhưng đó là một trường hợp góc bất thường mà tôi không chắc ai có thể làm phiền.

Khuôn khổ SQL/JDBC cấp thấp hơn như Ibatis có thể là đặt cược tốt hơn vì nó sẽ cung cấp cho bạn quyền kiểm soát mà bạn muốn.

Tôi cũng đã suy nghĩ để tự động thay đổi hoặc chỉ định tại chú thích khi chạy.Mặc dù tôi chưa chắc liệu điều đó có thể xảy ra hay không, ngay cả khi tôi không chắc chắn nó sẽ giúp ích gì. Tôi không thể tưởng tượng một người quản lý thực thể hoặc bộ nhớ đệm không bị nhầm lẫn vô vọng bởi điều đó xảy ra. Một khả năng khác mà tôi nghĩ là tự động tạo các lớp con khi chạy (như các lớp con ẩn danh) nhưng vẫn có vấn đề về chú thích và tôi không chắc chắn cách bạn thêm nó vào một đơn vị tồn tại lâu dài.

Nó có thể hữu ích nếu bạn cung cấp thêm một số chi tiết về những gì bạn đang làm và tại sao. Dù nó là gì mặc dù, tôi nghiêng về phía suy nghĩ bạn cần phải suy nghĩ lại những gì bạn đang làm hoặc làm thế nào bạn đang làm nó hoặc bạn cần phải chọn một công nghệ kiên trì khác nhau.

+0

Tôi muốn ở lại trong JPA nếu có thể. –

+0

Câu hỏi 1: Lược đồ bảng được cố định, nhưng tên bảng thì không. Điều này có nghĩa là nếu A, B và C được chỉnh sửa, tất cả chúng sẽ chia sẻ cùng một lược đồ và có các nhận dạng riêng biệt về mặt logic. I E. các mục sẽ không được di chuyển từ A sang B hoặc tương tự. Tôi _could_ về nguyên tắc cùng với việc đọc trong các trang trong một bản đồ mỗi hàng và làm việc với điều đó, nhưng tôi đang cố gắng để có được chức năng "này, ai đó đã cập nhật hàng này". –

+0

Tôi sẽ suy nghĩ một chút về cách giải quyết vấn đề này tốt nhất dựa trên phản hồi và có thể đây là điều quá mức về mặt kỹ thuật. Tôi cũng phải xem xét rằng điều này có thể sẽ được sử dụng trong một thời gian dài vì vậy nó không nên phức tạp không cần thiết cho các nhà bảo trì trong tương lai. –

2

như thay thế cấu hình XML, bạn có thể muốn tự động tạo lớp java với chú thích sử dụng khung thao tác bytecode ưa thích của bạn

1

Nếu bạn không nhớ ràng buộc tự của bạn để Hibernate, bạn có thể sử dụng một số phương pháp mô tả tại https://www.hibernate.org/171.html. Bạn có thể thấy tự mình sử dụng một vài chú thích ngủ đông tùy thuộc vào độ phức tạp của dữ liệu của bạn, khi chúng đi lên và vượt quá đặc tả JPA, vì vậy nó có thể là một mức giá nhỏ để trả.

+0

Tôi thích JPA hơn, nhưng Hibernate cũng là một lựa chọn. Như đã đề cập, lược đồ là tĩnh nhưng tôi sẽ cần phải chỉnh sửa một hoặc nhiều bảng cơ sở dữ liệu trong một phiên duy nhất chỉ khác nhau theo tên bảng. –

8

Bạn có thể chỉ định tên bảng vào thời gian tải thông qua tùy chỉnh ClassLoader ghi lại chú thích @Table trên các lớp khi chúng được tải. Tại thời điểm này, tôi không chắc chắn 100% cách bạn sẽ bảo đảm Hibernate đang tải các lớp của nó thông qua ClassLoader này.

Lớp học được viết lại bằng the ASM bytecode framework.

Cảnh báo: Các lớp này là thử nghiệm.

public class TableClassLoader extends ClassLoader { 

    private final Map<String, String> tablesByClassName; 

    public TableClassLoader(Map<String, String> tablesByClassName) { 
     super(); 
     this.tablesByClassName = tablesByClassName; 
    } 

    public TableClassLoader(Map<String, String> tablesByClassName, ClassLoader parent) { 
     super(parent); 
     this.tablesByClassName = tablesByClassName; 
    } 

    @Override 
    public Class<?> loadClass(String name) throws ClassNotFoundException { 
     if (tablesByClassName.containsKey(name)) { 
      String table = tablesByClassName.get(name); 
      return loadCustomizedClass(name, table); 
     } else { 
      return super.loadClass(name); 
     } 
    } 

    public Class<?> loadCustomizedClass(String className, String table) throws ClassNotFoundException { 
     try { 
      String resourceName = getResourceName(className); 
      InputStream inputStream = super.getResourceAsStream(resourceName); 
      ClassReader classReader = new ClassReader(inputStream); 
      ClassWriter classWriter = new ClassWriter(0); 
      classReader.accept(new TableClassVisitor(classWriter, table), 0); 

      byte[] classByteArray = classWriter.toByteArray(); 

      return super.defineClass(className, classByteArray, 0, classByteArray.length); 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     } 
    } 

    private String getResourceName(String className) { 
     Type type = Type.getObjectType(className); 
     String internalName = type.getInternalName(); 
     return internalName.replaceAll("\\.", "/") + ".class"; 
    } 

} 

Các TableClassLoader dựa trên TableClassVisitor để bắt phương pháp visitAnnotation gọi:

public class TableClassVisitor extends ClassAdapter { 

    private static final String tableDesc = Type.getDescriptor(Table.class); 

    private final String table; 

    public TableClassVisitor(ClassVisitor visitor, String table) { 
     super(visitor); 
     this.table = table; 
    } 

    @Override 
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 
     AnnotationVisitor annotationVisitor; 

     if (desc.equals(tableDesc)) { 
      annotationVisitor = new TableAnnotationVisitor(super.visitAnnotation(desc, visible), table); 
     } else { 
      annotationVisitor = super.visitAnnotation(desc, visible); 
     } 

     return annotationVisitor; 
    } 

} 

Các TableAnnotationVisitor là cuối cùng chịu trách nhiệm về thay đổi name lĩnh vực @Table chú thích:

public class TableAnnotationVisitor extends AnnotationAdapter { 

    public final String table; 

    public TableAnnotationVisitor(AnnotationVisitor visitor, String table) { 
     super(visitor); 
     this.table = table; 
    } 

    @Override 
    public void visit(String name, Object value) { 
     if (name.equals("name")) { 
      super.visit(name, table); 
     } else { 
      super.visit(name, value); 
     } 
    } 

} 

Vì Tôi đã không tìm thấy một số AnnotationAdapter lớp trong thư viện ASM của, đây là một trong tôi đã thực hiện bản thân mình:

public class AnnotationAdapter implements AnnotationVisitor { 

    private final AnnotationVisitor visitor; 

    public AnnotationAdapter(AnnotationVisitor visitor) { 
     this.visitor = visitor; 
    } 

    @Override 
    public void visit(String name, Object value) { 
     visitor.visit(name, value); 
    } 

    @Override 
    public AnnotationVisitor visitAnnotation(String name, String desc) { 
     return visitor.visitAnnotation(name, desc); 
    } 

    @Override 
    public AnnotationVisitor visitArray(String name) { 
     return visitor.visitArray(name); 
    } 

    @Override 
    public void visitEnd() { 
     visitor.visitEnd(); 
    } 

    @Override 
    public void visitEnum(String name, String desc, String value) { 
     visitor.visitEnum(name, desc, value); 
    } 

} 
+2

ý tưởng rất tuyệt vời! –

+2

Tuyệt vời, Làm việc với một lượng nhỏ mát-xa, sử dụng nó để quản lý chú thích DynamoDBTable. Bây giờ để xem liệu vùng chứa web của tôi có cho phép trình tải lớp tùy chỉnh hay không. – stjohnroe

4

Nghe có vẻ với tôi như những gì bạn đang sau là Overriding the JPA Annotations with an ORM.xml.

Điều này sẽ cho phép bạn chỉ định Chú thích nhưng sau đó ghi đè chúng chỉ khi chúng thay đổi. Tôi đã thực hiện tương tự để ghi đè số schema trong chú thích @Table vì nó thay đổi giữa các môi trường của tôi.

Sử dụng phương pháp này, bạn cũng có thể ghi đè lên tên bảng trên các thực thể riêng lẻ.

[Đang cập nhật câu trả lời này vì nó không phải là tài liệu tốt và người khác có thể tìm thấy nó hữu ích]

Dưới đây là orm tôi.file xml (lưu ý rằng tôi chỉ trọng các lược đồ và rời khỏi chú thích JPA & Hibernate khác một mình, tuy nhiên thay đổi bàn ở đây là hoàn toàn có thể. Cũng lưu ý rằng tôi đang chú thích trên các lĩnh vực không phải là Getter)

<?xml version="1.0" encoding="UTF-8"?> 
<entity-mappings 
    xmlns="http://java.sun.com/xml/ns/persistence/orm" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd" 
    version="1.0"> 
    <package>models.jpa.eglobal</package> 
    <entity class="MyEntityOne" access="FIELD"> 
     <table name="ENTITY_ONE" schema="MY_SCHEMA"/> 
    </entity> 
    <entity class="MyEntityTwo" access="FIELD"> 
     <table name="ENTITY_TWO" schema="MY_SCHEMA"/> 
    </entity> 
</entity-mappings> 
+0

hoàn hảo này giải quyết được vấn đề cho tôi. ** BTW: ** bạn phải nhưng orm.xml trong META-INF theo classpath của bạn –

Các vấn đề liên quan