Хорошо, это несколько лет спустя, но с java 8 вы можете использовать Comparator.naturalOrder ():
http://docs.oracle.com/javase/8/docs/api/ java / util / Comparator.html # naturalOrder -
Из javadoc:
static > Comparator naturalOrder()
Возвращает компаратор, который сравнивает сопоставимые объекты в естественном порядке. Возвращенный компаратор является сериализуемым и выдает исключение NullPointerException при сравнении нуля.
Вам необходимо использовать XML-версию конфигурации, а не аннотации. Таким образом, вы можете динамически генерировать XML во время выполнения.
Или, может быть, вас заинтересует что-то вроде Dynamic JPA ?
Я думаю, что необходимо дополнительно прояснить вопросы, связанные с этой проблемой.
Первый вопрос: известен ли набор таблиц, в которых может храниться объект? Под этим я подразумеваю, что вы не создаете таблицы динамически во время выполнения и не хотите связывать с ними сущности. Этот сценарий требует, чтобы, скажем, три таблицы были известны во время компиляции . Если это так, вы можете использовать наследование JPA. В документации OpenJPA подробно описана таблица для стратегии наследования классов .
Преимущество этого метода в том, что это чистый JPA. Однако он имеет ограничения, так как таблицы должны быть известны, и вы не можете легко изменить, в какой таблице хранится данный объект (если это требуется для вас), так же, как объекты в системах OO обычно не меняют класс или введите.
Если вы хотите, чтобы это было действительно динамично и перемещало объекты между таблицами (по сути), то я не уверен, что JPA - правильный инструмент для вас. Ужасно много магии тратится на то, чтобы заставить JPA работать, включая ткачество во время загрузки (инструментарий) и обычно один или несколько уровней кэширования. Более того, менеджеру сущностей необходимо записывать изменения и обрабатывать обновления управляемых объектов. Насколько мне известно, не существует простого средства, позволяющего указать менеджеру сущностей, что данная сущность должна храниться в той или иной таблице.
Такая операция перемещения неявно потребовала бы удаления из одной таблицы и вставки в другую. Если есть дочерние сущности, это становится труднее. Не исключено, заметьте, но это такой необычный угловой случай, я не уверен, что кто-нибудь когда-нибудь станет беспокоиться.
Фреймворк SQL / JDBC более низкого уровня, такой как Ibatis , может быть лучшим выбором, поскольку он даст вы тот элемент управления, который вам нужен.
Я также подумал о динамическом изменении или назначении аннотаций во время выполнения. Хотя я еще не уверен, возможно ли это, даже если это так, я не уверен, что это обязательно поможет. Я не могу себе представить, чтобы менеджер сущностей или кеширование не безнадежно сбивался с толку из-за того, что происходит.
Другая возможность, о которой я подумал, - это динамическое создание подклассов во время выполнения (как анонимные подклассы), но при этом все еще есть проблема с аннотациями, и снова я не уверен, как вы добавляете это к существующей единице сохранения состояния.
Это могло бы помочь если вы предоставили более подробную информацию о том, что вы делаете и почему. Как бы то ни было, я склоняюсь к мысли, что вам нужно переосмыслить то, что вы делаете или как вы это делаете, или вам нужно выбрать другую технологию сохранения.
в качестве альтернативы конфигурации XML вы можете захотеть динамически сгенерировать класс java с аннотацией, используя предпочитаемую вами платформу манипулирования байт-кодом
Если вы не против привязать себя к Hibernate, вы можете использовать некоторые из методов, описанных на https://www.hibernate.org/171.html . Вы можете обнаружить, что используете довольно много аннотаций гибернации в зависимости от сложности ваших данных, поскольку они выходят за рамки спецификации JPA, поэтому это может быть небольшой платой.
Вы можете указать имя таблицы во время загрузки с помощью специального 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);
}
}