Статьи

Morphia и MongoDB: развивающиеся структуры документов

В моем предыдущем посте о Morphia я рассмотрел некоторые типичные способы использования и упомянул некоторые предостережения и обходные пути для известных проблем. Я показал, как легко работать с Morphia и как чисто он взаимодействует с миром Java.

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

Изменение схемы

Как знает почти каждый, кто работал с базами данных в мире разработки, схемы постоянно развиваются. Поля устаревают или сразу удаляются, таблицы устаревают, добавляются новые поля и т. Д.

Хотя многие из этих проблем можно избежать с помощью хранилища данных без схемы, такого как MongoDB, иногда нам все еще нужна специальная обработка изменений, а в случае с Morphia мы, по сути, определили схему, поэтому нам нужно найти способы справиться с ней. это. Самое приятное в этом то, что Morphia делает его очень чистым и простым, чем вы можете увидеть практически в любом ORM.

Устаревшие поля

Один хороший пример — устаревшее поле, которое было заменено другим полем. Давайте представим, что у вас есть система отслеживания ошибок с документами, которые выглядят примерно так:

1
2
3
4
5
6
{
  _id:1,
  desc: "IE Rendering broken on intranet site",
  componentName: "INTRANET",
  dateCreated: ISODate("2011-09-06T20:52:50.258Z")
}

Вот определение Morphia:

1
2
3
4
5
6
7
8
@Entity("issues")
class Issue {
  @Id private long id;
  private String desc;
  private String componentName;
 
  private Date dateCreated = new Date();
}

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

К счастью, у нас есть кое-что в инструментарии Morphia , предназначенное именно для этого, — аннотация @AlsoLoad . Эта аннотация позволяет нам заполнить поле POJO одним из нескольких возможных источников. Мы просто обновляем наше отображение Morphia, чтобы указать старое имя поля, и мы можем легко удалить ссылки на старое поле, ничего не нарушая. Это сохраняет наш код и документы в чистоте.

01
02
03
04
05
06
07
08
09
10
@Entity("issues")
class Issue {
  @Id private long id;
  private String desc;
 
  @AlsoLoad("componentName") // handle old componentName field
  private String affects;
 
  private Date dateCreated = new Date();
}

Итак, здесь мы определили автоматический перевод нашего старого поля без необходимости обновлять документы или писать специальную логику в нашем классе POJO, чтобы обрабатывать документы по-разному в зависимости от того, когда они были созданы.

Одно важное замечание: в этом примере, если существуют поле воздействия и старое поле componentName , Morphia сгенерирует исключение, поэтому не пытайтесь использовать это для чего-либо кроме устаревших полей или, возможно, для заполнения одного поля двумя взаимоисключающими свойства.

Поддержка только для чтения устаревших полей

Другая возможность состоит в том, что вам просто нужно поддерживать старое поле в документе, которое приложение больше не записывает. Это очень просто: используйте аннотацию @NotSaved . Когда вы используете это в поле, данные будут загружены, но не записаны Morphia.

В нашем предыдущем примере мы с такой же легкостью могли бы решить просто поддерживать отображение для старого поля, но не обрабатывать его, заполняя его в полеффектов , поэтому давайте немного изменим наш Morphia POJO, чтобы показать, как используется @NotSaved .

01
02
03
04
05
06
07
08
09
10
11
12
@Entity("issues")
class Issue {
  @Id private long id;
  private String desc;
 
  private String affects;
 
  @NotSaved("componentName") // load old componentName field for display only
  private String componentName
 
  private Date dateCreated = new Date();
}

Замена поля встроенным объектом

Теперь, что если наш componentName поле фактически изменилось на сложный компонентный объект, который имеет имя, версию и номер сборки? Это немного сложнее, так как мы хотим заменить одно поле несколькими. Мы не можем пытаться загрузить поле из нескольких источников, поскольку они имеют разные структуры. Конечно, мы можем использовать встроенный объект для хранения информации о сложных компонентах, но как мы можем заставить наш код работать в любом случае без необходимости обновления наших документов?

В этом случае простейшим подходом будет использование комбинации трех аннотаций. Сначала мы пометим старое поле аннотацией @NotSaved , введем новый встроенный компонент   объект, используя аннотацию @Embedded , и, наконец, воспользуйтесь еще одной аннотацией, которую предоставляет Morphia@PostLoad . Этот позволяет нам иметь метод, который выполняется после заполнения POJO из MongoDB.

Вот пример:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Entity("issues")
class Issue {
  @Id private long id;
  private String desc;
 
  private String affects;
 
  @NotSaved("componentName") // load old componentName to convert to component
  private String componentName
 
  @Embedded // our new complex Component
  private Component component;
 
  private Date dateCreated = new Date();
  // getters and setters ...
 
  @PostLoad
  protected void handleComponent() {
      if (component == null && componentName != null) {
        component = new Component(componentName, null, null);
      }
  }
}
 
class Component {
  private String componentName;
  private Long version;
  private Long buildNumber;
 
  public Component(String componentName, Long version, Long buildNumber) {
    // ...
  }
 
  // getters and setters ...
}

В этом случае мы могли бы удалить метод getter и setter для поля componentName , чтобы наш отображенный объект предоставил только новый и улучшенный интерфейс.

Вывод

Используя мощные инструменты, которые Morphia предоставляет нам благодаря поддержке аннотаций, мы можем достичь следующих целей:

  1. Позвольте нашей структуре документа адаптироваться к приложению и оставаться чистым.
  2. Легко обрабатывать изменения структуры в нашем Java-коде без подверженного ошибкам кода.
  3. Предоставляйте только новую схему, поддерживая старую (действительно устаревший старый код и поля).

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

Ссылки: Morphia и MongoDB: развивающиеся структуры документов от нашего партнера JCG Крейга Фликхеля в блоге Carfey Software