ISIS: проблемы с сериализацией полей Blob / Clob - PullRequest
0 голосов
/ 28 марта 2019

Редактировать: Решение: Обновление до ISIS 1.17.0 и установка свойства isis.persistor.datanucleus.standaloneCollection.bulkLoad=false решили первые две проблемы.


Я использую Apache ISIS 1.16.2 и пытаюсь сохранить содержимое Blob / Clob в базе данных MariaDB (v10.1.35). Поэтому я использую соединитель БД org.mariadb.jdbc.mariadb-java-client (v2.3.0) и в коде аннотацию @Persistent, как показано во многих примерах и документации ISIS.

Используя приведенный ниже код, я получаю один столбец с именем content_name (в котором объект Blob сериализуется в двоичной форме) вместо трех столбцов content_name, content_mimetype и content_bytes.

Это класс Document с полем Blob content:

@PersistenceCapable(identityType = IdentityType.DATASTORE)
@DatastoreIdentity(strategy = IdGeneratorStrategy.IDENTITY, column = "id")
@DomainObject(editing = Editing.DISABLED, autoCompleteRepository = DocumentRepository.class, objectType = "Document")
@Getter
// ...
public class Document implements Comparable<Document> {

    @Persistent(
        defaultFetchGroup = "false",
        columns = {
            @Column(name = "content_name"),
            @Column(name = "content_mimetype"),
            @Column(name = "content_bytes",
                jdbcType = "BLOB",
                sqlType = "LONGVARBINARY")
            })
    @Nonnull
    @Column(allowsNull = "false")
    @Property(optionality = Optionality.MANDATORY)
    private Blob content;

    // ...

    @Column(allowsNull = "false")
    @Property
    private Date created = new Date();

    public Date defaultCreated() {
        return new Date();
    }

    @Column(allowsNull = "true")
    @Property
    @Setter
    private String owner;

    // ...
}

Это создает следующую схему для класса DomainObject Document с одним столбцом для поля Blob:

CREATE TABLE `document` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `content_name` mediumblob,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `owner` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Обычно класс org.apache.isis.objectstore.jdo.datanucleus.valuetypes.IsisBlobMapping платформы ISIS должен выполнять сопоставление. Но похоже, что этот маппер как-то не участвует ...

1. Вопрос: Как разделить поле Blob на три столбца (как описано выше и во многих демонстрационных проектах). Даже если я переключусь на HSQLDB, я все равно получу только один столбец, так что это может не быть проблемой с MariaDB.

2. Вопрос: Если я использую поле Blob / Clob в классе, который наследуется от другого класса DomainObject, я часто получаю org.datanucleus.exceptions.NucleusException (трассировка стека, см. Ниже), и я не могу сделать ни голову, ни хвост. Каковы потенциальные подводные камни при работе с наследованием? Почему я получаю это исключение?

3. Вопрос: Мне нужно хранить документы, принадлежащие объектам домена (как вы уже догадались). Правильный способ сделать это - хранить документы в дереве файловой системы, а не в базе данных (которая также имеет некоторые ограничения по размеру для данных объекта) и ссылаться на файлы в объекте. В документации Datanucleus я нашел расширение serializeToFileLocation, которое должно делать именно это. Я попробовал это, добавив строку @Extension(vendorName="datanucleus", key="serializeToFileLocation" value="document-repository") в поле Blob, но ничего не произошло. Поэтому мой вопрос: совместимо ли это расширение Datanucleus с Apache Isis?

Если это расширение конфликтует с Isis, возможно ли иметь javax.jdo.listener.StoreLifecycleListener или org.apache.isis.applib.AbstractSubscriber, который хранит BLOB-объект в файловой системе перед сохранением объекта домена в базе данных и восстановлением его перед загрузкой? Есть ли лучшие решения?

Вот и все пока. Заранее спасибо! ; -)


Трассировка стека к вопросу 2:

... (other Wicket related stack trace)
Caused by: org.datanucleus.exceptions.NucleusException: Creation of SQLExpression for mapping "org.datanucleus.store.rdbms.mapping.java.SerialisedMapping" caused error
        at org.datanucleus.store.rdbms.sql.expression.SQLExpressionFactory.newExpression(SQLExpressionFactory.java:199)
        at org.datanucleus.store.rdbms.sql.expression.SQLExpressionFactory.newExpression(SQLExpressionFactory.java:155)
        at org.datanucleus.store.rdbms.request.LocateBulkRequest.getStatement(LocateBulkRequest.java:158)
        at org.datanucleus.store.rdbms.request.LocateBulkRequest.execute(LocateBulkRequest.java:283)
        at org.datanucleus.store.rdbms.RDBMSPersistenceHandler.locateObjects(RDBMSPersistenceHandler.java:564)
        at org.datanucleus.ExecutionContextImpl.findObjects(ExecutionContextImpl.java:3313)
        at org.datanucleus.api.jdo.JDOPersistenceManager.getObjectsById(JDOPersistenceManager.java:1850)
        at org.apache.isis.core.runtime.system.persistence.PersistenceSession.loadPersistentPojos(PersistenceSession.java:1010)
        at org.apache.isis.core.runtime.system.persistence.PersistenceSession.adaptersFor(PersistenceSession.java:1603)
        at org.apache.isis.core.runtime.system.persistence.PersistenceSession.adaptersFor(PersistenceSession.java:1573)
        at org.apache.isis.viewer.wicket.model.models.EntityCollectionModel$Type$1.loadInBulk(EntityCollectionModel.java:107)
        at org.apache.isis.viewer.wicket.model.models.EntityCollectionModel$Type$1.load(EntityCollectionModel.java:93)
        at org.apache.isis.viewer.wicket.model.models.EntityCollectionModel.load(EntityCollectionModel.java:454)
        at org.apache.isis.viewer.wicket.model.models.EntityCollectionModel.load(EntityCollectionModel.java:70)
        at org.apache.wicket.model.LoadableDetachableModel.getObject(LoadableDetachableModel.java:135)
        at org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.CollectionContentsSortableDataProvider.size(CollectionContentsSortableDataProvider.java:68)
        at org.apache.wicket.markup.repeater.data.DataViewBase.internalGetItemCount(DataViewBase.java:142)
        at org.apache.wicket.markup.repeater.AbstractPageableView.getItemCount(AbstractPageableView.java:235)
        at org.apache.wicket.markup.repeater.AbstractPageableView.getRowCount(AbstractPageableView.java:216)
        at org.apache.wicket.markup.repeater.AbstractPageableView.getViewSize(AbstractPageableView.java:314)
        at org.apache.wicket.markup.repeater.AbstractPageableView.getItemModels(AbstractPageableView.java:99)
        at org.apache.wicket.markup.repeater.RefreshingView.onPopulate(RefreshingView.java:93)
        at org.apache.wicket.markup.repeater.AbstractRepeater.onBeforeRender(AbstractRepeater.java:124)
        at org.apache.wicket.markup.repeater.AbstractPageableView.onBeforeRender(AbstractPageableView.java:115)
        at org.apache.wicket.Component.internalBeforeRender(Component.java:950)
        at org.apache.wicket.Component.beforeRender(Component.java:1018)
        at org.apache.wicket.MarkupContainer.onBeforeRenderChildren(MarkupContainer.java:1825)
        ... 81 more
 Caused by: org.datanucleus.exceptions.NucleusException: Unable to create SQLExpression for mapping of type "org.datanucleus.store.rdbms.mapping.java.SerialisedMapping" since not supported
        at org.datanucleus.store.rdbms.sql.expression.SQLExpressionFactory#newExpression(SQLExpressionFactory.java:189)
        at org.datanucleus.store.rdbms.sql.expression.SQLExpressionFactory#newExpression(SQLExpressionFactory.java:155)
        at org.datanucleus.store.rdbms.request.LocateBulkRequest#getStatement(LocateBulkRequest.java:158)
        at org.datanucleus.store.rdbms.request.LocateBulkRequest#execute(LocateBulkRequest.java:283)
        at org.datanucleus.store.rdbms.RDBMSPersistenceHandler#locateObjects(RDBMSPersistenceHandler.java:564)
        at org.datanucleus.ExecutionContextImpl#findObjects(ExecutionContextImpl.java:3313)
        at org.datanucleus.api.jdo.JDOPersistenceManager#getObjectsById(JDOPersistenceManager.java:1850)
        at org.apache.isis.core.runtime.system.persistence.PersistenceSession#loadPersistentPojos(PersistenceSession.java:1010)
        at org.apache.isis.core.runtime.system.persistence.PersistenceSession#adaptersFor(PersistenceSession.java:1603)
        at org.apache.isis.core.runtime.system.persistence.PersistenceSession#adaptersFor(PersistenceSession.java:1573)
        at org.apache.isis.viewer.wicket.model.models.EntityCollectionModel$Type$1#loadInBulk(EntityCollectionModel.java:107)
        at org.apache.isis.viewer.wicket.model.models.EntityCollectionModel$Type$1#load(EntityCollectionModel.java:93)
        at org.apache.isis.viewer.wicket.model.models.EntityCollectionModel#load(EntityCollectionModel.java:454)
        at org.apache.isis.viewer.wicket.model.models.EntityCollectionModel#load(EntityCollectionModel.java:70)
        at org.apache.wicket.model.LoadableDetachableModel#getObject(LoadableDetachableModel.java:135)
        at org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.CollectionContentsSortableDataProvider#size(CollectionContentsSortableDataProvider.java:68)
        at org.apache.wicket.markup.repeater.data.DataViewBase#internalGetItemCount(DataViewBase.java:142)
        at org.apache.wicket.markup.repeater.AbstractPageableView#getItemCount(AbstractPageableView.java:235)
        at org.apache.wicket.markup.repeater.AbstractPageableView#getRowCount(AbstractPageableView.java:216)
        at org.apache.wicket.markup.repeater.AbstractPageableView#getViewSize(AbstractPageableView.java:314)
        at org.apache.wicket.markup.repeater.AbstractPageableView#getItemModels(AbstractPageableView.java:99)
        at org.apache.wicket.markup.repeater.RefreshingView#onPopulate(RefreshingView.java:93)
        at org.apache.wicket.markup.repeater.AbstractRepeater#onBeforeRender(AbstractRepeater.java:124)
        at org.apache.wicket.markup.repeater.AbstractPageableView#onBeforeRender(AbstractPageableView.java:115)
        // <-- 8 times the following lines
        at org.apache.wicket.Component#internalBeforeRender(Component.java:950)
        at org.apache.wicket.Component#beforeRender(Component.java:1018)
        at org.apache.wicket.MarkupContainer#onBeforeRenderChildren(MarkupContainer.java:1825)
        at org.apache.wicket.Component#onBeforeRender(Component.java:3916)
        // -->
        at org.apache.wicket.Page#onBeforeRender(Page.java:801)
        at org.apache.wicket.Component#internalBeforeRender(Component.java:950)
        at org.apache.wicket.Component#beforeRender(Component.java:1018)
        at org.apache.wicket.Component#internalPrepareForRender(Component.java:2236)
        at org.apache.wicket.Page#internalPrepareForRender(Page.java:242)
        at org.apache.wicket.Component#render(Component.java:2325)
        at org.apache.wicket.Page#renderPage(Page.java:1018)
        at org.apache.wicket.request.handler.render.WebPageRenderer#renderPage(WebPageRenderer.java:124)
        at org.apache.wicket.request.handler.render.WebPageRenderer#respond(WebPageRenderer.java:195)
        at org.apache.wicket.core.request.handler.RenderPageRequestHandler#respond(RenderPageRequestHandler.java:175)
        at org.apache.wicket.request.cycle.RequestCycle$HandlerExecutor#respond(RequestCycle.java:895)
        at org.apache.wicket.request.RequestHandlerStack#execute(RequestHandlerStack.java:64)
        at org.apache.wicket.request.cycle.RequestCycle#execute(RequestCycle.java:265)
        at org.apache.wicket.request.cycle.RequestCycle#processRequest(RequestCycle.java:222)
        at org.apache.wicket.request.cycle.RequestCycle#processRequestAndDetach(RequestCycle.java:293)
        at org.apache.wicket.protocol.http.WicketFilter#processRequestCycle(WicketFilter.java:261)
        at org.apache.wicket.protocol.http.WicketFilter#processRequest(WicketFilter.java:203)
        at org.apache.wicket.protocol.http.WicketFilter#doFilter(WicketFilter.java:284)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain#doFilter(ServletHandler.java:1668)
        at org.apache.isis.core.webapp.diagnostics.IsisLogOnExceptionFilter#doFilter(IsisLogOnExceptionFilter.java:52)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain#doFilter(ServletHandler.java:1668)
        at org.apache.shiro.web.servlet.AbstractShiroFilter#executeChain(AbstractShiroFilter.java:449)
        at org.apache.shiro.web.servlet.AbstractShiroFilter$1#call(AbstractShiroFilter.java:365)
        at org.apache.shiro.subject.support.SubjectCallable#doCall(SubjectCallable.java:90)
        at org.apache.shiro.subject.support.SubjectCallable#call(SubjectCallable.java:83)
        at org.apache.shiro.subject.support.DelegatingSubject#execute(DelegatingSubject.java:383)
        at org.apache.shiro.web.servlet.AbstractShiroFilter#doFilterInternal(AbstractShiroFilter.java:362)
        at org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter(OncePerRequestFilter.java:125)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain#doFilter(ServletHandler.java:1668)
        // ... some Jetty stuff
        at java.lang.Thread#run(Thread.java:748)

1 Ответ

1 голос
/ 09 апреля 2019

После некоторого исследования, я думаю, что проблема вопрос 1 и 2 , похоже, связана с этим сообщением об ошибке ISIS # 1902 .

Вкратце: Механизм разрешения подключаемых модулей расширения datanucleus, похоже, не находит адаптеры типов значений ISIS и, следовательно, не может знать, как сериализировать типы BLOB / Clob ISIS.

Согласно вышеупомянутому сообщению об ошибке ISIS, эта проблема исправлена ​​в 1.17.0, поэтому я пытаюсь обновить версию 1.16.2 до этой версии (которая привела ко многим другим проблемам, но это будет дополнительной темой).

По вопросу 3 Я обнаружил Minio , который в основном решает мою проблему, но он немного завышен для моих нужд. Я буду продолжать искать другие решения для хранения Blob / Clobs в локальной файловой системе и буду помнить о Minio ...

ОБНОВЛЕНИЕ :

  1. Я обновил свой проект до версии ISIS 1.17.0. Это решило мою проблему в вопросе 1 (теперь я получаю три столбца для объекта Blob / Clob).
  2. Проблема в вопросе 2 (NucleusException) не решается обновлением. Я выяснил, что он генерируется только при возврате списка объектов DomainObject с полями Blob / Clob, т. Е. При отображении в виде отдельной таблицы. Если я попадаю непосредственно в представление объекта, исключение не выдается, и я могу видеть / изменять / загружать содержимое Blob / Clob.
  3. Тем временем я написал свой собственный плагин datanucleus, который сохраняет Blob / Clobs в виде файлов в файловой системе.

ОБНОВЛЕНИЕ 2

  1. Я нашел решение для обхода org.datanucleus.exceptions.NucleusException: Unable to create SQLExpression for mapping of type "org.apache.isis.objectstore.jdo.datanucleus.valuetypes.IsisClobMapping" since not supported. Кажется, проблема с массовой загрузкой (но я не знаю подробностей). Деактивация массовой загрузки с помощью свойства isis.persistor.datanucleus.standaloneCollection.bulkLoad=false (которое изначально установлено на true по архетипам ISIS) решило проблему.
...