简体   繁体   English

使用 Spring Data Rest 时公开所有 ID

[英]Expose all IDs when using Spring Data Rest

I'd like to expose all IDs using a Spring Rest interface.我想使用 Spring Rest 接口公开所有 ID。

I know that per default an ID like this will not be exposed via the rest interface:我知道默认情况下,这样的 ID 不会通过 rest 接口公开:

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(unique=true, nullable=false)
    private Long id;

I'm aware that I can use this to expose the ID for User :我知道我可以使用它来公开User的 ID:

@Configuration
public class RepositoryConfig extends RepositoryRestMvcConfiguration {
    @Override
    protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(User.class);
    }
}

But is there an easy way to expose all IDs without manually maintaining a list in this configureRepositoryRestConfiguration method?但是有没有一种简单的方法可以公开所有 ID,而无需在此configureRepositoryRestConfiguration方法中手动维护列表?

If you want to expose the id field for all your entity classes:如果要公开所有实体类的 id 字段:

import java.util.stream.Collectors;

import javax.persistence.EntityManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;

@Configuration
public class MyRepositoryRestConfigurerAdapter extends RepositoryRestConfigurerAdapter {

    @Autowired
    private EntityManager entityManager;

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(entityManager.getMetamodel().getEntities().stream().map(e -> e.getJavaType()).collect(Collectors.toList()).toArray(new Class[0]));
    }

}

I discovered that if you name the @Id field ' Id ' it will display in the JSON if you have a public getter for the Id .我发现,如果您将@Id字段命名为“ Id ”,如果您有该Id的公共 getter,它将显示在 JSON 中。 The Id will show up as a JSON key called ' id ' Id 将显示为名为“ id ”的 JSON 键

For example: @Id @Column(name="PERSON_ROLE_ID") private Long Id;例如: @Id @Column(name="PERSON_ROLE_ID") private Long Id;

This also works for @EmbeddedId fields called ' Id ' as well as long as it has a public getter.这也适用于名为“ Id ”的@EmbeddedId字段,只要它具有公共吸气剂。 In this case the fields of the Id will show up as a JSON object.在这种情况下,Id 的字段将显示为 JSON 对象。

For example: @EmbeddedId private PrimaryKey Id;例如: @EmbeddedId private PrimaryKey Id;

Surprisingly this is case sensitive, calling id ' id ' doesn't work even though it would be a more conventional name for a Java field.令人惊讶的是,这是区分大小写的,调用 id ' id ' 不起作用,即使它是 Java 字段的更传统名称。

I should say that I discovered this completely by accident so I don't know if this is an accepted convention or will work with previous or future versions of Spring Data and REST.我应该说我完全是偶然发现了这一点,所以我不知道这是否是公认的约定,或者是否适用于 Spring Data 和 REST 的先前或未来版本。 Therefore I have included the relevant parts of my maven pom just incase it's sensittive to versions...因此,我已经包含了我的 maven pom 的相关部分,以防它对版本敏感......

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.0.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>
    <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>ojdbc7</artifactId>
        <version>12.1.0.2</version>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
    </dependency>
</dependencies>

Currently, there is no way to do this provided by SDR.目前,SDR 没有提供这种方法。 This issue on the SDR Jira tracker gives some explanation as to why this isn't (and perhaps shouldn't) be possible. SDR Jira 跟踪器上的这个问题给出了一些解释,说明为什么这不可能(也许不应该)是可能的。

The argument is basically that since the IDs are already contained within the self links in the response, you don't need to expose them as properties of the object itself.这个论点基本上是因为 ID 已经包含在响应中的self链接中,您不需要将它们作为对象本身的属性公开。

That said, you may be able to use reflection to retrieve all classes that have a javax.persistence.Id annotation and then call RepositoryRestConfiguration#exposeIdsFor(Class<?>... domainTypes) .也就是说,您可以使用反射来检索所有具有javax.persistence.Id注释的类,然后调用RepositoryRestConfiguration#exposeIdsFor(Class<?>... domainTypes)

An updated answer to @mekasu . @mekasu的更新答案。 The RepositoryRestConfigurer interface was changed a bit in 2.4. RepositoryRestConfigurer接口在 2.4 中有所更改。

Pre 2.4: 2.4 之前:

import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
import javax.persistence.EntityManager;
import javax.persistence.metamodel.Type;

@Configuration
public class Config implements RepositoryRestConfigurer {

    @Autowired
    private EntityManager entityManager;

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(entityManager.getMetamodel().getEntities().stream().map(Type::getJavaType).toArray(Class[]::new));
    }
}

Post 2.4发布 2.4

import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import javax.persistence.EntityManager;
import javax.persistence.metamodel.Type;

@Configuration
public class Config implements RepositoryRestConfigurer {

    @Autowired
    private EntityManager entityManager;

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
        config.exposeIdsFor(entityManager.getMetamodel().getEntities().stream().map(Type::getJavaType).toArray(Class[]::new));
    }
}

Try this configuration.试试这个配置。 It works perfectly fine for me.它对我来说非常好。

@Configuration
public class RestConfiguration extends RepositoryRestConfigurerAdapter{

      @PersistenceContext
      private EntityManager entityManager;

      @Override
      public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
           //TODO: Expose for specific entity!
           //config.exposeIdsFor(Officer.class);
           //config.exposeIdsFor(Position.class);

           //TODO: Expose id for all entities!
           entityManager.getMetamodel().getEntities().forEach(entity->{
                try {
                     System.out.println("Model: " + entity.getName());
                     Class<? extends Object> clazz = Class.forName(String.format("yourpackage.%s", entity.getName()));
                     config.exposeIdsFor(clazz);
                } catch (Exception e) {
                     System.out.println(e.getMessage());
                }
            });
    }
}

You can use this method to find all @Entity classes of the EntityManagerFactory:您可以使用此方法查找 EntityManagerFactory 的所有@Entity类:

private List<Class<?>> getAllManagedEntityTypes(EntityManagerFactory entityManagerFactory) {
    List<Class<?>> entityClasses = new ArrayList<>();
    Metamodel metamodel = entityManagerFactory.getMetamodel();
    for (ManagedType<?> managedType : metamodel.getManagedTypes()) {
        Class<?> javaType = managedType.getJavaType();
        if (javaType.isAnnotationPresent(Entity.class)) {
            entityClasses.add(managedType.getJavaType());
        }
    }
    return entityClasses;
}

then, to expose the IDs for all your entity classes:然后,公开所有实体类的 ID:

@Configuration
public class RestConfig extends RepositoryRestMvcConfiguration {

    @Bean
    public RepositoryRestConfigurer repositoryRestConfigurer(EntityManagerFactory entityManagerFactory) {
        List<Class<?>> entityClasses = getAllManagedEntityTypes(entityManagerFactory);

        return new RepositoryRestConfigurerAdapter() {

            @Override
            public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
                for (Class<?> entityClass : entityClasses) {
                    config.exposeIdsFor(entityClass);
                }
            }
    }
}

以下代码看起来更漂亮:

.exposeIdsFor(entityManager.getMetamodel().getEntities().stream().map(entityType -> entityType.getJavaType()).toArray(Class[]::new))

Full working example based on @FrancoisGengler's answer :基于@FrancoisGengler 的回答的完整工作示例:

@SpringBootApplication
public class DataRestApplication {
    public static void main(String[] args) {
        SpringApplication.run(DataRestApplication.class, args);
    }

    @Bean
    public RepositoryRestConfigurer repositoryRestConfigurer(EntityManager entityManager) {
        return RepositoryRestConfigurer.withConfig(config -> {
            config.exposeIdsFor(entityManager.getMetamodel().getEntities()
                    .stream().map(Type::getJavaType).toArray(Class[]::new));
        });
    }
}

Proabably you can try this to include all id fields.可能您可以尝试将所有 id 字段包含在内。 I havent tried it yet, but will keep posted.我还没有尝试过,但会继续发布。

 public class ExposeAllRepositoryRestConfiguration extends RepositoryRestConfiguration {
    @Override
    public boolean isIdExposedFor(Class<?> domainType) {
        return true;
        }
    }

Excerpt from this link摘自此链接

You can add all your entity classes by exposeIdsFor .您可以通过exposeIdsFor添加所有实体类。 Replace "db.entity" to whick package you put your entities.将“db.entity”替换为放置实体的 whick 包。

@Configuration
public class CustomRepositoryRestConfigurer extends RepositoryRestConfigurerAdapter {
    Logger logger = Logger.getLogger(this.getClass());

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        Set<String> classNameSet = ClassTool.getClassName("db.entity", false);
        for (String className : classNameSet) {
            try {
                config.exposeIdsFor(Class.forName(className));
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

        logger.info("exposeIdsFor : " + classNameSet);
    }
}

The ClassTool is my custom function to get class from given package, you can write by yourself. ClassTool 是我从给定包中获取类的自定义函数,您可以自己编写。

Here is what worked perfectly for me ( source here ):这对我来说非常有效( 来源在这里):

@Configuration
public class RepositoryRestConfig extends RepositoryRestConfigurerAdapter {

  @Override
  public void configureRepositoryRestConfiguration(final RepositoryRestConfiguration config) {

    final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(
        false);
    provider.addIncludeFilter(new AnnotationTypeFilter(Entity.class));

    final Set<BeanDefinition> beans = provider.findCandidateComponents("com.your.domain");

    for (final BeanDefinition bean : beans) {
      try {
        config.exposeIdsFor(Class.forName(bean.getBeanClassName()));
      } catch (final ClassNotFoundException e) {
        // Can't throw ClassNotFoundException due to the method signature. Need to cast it
        throw new IllegalStateException("Failed to expose `id` field due to", e);
      }
    }
  }
}

It finds all beans with the @Entity annotation and exposes them.它找到所有带有 @Entity 注释的 bean 并公开它们。

Please find a simple solution for this, avoiding to find entities related.请为此找到一个简单的解决方案,避免查找相关实体。

@Component
public class EntityExposingIdConfiguration extends RepositoryRestConfigurerAdapter {

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        try {
            Field exposeIdsFor = RepositoryRestConfiguration.class.getDeclaredField("exposeIdsFor");
            exposeIdsFor.setAccessible(true);
            ReflectionUtils.setField(exposeIdsFor, config, new ListAlwaysContains());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    class ListAlwaysContains extends ArrayList {

        @Override
        public boolean contains(Object o) {
            return true;
        }
    }
}

You can try with this solution: - First import reflections library to your POM file:您可以尝试使用此解决方案: - 首先将 反射库导入您的 POM 文件:

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.11</version>
</dependency>

- Then change your RepositoryConfig class to: - 然后将您的 RepositoryConfig 类更改为:

@Configuration
public class RepositoryConfig extends RepositoryRestMvcConfiguration {
    @Override
    protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        Reflections reflections = new Reflections("com.example.entity");
        Set<Class<?>> idExposedClasses = reflections.getTypesAnnotatedWith(Entity.class, false);
        idExposedClasses.forEach(config::exposeIdsFor);
        return config;
    }
}

Change "com.example.entity" to your Entity package and you are good to go.“com.example.entity”更改为您的实体包,您就可以开始了。 Good luck!祝你好运!

I'm sharing my solution which is based on other answer .我正在分享基于其他答案的解决方案。

In my case which configures multiple databases, I don't why but, I need to autowire instances of EntityManagerFactory .在我配置多个数据库的情况下,我不知道为什么,但是我需要自动装配EntityManagerFactory的实例。

@Db1 @Autowire
EntityManagerFactory entityManagerFactoryDb1;

@Db2 @Autowire
EntityManagerFactory entityManagerFactoryDb2;

Now all I need is a method streaming all entity classes gathered from all injected persistence units.现在我所需要的只是一个流式传输从所有注入的持久性单元收集的所有实体类的方法。

(Maybe, checking the existence of @Entity annotation or a custom annotation , say @EntityRestExposeId , can be applied.) (也许,可以应用检查@Entity注释或自定义注释的存在,比如@EntityRestExposeId 。)

    private void forEachEntityClass(final Consumer<? super Class<?>> consumer) {
        Arrays.stream(DataRestConfiguration.class.getDeclaredFields())
                .filter(f -> {
                    final int modifiers = f.getModifiers();
                    return !Modifier.isStatic(modifiers);
                })
                .filter(f -> EntityManagerFactory.class.isAssignableFrom(f.getType()))
                .map(f -> {
                    f.setAccessible(true);
                    try {
                        return (EntityManagerFactory) f.get(this);
                    } catch (final ReflectiveOperationException roe) {
                        throw new RuntimeException(roe);
                    }
                })
                .flatMap(emf -> emf.getMetamodel().getEntities().stream().map(EntityType::getJavaType))
                .forEach(consumer);
    }

Calling the exposeIdFor method is straightforward.调用exposeIdFor方法很简单。

@Configuration
class DataRestConfiguration {

    @Bean
    public RepositoryRestConfigurer repositoryRestConfigurer() {
        return RepositoryRestConfigurer.withConfig((configuration, registry) -> {
            forEachEntityClass(configuration::exposeIdsFor);
            // ...
        });
    }

    private void forEachEntityClass(final Consumer<? super Class<?>> consumer) {
        // ...
    }

    @Db1 @Autowired
    EntityManagerFactory entityManagerFactoryDb1;

    @Db2 @Autowired
    EntityManagerFactory entityManagerFactoryDb2;

    @Db3 @Autowired
    EntityManagerFactory entityManagerFactoryDb3;
}

Here is a short solution to expose all ids using only things from springframework:这是一个简短的解决方案,仅使用 springframework 中的内容来公开所有 id:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;

@Configuration
public class MyRepositoryRestConfigurer implements RepositoryRestConfigurer {
    @Autowired
    private Repositories repositories;

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
        this.repositories.iterator().forEachRemaining(r -> {
            config.exposeIdsFor(r);
        });
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM