后台中的Convert转换

分类

后台中的Convert,我总结了如下分类:

  1. springmvc自定义属性编辑器PropertyEditor

    我们在使用SpringMVC时,常常需要把表单中的参数映射到我们对象的属性中,我们只要使用默认的配置即可做到普通数据类型的转换,如将String转换成Integer和Double等。但如果我要把String映射到对象上,如“rick-18”映射到Student对象,就必须自定义converter。值得注意的是,这里的提交方式不能是raw(application/json)的 。只能是x-www-form-urlencoded/form-data类型

  2. springmvc自定义Converter

    Converter能完成属性编辑器的功能,而且更加通用,不仅仅可以接收UI参数。是Spring推荐的实现方式。关于两者的比较,可以查看https://stackoverflow.com/questions/12544479/spring-mvc-type-conversion-propertyeditor-or-converter

  3. stringmvc自定义HttpMessageConverter

    HTTP消息转换,比如可以让spring接收Content-Type:xx/yy类型,body是字符串“rick-18”,后台用对象Student接收。自定义raw。spring提供了很多默认的转换,如FormHttpMessageConverter,MappingJackson2HttpMessageConverter

  4. hibernate自定义属性

    hibernate在持久化,如果是自定义属性,该如何处理映射呢?如,有个属性是Student对象,希望能够将Student的json对象存储varchar到列stu上

  5. hibernate validation自定义验证方法

    如何自定义validation,验证逻辑错误。

实现

所有实现都是基于Springboot2.0,VEHICLE_BRAND#NS 映射到 Word

  • ## PropertyEditor
  1. 定义属性编辑器
    WordEditor.java
public class WordEditor extends PropertyEditorSupport {
    private static final String PARAM_SEPARATOR = "#";

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        if (StringUtils.hasText(text)) {
            String[] values = text.split(PARAM_SEPARATOR);


            Word word = new Word();
            word.setCategory(values[0]);
            word.setName(values[1]);
            setValue(word);

        } else {
            setValue(null);
        }
    }

    @Override
    public String getAsText() {
        Word word = (Word) getValue();
        if (word != null) {
            return word.getCategory() + "#" + word.getName();
        } else {
            return "";
        }
    }
}
  1. 全局绑定属性编辑器

PropertyConfiguration.java

@RestControllerAdvice
public class PropertyConfiguration {

    @InitBinder
    public void registerCustomEditors(WebDataBinder binder) {
        binder.registerCustomEditor(Word.class, new WordEditor());
    }
}

当然,可用在单独的Controller中绑定

  • ## Converter
  1. 定义Converter

StringToWordConverter.java

@Component
public class StringToWordConverter implements Converter<String, Word> {
    private static final String PARAM_SEPARATOR = "#";

    @Override
    public Word convert(String s) {
        String[] values = s.split(PARAM_SEPARATOR);


        Word word = new Word();
        word.setCategory(values[0]);
        word.setName(values[1]);
        return word;
    }
}

注意:Converter是来自接口org.springframework.core.convert.converter.Converter

  1. 添加Converter
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToWordConverter());
    }
}
  • ## HttpMessageConverter
  1. 定义 HttpMessageConverter

WordHttpMessageConverter.java

@Component
public class WordHttpMessageConverter extends AbstractHttpMessageConverter<Word> {
    private static final String PARAM_SEPARATOR = "#";

    public WordConverter() {
        super(new MediaType("text", "word"));
    }

    @Override
    protected boolean supports(Class<?> aClass) {
        return Word.class.isAssignableFrom(aClass);
    }

    @Override
    protected Word readInternal(Class<? extends Word> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
        String s = toString(httpInputMessage.getBody());

        String[] values = s.split(PARAM_SEPARATOR);


        Word word = new Word();
        word.setCategory(values[0]);
        word.setName(values[1]);
        return word;
    }

    @Override
    protected void writeInternal(Word word, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
        OutputStream outputStream = httpOutputMessage.getBody();
        String body = word.getCategory() + "#" + word.getName();
        outputStream.write(body.getBytes());
        outputStream.close();
    }

    private static String toString(InputStream inputStream) {
        Scanner scanner = new Scanner(inputStream, "UTF-8");
        return scanner.useDelimiter("\\A").next();
    }

}

前端可用通过Content-Type: text/word,进行消息转换

  1. 添加MessageConverter
@Configuration
public class OAWebMvcConfigurer implements WebMvcConfigurer {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new WordConverter());
    }
}
  • ## hibernate Converter
  1. 新建Converter

WordJpaConverter.java

@Convert
public class WordJpaConverter implements AttributeConverter<Word, String> {

    private static final String PARAM_SEPARATOR = "#";

    @Override
    public String convertToDatabaseColumn(Word word) {
        if (Objects.isNull(word)) return "";

        StringBuilder sb = new StringBuilder();
        return sb.append(word.getCategory()).append(PARAM_SEPARATOR).append(word.getName()).toString();
    }

    @Override
    public Word convertToEntityAttribute(String s) {
        if (StringUtils.hasText(s)) {
            String[] values = s.split(PARAM_SEPARATOR);

            Word word = new Word();
            word.setCategory(values[0]);
            word.setName(values[1]);
            return word;

        }
        return null;
    }
} 
  1. 使用Converter

Vehicle.java

    @Entity
    public class Vehicle {
        @Id
        private Long id;

        @Convert(converter = WordJpaConverter.class)
        private Word vehicleBrand;
    }
  • ## hibernate validation
  1. 添加注解DictionaryConstraint.java
@Documented
@Constraint(validatedBy = DictionaryValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DictionaryConstraint {
    String message() default "Invalid dictionary data";
    String name();
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
  1. 设置验证逻辑DictionaryValidator.java
public class DictionaryValidator implements ConstraintValidator<DictionaryConstraint, Word> {

    private DictionaryConstraint constraintAnnotation;


    @Override
    public void initialize(DictionaryConstraint constraintAnnotation) {
        this.constraintAnnotation = constraintAnnotation;
    }

    @Override
    public boolean isValid(Word word, ConstraintValidatorContext constraintValidatorContext) {
        String category = constraintAnnotation.name();

        if (Objects.nonNull(category) && !Objects.equals(category, word.getCategory())) return false;

        DictionaryService dictionaryService = Global.applicationContext.getBean(DictionaryService.class);

        Word _word = dictionaryService.findByCategoryAndName(category, word.getName());

        return Objects.nonNull(_word);
    }
}
  1. 使用验证
    Vehicle.java
    @Entity
    public class Vehicle {
        @Id
        private Long id;

        @DictionaryConstraint(name = "VEHICLE_BRAND")
        @Convert(converter = WordJpaConverter.class)
        private Word vehicleBrand;
    }