I've got an endpoint:
/api/offers/search/findByType?type=X
where X
should be an Integer
value (an ordinal value of my OfferType
instance), whereas Spring considers X
a String
and will be applying its StringToEnumConverterFactory
with the StringToEnum
convertor.
public interface OfferRepository extends PagingAndSortingRepository<Offer, Long> {
List<Offer> findByType(@Param("type") OfferType type);
}
So I wrote a custom Converter<Integer, OfferType>
which simply get a instance by the given ordinal number:
public class IntegerToOfferTypeConverter implements Converter<Integer, OfferType> {
@Override
public OfferType convert(Integer source) {
return OfferType.class.getEnumConstants()[source];
}
}
Then I registered it properly with a Configuration
:
@EnableWebMvc
@Configuration
@RequiredArgsConstructor
public class GlobalMVCConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new IntegerToOfferTypeConverter());
}
}
And I was expected that all requests to findByType?type=X
will pass through my converter, but they do not.
Is any way to say that all enums defined as a request parameters have to be provided as an Integer
? Furthermore, is any way to say it globally, not just for a specific enum?
EDIT: I've found IntegerToEnumConverterFactory
in my classpath that does all I need. And it is registered with DefaultConversionService
which is a default service for conversion. How can that be applied?
EDIT2: It's such a trivial thing, I was wondering if there is a property to turn enum conversion on.
EDIT3: I tried to write a Converter<String, OfferType>
after I had got String
from TypeDescriptor.forObject(value)
, it didn't help.
EDIT4: My problem was that I had placed custom converter registration into a MVC configuration (WebMvcConfigurerAdapter
with addFormatters
) instead of a REST Repositories one (RepositoryRestConfigurerAdapter
with configureConversionService
).
Actually, the difference between findallBy and findby, is that : findAllBy returns a Collection but findBy returns Optional. so it's preferable to write List findAllBy instead of writing List findBy (but it will work also :p). and to write Optional findBy instead of Optional findAllBy.
Its findById method retrieves an entity by its id. The return value is Optional<T> . Optional<T> is a container object which may or may not contain a non-null value. If a value is present, isPresent returns true and get returns the value.
On saveAndFlush , changes will be flushed to DB immediately in this command. With save , this is not necessarily true, and might stay just in memory, until flush or commit commands are issued.
JPA & Hibernate Standard Enum Mappings By default, Hibernate maps an enum to a number. It uses the ordinal value, which is the zero-based position of a value within the definition of the enum. So, the enum value that's defined first gets mapped to 0, the second one to 1 and so on.
Spring parses the query parameters as Strings. I believe it always uses Converter<String, ?>
converters to convert from the query parameters to your repository methods parameters. It uses an enhanced converter service, since it registers its own converters such as Converter<Entity, Resource>
.
Therefore you have to create a Converter<String, OfferType>
, e.g.:
@Component
public class StringToOfferTypeConverter implements Converter<String, OfferType> {
@Override
public OfferType convert(String source) {
return OfferType.class.getEnumConstants()[Integer.valueOf(source)];
}
}
And then configure this converter to be used by the Spring Data REST, in a class extending RepositoryRestConfigurerAdapter
:
@Configuration
public class ConverterConfiguration extends RepositoryRestConfigurerAdapter {
@Autowired
StringToOfferTypeConverter converter;
@Override
public void configureConversionService(ConfigurableConversionService conversionService) {
conversionService.addConverter(converter);
super.configureConversionService(conversionService);
}
}
I tried to add this to the basic tutorial, added a simple enum to the Person class:
public enum OfferType {
ONE, TWO;
}
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private OfferType type;
public OfferType getType() {
return type;
}
public void setType(OfferType type) {
this.type = type;
}
}
And when I call:
http://localhost:8080/people/search/findByType?type=1
I get the result without errors:
{
"_embedded" : {
"people" : [ ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/search/findByType?type=1"
}
}
}
To implement a global Enum converter, you have to create a factory and register it in the configuration using the method: conversionService.addConverterFactory()
. The code below is a modified example from the documentation:
public class StringToEnumFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnum(targetType);
}
private final class StringToEnum<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnum(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
Integer index = Integer.valueOf(source);
return enumType.getEnumConstants()[index];
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With