[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"question-jira-chto-takoe-active-objects-i-kak-s-nimi-rabotat":3},{"id":4,"slug":5,"topicId":6,"topicSlug":7,"topicName":8,"topicEmoji":9,"question":10,"answer":11,"codeLang":12,"codeSrc":12,"important":12,"commonMistakes":12,"modernUsage":12,"difficulty":13,"tags":14,"related":19,"progress":20,"seo":21},923,"chto-takoe-active-objects-i-kak-s-nimi-rabotat",27,"jira","Jira","📋","Что такое Active Objects и как с ними работать?","Active Objects (AO) — ORM-фреймворк для плагинов Jira Data Center, позволяющий плагинам хранить данные в БД Jira без создания таблиц вручную. По концепции похож на JPA, но значительно легковеснее.\n\n> **Аналогия из жизни:** Active Objects — это как встроенный шкаф в съёмной квартире. Вы не строите мебель сами (не создаёте таблицы SQL), а описываете, что хотите хранить (интерфейсы), и фреймворк сам организует пространство. При выезде (удалении плагина) шкаф убирается автоматически.\n\n### Ключевые особенности\n\n- Таблицы создаются автоматически из Java-интерфейсов\n- Префикс таблиц — уникальный для каждого плагина (избежание конфликтов)\n- Поддержка миграций (upgrade tasks)\n- Работает с БД Jira (PostgreSQL, MySQL, Oracle, MS SQL)\n\n### Определение сущности (entity)\n\n```java\n@Table(\"TASK_CONFIG\")\n@Preload  \u002F\u002F загружать все поля при выборке (оптимизация)\npublic interface TaskConfig extends Entity {\n    \u002F\u002F Entity предоставляет int getID() автоматически\n\n    @StringLength(255)\n    @NotNull\n    String getProjectKey();\n    void setProjectKey(String projectKey);\n\n    @StringLength(StringLength.UNLIMITED)\n    String getConfiguration();\n    void setConfiguration(String configuration);\n\n    boolean isEnabled();\n    void setEnabled(boolean enabled);\n\n    @Default(\"0\")\n    int getPriority();\n    void setPriority(int priority);\n\n    \u002F\u002F Связь один-ко-многим\n    @OneToMany(reverse = \"getTaskConfig\")\n    TaskExecution[] getExecutions();\n}\n\n@Table(\"TASK_EXEC\")\npublic interface TaskExecution extends Entity {\n\n    @NotNull\n    TaskConfig getTaskConfig();\n    void setTaskConfig(TaskConfig config);\n\n    @StringLength(50)\n    String getStatus();\n    void setStatus(String status);\n\n    long getStartedAt();\n    void setStartedAt(long startedAt);\n\n    long getFinishedAt();\n    void setFinishedAt(long finishedAt);\n\n    @StringLength(StringLength.UNLIMITED)\n    String getErrorMessage();\n    void setErrorMessage(String message);\n}\n```\n\n### Регистрация AO в atlassian-plugin.xml\n\n```xml\n\u003Cao key=\"ao-module\">\n    \u003Cdescription>Active Objects модуль\u003C\u002Fdescription>\n    \u003Centity>com.example.plugin.ao.TaskConfig\u003C\u002Fentity>\n    \u003Centity>com.example.plugin.ao.TaskExecution\u003C\u002Fentity>\n\u003C\u002Fao>\n```\n\n### CRUD-операции\n\n\u003Cdetails>\n\u003Csummary>Код TaskConfigService\u003C\u002Fsummary>\n\n```java\n@Named\npublic class TaskConfigService {\n\n    private final ActiveObjects ao;\n\n    @Inject\n    public TaskConfigService(@ComponentImport ActiveObjects ao) {\n        this.ao = ao;\n    }\n\n    \u002F\u002F CREATE\n    public TaskConfig create(String projectKey, String config) {\n        return ao.executeInTransaction(() -> {\n            TaskConfig entity = ao.create(TaskConfig.class);\n            entity.setProjectKey(projectKey);\n            entity.setConfiguration(config);\n            entity.setEnabled(true);\n            entity.setPriority(0);\n            entity.save();\n            return entity;\n        });\n    }\n\n    \u002F\u002F READ — по ID\n    public TaskConfig getById(int id) {\n        return ao.get(TaskConfig.class, id);\n    }\n\n    \u002F\u002F READ — поиск по условию\n    public TaskConfig[] findByProject(String projectKey) {\n        return ao.find(TaskConfig.class,\n                Query.select()\n                        .where(\"PROJECT_KEY = ?\", projectKey)\n                        .order(\"PRIORITY DESC\")\n                        .limit(100));\n    }\n\n    \u002F\u002F UPDATE\n    public void update(int id, String newConfig) {\n        ao.executeInTransaction(() -> {\n            TaskConfig entity = ao.get(TaskConfig.class, id);\n            if (entity != null) {\n                entity.setConfiguration(newConfig);\n                entity.save();\n            }\n            return null;\n        });\n    }\n\n    \u002F\u002F DELETE\n    public void delete(int id) {\n        ao.executeInTransaction(() -> {\n            TaskConfig entity = ao.get(TaskConfig.class, id);\n            if (entity != null) {\n                for (TaskExecution exec : entity.getExecutions()) {\n                    ao.delete(exec);\n                }\n                ao.delete(entity);\n            }\n            return null;\n        });\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### Миграция (Upgrade Task)\n\n```java\npublic class UpgradeTask001 implements ActiveObjectsUpgradeTask {\n\n    @Override\n    public ModelVersion getModelVersion() {\n        return ModelVersion.valueOf(\"1\");\n    }\n\n    @Override\n    public void upgrade(ModelVersion currentVersion, ActiveObjects ao) {\n        ao.migrate(TaskConfig.class);\n    }\n}\n```\n\n```xml\n\u003Cao key=\"ao-module\">\n    \u003Centity>com.example.plugin.ao.TaskConfig\u003C\u002Fentity>\n    \u003Centity>com.example.plugin.ao.TaskExecution\u003C\u002Fentity>\n    \u003CupgradeTask>com.example.plugin.upgrade.UpgradeTask001\u003C\u002FupgradeTask>\n\u003C\u002Fao>\n```\n\n### Частые ошибки\n\n- Забыть `@Preload` — N+1 проблема, каждый доступ к полю = отдельный SELECT\n- Использование Java-имён полей в Query вместо SQL-имён: `\"projectKey\"` вместо `\"PROJECT_KEY\"`\n- Не обернуть запись в транзакцию — данные могут быть в inconsistent-состоянии\n- Отсутствие upgrade tasks — при первом деплое таблицы не создаются\n- Хранение больших данных без `@StringLength(UNLIMITED)` — дефолтная длина строки 255 символов\n\n### Как используется в 2026\n\n- Active Objects остаётся стандартом для DC-плагинов\n- Нет альтернатив (JPA\u002FHibernate недоступны из-за OSGi-изоляции)\n- Для Cloud (Forge) аналог — Forge Storage API и Entity Storage\n- При планировании миграции DC -> Cloud нужно продумать перенос данных из AO в Cloud storage\n\n> **На собеседовании:** подчеркните, что AO — единственный поддерживаемый способ хранения данных плагина в БД Jira DC. Обязательно упомяните `@Preload` (без него — N+1), UPPERCASE-имена колонок в Query и `executeInTransaction()` для записи. Это практические знания, которые показывают реальный опыт.","","middle",[15,16,17,18,7],"databases","orm","active-objects","jira-plugin",[],null,{"title":22,"description":23,"ogTitle":24,"ogDescription":25,"keywords":26,"schemaAnswer":36,"featuredSnippetReady":37},"Что такое Active Objects и как с ними работать — Gymterview","Active Objects в Jira DC: определение сущностей через интерфейсы, CRUD-операции, @Preload, Upgrade Tasks, миграции. Примеры кода на Java.","Active Objects: ORM для плагинов Jira Data Center — Gymterview","Как работать с Active Objects: entity-интерфейсы, CRUD, @Preload для производительности, upgrade tasks для миграций.",[27,28,29,30,31,32,33,34,35],"Active Objects","AO","Jira ORM","@Preload","Upgrade Task","CRUD","executeInTransaction","Jira DC","Java","Active Objects — ORM-фреймворк для хранения данных плагинов в БД Jira DC. Сущности определяются как Java-интерфейсы с геттерами\u002Fсеттерами. Таблицы создаются автоматически с уникальным префиксом. @Preload обязателен (без него — N+1). Имена колонок в Query — UPPERCASE (PROJECT_KEY). Запись — через executeInTransaction(). Upgrade Tasks — для миграций схемы.",true]