Je voudrais exposer tous les ID à l'aide d'une interface Spring Rest.
Je sais que par défaut, un ID comme celui-ci ne sera pas exposé via l'interface de repos:
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(unique=true, nullable=false)
private Long id;
Je suis conscient que je peux l'utiliser pour exposer l'ID de User
:
@Configuration
public class RepositoryConfig extends RepositoryRestMvcConfiguration {
@Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(User.class);
}
}
Mais existe-t-il un moyen simple d'exposer tous les ID sans gérer manuellement une liste dans cette méthode configureRepositoryRestConfiguration
?
Actuellement, il n'y a aucun moyen de le faire fourni par SDR. Ce problème sur le traqueur SDR Jira explique pourquoi ce n'est pas (et ne devrait peut-être pas) être possible.
L'argument est essentiellement que, puisque les ID sont déjà contenus dans les liens self
dans la réponse, vous n'avez pas besoin de les exposer en tant que propriétés de l'objet lui-même.
Cela dit, vous pourrez peut-être utiliser la réflexion pour récupérer toutes les classes qui ont une annotation javax.persistence.Id
, Puis appeler RepositoryRestConfiguration#exposeIdsFor(Class<?>... domainTypes)
.
Si vous souhaitez exposer le champ id pour toutes vos classes d'entités:
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]));
}
}
J'ai découvert que si vous nommez le champ @Id
'Id
', il s'affichera dans le JSON si vous avez un getter public pour le Id
. L'ID apparaîtra comme une clé JSON appelée 'id
'
Par exemple: @Id @Column(name="PERSON_ROLE_ID") private Long Id;
Cela fonctionne également pour les champs @EmbeddedId
Appelés 'Id
' aussi longtemps qu'il a un getter public. Dans ce cas, les champs de l'ID apparaîtront comme un objet JSON.
Par exemple: @EmbeddedId private PrimaryKey Id;
Étonnamment, cela est sensible à la casse, l'appel de l'id 'id
' ne fonctionne pas même s'il s'agit d'un nom plus conventionnel pour un champ Java.
Je dois dire que j'ai découvert cela complètement par accident, donc je ne sais pas s'il s'agit d'une convention acceptée ou fonctionnera avec les versions précédentes ou futures de Spring Data et REST. Par conséquent, j'ai inclus les parties pertinentes de mon pom maven au cas où il serait sensible aux versions ...
<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>
Vous pouvez utiliser cette méthode pour trouver tous les @Entity
classes de EntityManagerFactory:
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;
}
puis, pour exposer les ID de toutes vos classes d'entités:
@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);
}
}
}
}
Essayez cette configuration. Cela fonctionne parfaitement bien pour moi.
@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());
}
});
}
}
Vous pouvez ajouter toutes vos classes d'entités par exposeIdsFor. Remplacez "db.entity" pour whick package que vous mettez vos entités.
@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);
}
}
Le ClassTool est ma fonction personnalisée pour obtenir la classe d'un package donné, vous pouvez écrire par vous-même.
Voici ce qui a parfaitement fonctionné pour moi ( source ici ):
@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);
}
}
}
}
Il trouve tous les beans avec l'annotation @Entity et les expose.
Vous pouvez probablement essayer ceci pour inclure tous les champs id. Je ne l'ai pas encore essayé, mais je resterai au courant.
public class ExposeAllRepositoryRestConfiguration extends RepositoryRestConfiguration {
@Override
public boolean isIdExposedFor(Class<?> domainType) {
return true;
}
}
Le code suivant semble plus joli:
.exposeIdsFor(entityManager.getMetamodel().getEntities().stream().map(entityType -> entityType.getJavaType()).toArray(Class[]::new))
Vous pouvez essayer avec cette solution: - Première importation réflexions bibliothèque dans votre fichier POM:
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
- Modifiez ensuite votre classe RepositoryConfig en:
@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;
}
}
Modifiez "com.example.entity" en votre Package d'entité et vous êtes prêt à partir. Bonne chance!
Veuillez trouver une solution simple pour cela, en évitant de trouver des entités liées.
@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;
}
}
}