当前位置: 首页 > 知识库问答 >
问题:

Eclipse Link编织的实体的Spring-data-jpa问题

丁振海
2023-03-14

当我们使用EclipseLink为JPA实体使用静态编织时,我们的应用程序中就会出现异常。

该应用程序是一个web应用程序,使用SpringDataJPA和SpringDataRESTWebMVC提供CRUD功能来更改实体。

当实体类没有通过编织处理时,它就工作了。但一旦我们使用编织实体,我们就会得到:

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响应应包含一个实体时,就会发生这种情况。实体非常简单,只有两个属性,没有关联等。

以下是使用的版本:

<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>

是否有支持这种组合的配置或描述这种问题的已知问题?

共有2个答案

商松
2023-03-14

我将JPA@Transient注释属性视为Transient now,从而解决了这个问题。因此,只要您的持久性提供程序正确地使用了这些,您就可以了。如果不行,我们就得开罚单。

Spring数据JPA的1.3.5和1.4 RC1中将提供该修复程序。

仉俊能
2023-03-14

经过一些研究,我找到了一个解决办法,但也许有人知道一个更好的解决办法。原因是,SpringDataCommons中的类JpaMetamodelMappingContext在编织过程中添加的实体类中找到了变量。它在元模型(JpaPersistentEntity)中收集它们作为属性,尽管这些字段只对EclipseLink的内部行为感兴趣。

我用自己的子类替换了JpaMetamodelMappingContext,这样就不会收集这些字段(比如@Transient)。这是用黑客手段完成的。

在spring配置中,我们向jpa:repositories添加了一个工厂类

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

新的类Eclipse JpaRepositoryFactoryBean引入了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();
}
}

过滤内部属性的Hack包含在类Eclipse JpaMetamodelMappingContext中(包必须是org.springframework.data.jpa.mapping才能访问类JpaPeristentEntityImpl!)

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);
    }
}
}

无论编织或未编织的实体如何,这都适用于我们。

使用Hibernate而不是EclipseLink时不会出现问题,但是使用Hibernate需要jackson Hibernate模块和其他配置更改。

 类似资料:
  • 我有两个实体类它只有一个字段叫做和第二个实体它有两个字段和。数据库结构 我正在创建一个简单的CRUD web-app,但是update方法不是更新实体,而是在DB中插入新值。我检查了一下,当用户点击指定菜单上的更新时,第一个实体的id和它的配料id也会被预先定义。我是spring boot和thymeleaf的新手,不知道当您有一个以上的实体时如何使用JPA。 菜单实体: 配料实体: 控制器(仅更

  • 问题内容: 我开始使用Spring Data JPA在Spring Boot应用程序上进行工作,以在用户和角色之间建立ManyToMany关系。 此关系在User类中定义如下: 我使用以下角色创建角色: 之后向用户分配角色失败: 引发以下异常(为便于阅读而设置格式): 但是,如果在创建关系之前保存了用户,则可以正常工作: 我不得不两次保存/更新用户似乎有些不合理。有什么方法可以在不先保存 用户的

  • 我已经尝试使用Spring Boot+Spring Data JPA更新实体很久了。我得到所有正确的观点回到我。我的编辑视图按ID向我返回正确的实体。一切进展顺利。直到我实际尝试保存/合并/持久化对象。每次我拿回一个有新ID的新实体。我只是不知道为什么。我看了网上的例子,还有你可能会参考的重复问题的链接。那么,在这些代码中,我在哪里犯了错误呢? 在这段代码之后。我的视图将我返回到正确的URL,但I

  • 我在从数据库中删除实体时遇到问题。不管我做什么,它都不会删除。 驾驶员等级 评级等级 我做的第一件事是用,当调用时,它会抛出: org.postgresql.util。PSQLException:错误:更新或删除表“drivers”违反了表“ratings”上的外键约束“fk3raf3d9ucm571r485t8e7ew83” 好吧,选择另一种方式。尝试使用删除每个评级对象,但从未发生过,它只是彻

  • 我有一个测试用例,需要将100,000个实体实例持久化到数据库中。我当前使用的代码就是这样做的,但直到将所有数据持久化到数据库中为止,最多需要40秒。数据是从一个大约15 MB大小的JSON文件中读取的。 现在,我已经为另一个项目在自定义存储库中实现了批处理插入方法。但是,在这种情况下,我有很多顶级实体要持久化,只有几个嵌套实体。 在我当前的例子中,我有5个实体,这些实体包含大约30个实体的列表。