Почему в моем проекте JPA/Olingo неправильно работает $ expand

В настоящее время я работаю над сервисом обработки данных на основе JPA/Olingo. Используемая версия Olingo - 2.0.7. Используемая реализация JPA - версия 2.5.1 eclipselink. Есть две сущности, связанные через отношения OneToMany (Компания, Страница). Запрос компании из службы (например, /odata/v2/Companies) без расширения $ работает нормально. То же самое для запроса страниц. Запрос страниц и расширение CompanyDetails также отлично работает. Каким-то образом запрос компании и расширение связанных страниц (например, /odata/v2/Companies?$ Expand=Pages) возвращает массив нулевого размера для страниц, хотя при вызове отложенной ссылки (например, /odata/v2/Companies('P')/Pages) в сущности компании возвращает массив страниц, как и ожидалось.

Вот мой файл persistence.xml (за исключением других еще не проверенных объектов):

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="s.h.backend"
        transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>c.p.s.h.data.Company</class>

...

        <class>c.p.s.h.data.Page</class>

...

        <properties>
            <property name="eclipselink.ddl-generation"
value="create-tables" />
            <property name="eclipselink.logging.level" value="INFO" />
            <property name="eclipselink.jpql.parser"
value="org.eclipse.persistence.queries.ANTLRQueryBuilder" />
        </properties>
    </persistence-unit>
</persistence>

Класс моей компании выглядит так:

@Entity
@Table(name = "HUM_COMPANY")
public class Company {
    private static final Logger log =
LoggerFactory.getLogger(Company.class);

    @Id
    private String id;

    @Column
    private String datacenterUrl;

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "company", cascade =
CascadeType.ALL)
    @CascadeOnDelete
    private List<Page> pages;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(insertable = false, updatable = false)
    private Date modified;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(insertable = false, updatable = false)
    private Date created;

    @PrePersist
    public void prePersist() {
        Date now = new Date();
        created = now;
        modified = now;
    }

    @PreUpdate
    public void preUpdate() {
        modified = new Date();
    }

    public Date getModified() {
        return modified;
    }

    public void setModified(Date modified) {
        log.debug("Olingo trying to set date {}", modified);
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        log.debug("Olingo trying to set date {}", created);
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getDatacenterUrl() {
        return datacenterUrl;
    }

    public void setDatacenterUrl(String datacenterUrl) {
        this.datacenterUrl = datacenterUrl;
    }

    public List<Page> getPages() {
        return pages;
    }

    public void setPages(List<Page> pages) {
        this.pages = pages;
    }
}

Класс моей страницы выглядит так:

@Entity
@Table(name = "HUM_PAGE")
public class Page implements Serializable {
    private static final Logger log = LoggerFactory.getLogger(Page.class);

    /**
     *
     */
    private static final long serialVersionUID = 1L;


    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false, name = "page_name")
    private String name;

    @Column
    private String description;

    @OneToOne
    private Context context;

    @ManyToOne(cascade = CascadeType.REFRESH)
    @JoinColumn(name = "company_id", nullable = false)
    private Company company;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(nullable = false)
    private Date modified;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(nullable = false)
    private Date created;

    @PrePersist
    public void prePersist() {
        Date now = new Date();
        created = now;
        modified = now;
    }

    @PreUpdate
    public void preUpdate() {
        modified = new Date();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Long getId() {
        return id;
    }

    public Date getModified() {
        return modified;
    }

    public Date getCreated() {
        return created;
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    public void setId(Long id) {
        log.debug("Olingo trying to set Id {}", id);
    }

    public void setModified(Date modified) {
        log.debug("Olingo trying to set date {}", modified);
    }

    public void setCreated(Date created) {
        log.debug("Olingo trying to set date {}", created);
    }

}

Я расширяю ODataJPAServiceFactory и переопределяю метод initializeODataJPAContext:

    @Override
    public ODataJPAContext initializeODataJPAContext() throws
ODataJPARuntimeException {
        ODataJPAContext oDataJPAContext = getODataJPAContext();
        try {

oDataJPAContext.setEntityManagerFactory(JpaEntityManagerFactory.getEntityManagerFactory());

oDataJPAContext.setPersistenceUnitName(JpaEntityManagerFactory.PERSISTENCE_UNIT_NAME);
            oDataJPAContext.setJPAEdmMappingModel("HumEdmMapping.xml");
        } catch (NamingException | SQLException e) {
            throw new ODataRuntimeException(e);
        }
        return oDataJPAContext;
    }

EntityManagerFactory создается следующим образом:

    public static synchronized EntityManagerFactory
getEntityManagerFactory()
            throws NamingException, SQLException {
        if (entityManagerFactory == null) {
            InitialContext ctx = new InitialContext();
            DataSource ds = (DataSource) ctx.lookup(DATA_SOURCE_NAME);
            Map<String, Object> properties = new HashMap<String, Object>();
            properties.put(PersistenceUnitProperties.NON_JTA_DATASOURCE,
ds);
            entityManagerFactory = Persistence.createEntityManagerFactory(
                    PERSISTENCE_UNIT_NAME, properties);
        }
        return entityManagerFactory;
    }

Мой файл сопоставления выглядит так:

<?xml version="1.0" encoding="UTF-8"?>
<JPAEDMMappingModel

xmlns="http://www.apache.org/olingo/odata2/jpa/processor/api/model/mapping">
    <PersistenceUnit name="s.h.backend">
        <JPAEntityTypes>
            <JPAEntityType name="Company">
                <EDMEntityType>Company</EDMEntityType>
                <EDMEntitySet>Companies</EDMEntitySet>
                <JPAAttributes>
                    <JPAAttribute name="created">Created</JPAAttribute>
                    <JPAAttribute
name="datacenterUrl">DatacenterUrl</JPAAttribute>
                    <JPAAttribute name="id">Id</JPAAttribute>
                    <JPAAttribute name="modified">Modified</JPAAttribute>
                </JPAAttributes>
                <JPARelationships>
                    <JPARelationship
name="pages">Pages</JPARelationship>               
                </JPARelationships>
            </JPAEntityType>
            <JPAEntityType name="Page">
                <EDMEntityType>Page</EDMEntityType>
                <EDMEntitySet>Pages</EDMEntitySet>
                <JPAAttributes>
                    <JPAAttribute name="created">Created</JPAAttribute>
                    <JPAAttribute name="name">Name</JPAAttribute>
                    <JPAAttribute
name="description">Description</JPAAttribute>
                    <JPAAttribute name="id">Id</JPAAttribute>
                    <JPAAttribute name="modified">Modified</JPAAttribute>
                </JPAAttributes>
                <JPARelationships>
                    <JPARelationship
name="company">Company</JPARelationship>               
                </JPARelationships>
            </JPAEntityType>

...

        </JPAEntityTypes>
        <JPAEmbeddableTypes>
        </JPAEmbeddableTypes>
    </PersistenceUnit>
</JPAEDMMappingModel>

1 ответ

Решение

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

  • @Cache (type = CacheType.NONE): в соответствии с документацией EclispeLink ( https://eclipse.org/eclipselink/api/2.0/org/eclipse/persistence/config/CacheType.html) использование этой аннотации не рекомендуется,

  • зарегистрируйте класс PostPersistListener с классом Page, используя @EntityListeners, и сделайте "invalidateAll" в методе, аннотированном @PostPersist. Результат не был надежным.

  • @Cache (изоляция =CacheIsolationType.ISOLATED): эта аннотация делает то, что мне нужно.

Так что моя проблема на данный момент решена. Думаю, это хорошая идея, чтобы документировать это для тех, кто испытывает ту же проблему.. (И для меня, чтобы запомнить это в следующий раз;-))

Другие вопросы по тегам