0

EclipseLinkを使用して JPA エンティティに静的ウィービングを使用するとすぐに、アプリケーションで例外が発生します。

このアプリケーションは、spring-data-jpaspring-data-rest-webmvcを使用してエンティティを変更する CRUD 機能を提供する Web アプリケーションです。

エンティティ クラスがウィービングによって処理されていない場合は機能します。しかし、織りエンティティを使用するとすぐに、次のようになります。

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: 
 Conflicting getter definitions for property "rootParentDescriptor":   
  org.eclipse.persistence.descriptors.InheritancePolicy#isRootParentDescriptor(0 params) vs org.eclipse.persistence.descriptors.InheritancePolicy#getRootParentDescriptor(0 params) (through reference chain:    org.springframework.hateoas.Resources["content"]->java.util.UnmodifiableCollection[0]->org.eclipse.persistence.internal.descriptors.changetracking.AttributeChangeListener["descriptor"]->org.eclipse.persistence.descriptors.RelationalDescriptor["inheritancePolicy"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Conflicting getter definitions for property "rootParentDescriptor": org.eclipse.persistence.descriptors.InheritancePolicy#isRootParentDescriptor(0 params) vs org.eclipse.persistence.descriptors.InheritancePolicy#getRootParentDescriptor(0 params) (through reference chain: org.springframework.hateoas.Resources["content"]->java.util.UnmodifiableCollection[0]->org.eclipse.persistence.internal.descriptors.changetracking.AttributeChangeListener["descriptor"]->org.eclipse.persistence.descriptors.RelationalDescriptor["inheritancePolicy"])

..

Caused by: java.lang.IllegalArgumentException: Conflicting getter definitions for property "rootParentDescriptor": org.eclipse.persistence.descriptors.InheritancePolicy#isRootParentDescriptor(0 params) vs org.eclipse.persistence.descriptors.InheritancePolicy#getRootParentDescriptor(0 params)
at com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder.getGetter(POJOPropertyBuilder.java:190) ~[jackson-databind-2.2.2.jar:2.2.2]

これは、エンティティを含む JSON 応答がマーシャリングされるときに発生します。エンティティはシンプルで、プロパティは 2 つだけで、関連付けなどはありません。

使用されているバージョンは次のとおりです。

<dependency>
 <groupId>org.springframework.data</groupId>
 <artifactId>spring-data-rest-webmvc</artifactId>
 <version>1.1.0.M1</version>
</dependency>

<dependency>
 <groupId>org.eclipse.persistence</groupId>
 <artifactId>org.eclipse.persistence.jpa</artifactId>
 <version>2.5.0</version>
</dependency>

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>1.3.2.RELEASE</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.2.2</version>
</dependency>

この組み合わせをサポートする構成、またはそのような問題を説明する既知の問題はありますか?

4

2 に答える 2

1

JPAアノテーション付きプロパティを一時的なものと見なすことで、これを修正しました。@Transientしたがって、永続化プロバイダーがそれらを正しく使用している限り、問題はありません。そうでない場合は、それに対してチケットを提出する必要があります。

この修正は、Spring Data JPA の 1.3.5 および 1.4 RC1 で利用可能になります。

于 2013-07-25T19:44:40.403 に答える
1

いくつかの調査の後、回避策を見つけましたが、誰かがより良い解決策を知っているかもしれません。その理由は、spring-data-commons のクラス JpaMetamodelMappingContext が、ウィービング プロセスによって追加されたエンティティ クラス内の変数を見つけるためです。これらのフィールドはEclipseLinkの内部動作にのみ関係しますが、メタモデル(JpaPersistentEntity)にプロパティとして収集されます。

JpaMetamodelMappingContext を独自のサブクラスに置き換えて、これらのフィールドが収集されないようにしました (@Transient の場合など)。これはHACKで行われました。

春の構成では、factory-class を jpa:repositories に追加します。

<jpa:repositories base-package="de.viaboxx.vplan.database.dao"
                  entity-manager-factory-ref="entityManagerFactory"
                  factory-class="de.viaboxx.springframework.data.jpa.EclipseJpaRepositoryFactoryBean"/>

新しいクラス EclipseJpaRepositoryFactoryBean は、JpaMetamodelMappingContext の新しい実装を導入します。ソースは次のとおりです: (JpaRepositoryFactoryBean から setEntityManager() を変更してコピーしたクラス)

public class EclipseJpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends
    TransactionalRepositoryFactoryBeanSupport<T, S, ID> {

private EntityManager entityManager;

/**
 * The {@link EntityManager} to be used.
 *
 * @param entityManager the entityManager to set
 */
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
    this.entityManager = entityManager;
    setMappingContext(
            new EclipseJpaMetamodelMappingContext(entityManager.getMetamodel())); // <<-- this is the only change
}

/*
 * (non-Javadoc)
 *
 * @see org.springframework.data.repository.support.
 * TransactionalRepositoryFactoryBeanSupport#doCreateRepositoryFactory()
 */
@Override
protected RepositoryFactorySupport doCreateRepositoryFactory() {
    return createRepositoryFactory(entityManager);
}

/**
 * Returns a {@link RepositoryFactorySupport}.
 *
 * @param entityManager
 * @return
 */
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
    return new JpaRepositoryFactory(entityManager);
}

/*
 * (non-Javadoc)
 *
 * @see
 * org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
 */
@Override
public void afterPropertiesSet() {

    Assert.notNull(entityManager, "EntityManager must not be null!");
    super.afterPropertiesSet();
}
}

内部プロパティをフィルタリングするハックはクラス EclipseJpaMetamodelMappingContext に含まれています (クラス JpaPersistentEntityImpl にアクセスするには、パッケージは「org.springframework.data.jpa.mapping」でなければなりません!)

public class EclipseJpaMetamodelMappingContext extends JpaMetamodelMappingContext {
/**
 * Creates a new JPA {@link javax.persistence.metamodel.Metamodel} based {@link org.springframework.data.mapping.context.MappingContext}.
 *
 * @param model must not be {@literal null}.
 */
public EclipseJpaMetamodelMappingContext(Metamodel model) {
    super(model);
}

@Override
protected JpaPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
                                                         JpaPersistentEntityImpl<?> owner,
                                                         SimpleTypeHolder simpleTypeHolder) {
    // HACK: ignore fields added by eclipselink weaving
    // because they cause problems during JSON marshaling with spring-data-rest 1.1.0.M1
    if (field.getType().getName().startsWith("org.eclipse.") ||
            field.getType().equals(PropertyChangeListener.class)) {
        return new TransientPropertyToBeIgnored(field, descriptor, owner, simpleTypeHolder);
    } else {
        return super.createPersistentProperty(field, descriptor, owner, simpleTypeHolder);    // call super!
    }
}

static class TransientPropertyToBeIgnored extends AnnotationBasedPersistentProperty<JpaPersistentProperty>
        implements JpaPersistentProperty {

    @Override
    public boolean isTransient() {
        return true;  // this causes the property to be ignored!
    }

    public TransientPropertyToBeIgnored(Field field, PropertyDescriptor propertyDescriptor,
                                        PersistentEntity owner,
                                        SimpleTypeHolder simpleTypeHolder) {
        super(field, propertyDescriptor, owner, simpleTypeHolder);
    }

    @Override
    protected Association createAssociation() {
        return new Association<JpaPersistentProperty>(this, null);
    }
}
}

これは、編まれたエンティティまたは編まれていないエンティティに関係なく、私たちにとって機能します。

EclipseLink の代わりにHibernateを使用すると問題は発生しませんが、 Hibernate を使用するにはjackson-hibernate-moduleおよびその他の構成変更が必要になります。

于 2013-07-24T14:40:29.367 に答える