задача была скопировать все из одной базы данных в другую: юзер нам заливает hsqldb запакованную в tar, наша задача добавить все записи из этой базу в нашу основную postgres.
проблема была в том что там 3 таблицы, связанные ключами.
первая таблица содержит обычную строчку и ссылку на вторую таблицу.
во второй таблице хранится дикий ужас, представляющий файловую систему со всей ее древовидностью.
и иногда она содержит ссылку на третью таблицу.
просто скопировать и вставить ряды не получится: id всех элементов изменяются, а, значит, надо менять и ссылки на них.
просто запустить две сессии hibernate, в одной достать а в другую вставить обьект тоже не получится: тоже нет обновления id'шников для связанных с ним объектов.
выгрести объект, создать другой объект этого же класса, скопировать в него важные данные и вставить в базу тоже проблематично: все, что мы знаем про объект из второй таблицы (файлы) это то, что он является объектом, наследующим абстрактный класс ParsedFile, а таких классов около 10 штук. но нам на помощь приходит метод clone() из класса Object. как говорит документация, x.clone().getClass() == x.getClass(). т.е. то что нам нужно: не зная каков на самом деле класс нашего объекта, мы можем создать объект того же класса, да еще со всеми полями, скопированными из исходного объекта.
но в нашем классе
class MediaCollectionEntry {
Long id;
String name;
ParsedFile firstChild;
...
}
есть ссылка на дочерний объект, который тоже надо клонировать, для этого переопределяем метод clone() этого класса:
public MediaCollectionEntry clone() throws CloneNotSupportedException {
MediaCollectionEntry clone = (MediaCollectionEntry) super.clone();
clone.setId(null);
clone.setFirstChild(clone.getFirstChild().clone());
return clone;
}
мы обнуляем id, чтобы при вставке в базу он подставился самостоятельно и заменяем firstChild на его клон, где тоже будет обнулен id.
abstract class ParsedFile implements Cloneable {
protected Long id;
protected String fileName;
protected Set
public Set
return children;
}
public void setChildren(Set
this.children = children;
}
public void addChild(ParsedFile child) {
child.setParent(this);
children.add(child);
}
...
public ParsedFile clone() throws CloneNotSupportedException {
ParsedFile clone = (ParsedFile) super.clone();
clone.setId(null);
Set
clone.setChildren(new HashSet
for(ParsedFile child : cloneChildren) {
clone.addChild(child.clone());
}
return clone;
}
}
и тут побежала рекурисия по всем деткам и внучкам)
в результате этого копирование данных из одной базы данных в другую свелось к двум строчкам:
MediaCollectionEntry mce1 = session1.load(MediaCollectionEntry.class, mediaCollectionEntryId);
MediaCollectionEntry mce2 = mce1.clone();
session2.save(mce2);
это скопирует не только MediaCollectionEntry но и его firstChild и всех детей к нему привязанных.
я к чему весь этот непонятный бред пишу. у меня по нему два вопроса:
не является ли это решение нерациональным?
как бы вы такое сделали на пхп?
p.s. связь с третьей таблицей есть у некоторых деток ParsedFile'a а именно у наследующих абстрактный класс AudioFile:
abstract class AudioFile extends ParsedFile implements Cloneable {
AudioInfo audioInfo;
...
public AudioFile clone() throws CloneNotSupportedException {
// этот вызов пойдет к методу clone() в классе ParsedFile
// скопирует все его поля а потом обработает здесь audioInfo
AudioFile clone = (AudioFile) super.clone();
AudioInfo audioInfo = clone.getAudioInfo().clone();
audioInfo.setFileObject(clone);
clone.setAudioInfo(audioInfo);
return clone;
}
}

0 comments:
Post a Comment