JPA: Как я определяю имя таблицы, соответствующее классу во времени выполнения?

Хорошо, это несколько лет спустя, но с java 8 вы можете использовать Comparator.naturalOrder ():

http://docs.oracle.com/javase/8/docs/api/ java / util / Comparator.html # naturalOrder -

Из javadoc:

static > Comparator naturalOrder()

Возвращает компаратор, который сравнивает сопоставимые объекты в естественном порядке. Возвращенный компаратор является сериализуемым и выдает исключение NullPointerException при сравнении нуля.

13
задан Thorbjørn Ravn Andersen 25 May 2009 в 13:52
поделиться

4 ответа

Вам необходимо использовать XML-версию конфигурации, а не аннотации. Таким образом, вы можете динамически генерировать XML во время выполнения.

Или, может быть, вас заинтересует что-то вроде Dynamic JPA ?

Я думаю, что необходимо дополнительно прояснить вопросы, связанные с этой проблемой.

Первый вопрос: известен ли набор таблиц, в которых может храниться объект? Под этим я подразумеваю, что вы не создаете таблицы динамически во время выполнения и не хотите связывать с ними сущности. Этот сценарий требует, чтобы, скажем, три таблицы были известны во время компиляции . Если это так, вы можете использовать наследование JPA. В документации OpenJPA подробно описана таблица для стратегии наследования классов .

Преимущество этого метода в том, что это чистый JPA. Однако он имеет ограничения, так как таблицы должны быть известны, и вы не можете легко изменить, в какой таблице хранится данный объект (если это требуется для вас), так же, как объекты в системах OO обычно не меняют класс или введите.

Если вы хотите, чтобы это было действительно динамично и перемещало объекты между таблицами (по сути), то я не уверен, что JPA - правильный инструмент для вас. Ужасно много магии тратится на то, чтобы заставить JPA работать, включая ткачество во время загрузки (инструментарий) и обычно один или несколько уровней кэширования. Более того, менеджеру сущностей необходимо записывать изменения и обрабатывать обновления управляемых объектов. Насколько мне известно, не существует простого средства, позволяющего указать менеджеру сущностей, что данная сущность должна храниться в той или иной таблице.

Такая операция перемещения неявно потребовала бы удаления из одной таблицы и вставки в другую. Если есть дочерние сущности, это становится труднее. Не исключено, заметьте, но это такой необычный угловой случай, я не уверен, что кто-нибудь когда-нибудь станет беспокоиться.

Фреймворк SQL / JDBC более низкого уровня, такой как Ibatis , может быть лучшим выбором, поскольку он даст вы тот элемент управления, который вам нужен.

Я также подумал о динамическом изменении или назначении аннотаций во время выполнения. Хотя я еще не уверен, возможно ли это, даже если это так, я не уверен, что это обязательно поможет. Я не могу себе представить, чтобы менеджер сущностей или кеширование не безнадежно сбивался с толку из-за того, что происходит.

Другая возможность, о которой я подумал, - это динамическое создание подклассов во время выполнения (как анонимные подклассы), но при этом все еще есть проблема с аннотациями, и снова я не уверен, как вы добавляете это к существующей единице сохранения состояния.

Это могло бы помочь если вы предоставили более подробную информацию о том, что вы делаете и почему. Как бы то ни было, я склоняюсь к мысли, что вам нужно переосмыслить то, что вы делаете или как вы это делаете, или вам нужно выбрать другую технологию сохранения.

15
ответ дан 1 December 2019 в 20:01
поделиться

в качестве альтернативы конфигурации XML вы можете захотеть динамически сгенерировать класс java с аннотацией, используя предпочитаемую вами платформу манипулирования байт-кодом

3
ответ дан 1 December 2019 в 20:01
поделиться

Если вы не против привязать себя к Hibernate, вы можете использовать некоторые из методов, описанных на https://www.hibernate.org/171.html . Вы можете обнаружить, что используете довольно много аннотаций гибернации в зависимости от сложности ваших данных, поскольку они выходят за рамки спецификации JPA, поэтому это может быть небольшой платой.

1
ответ дан 1 December 2019 в 20:01
поделиться

Вы можете указать имя таблицы во время загрузки с помощью специального ClassLoader , который перезаписывает аннотацию @Table для классов по мере их загружены. На данный момент я не уверен на 100%, как можно гарантировать, что Hibernate загружает свои классы через этот ClassLoader.

Классы переписываются с использованием структуры байт-кода ASM .

Предупреждение: ] Эти классы являются экспериментальными.

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";
    }

}

TableClassLoader использует TableClassVisitor для перехвата вызовов метода visitAnnotation :

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;
    }

}

The TableAnnotationVisitor ] в конечном итоге отвечает за изменение поля name аннотации @Table :

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);
        }
    }

}

Потому что мне не удалось найти класс AnnotationAdapter в библиотеке ASM , вот один, который я сделал сам:

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);
    }

}
9
ответ дан 1 December 2019 в 20:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: