文档章节

说说类型转换

hyssop
 hyssop
发布于 2016/09/02 11:12
字数 1588
阅读 18
收藏 0

最近工作中总是发现类型不匹配的现象。比如说在mybatis里面,如果表里面的数据类型是timestamp ,如果你的dto对应字段是Date类型那么就会发生出来的数据不是你要的样子。这个时候你需要自己写一个自定义的转换器,并将其配置到mybatis的配置文件中。 第一步:配置


    <typeHandlers>
        <typeHandler  handler="com.framework.demo.utils.MyDateTypeHandler" javaType="String" jdbcType="TIMESTAMP"/>
    </typeHandlers>

java代码主要是实现接口TypeHandler<T>接口。比如讲temestamp类型的数据转换成自定义格式的string 第二步:编写代码

public class MyDateTypeHandler implements TypeHandler<String> {

    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public void setParameter(java.sql.PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, String.valueOf(parameter));
    }

    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
         String sqlTimestamp = rs.getString(columnName);
        if (sqlTimestamp != null) {
            try {
                return sqlTimestamp.substring(0,sqlTimestamp.lastIndexOf("."));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    public String getResult(ResultSet rs, int columnIndex) throws SQLException {
        Date sqlTimestamp = rs.getDate(columnIndex);

        if (sqlTimestamp != null) {
            try {
                return dateFormat.format(sqlTimestamp);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    public String getResult(java.sql.CallableStatement cs, int columnIndex) throws SQLException {
        String sqlTimestamp = cs.getString(columnIndex);
        if (sqlTimestamp != null) {
            try {
                return dateFormat.format(sqlTimestamp);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

这样就完成了,我发现如果不写这个自定义也能够转换,只是出来的字符串 多了一个(.0)。不知道什么原因。

除了mybatis经常会遇到类型转换之外,spring中的类型也是随处可见。 #案例 在我的项目中有这样一种配置

 @RequestMapping(value = "{id}/delete", method = RequestMethod.GET)
    public String showDeleteForm(@PathVariable("id") M m, Model model) 

这句话不只是将id映射到M中对应字段中,是将id对应的表行信息映射到M中去。 第一步,自定义类型转换器。

public class DomainClassConverter<T extends ConversionService & ConverterRegistry> implements
        ConditionalGenericConverter, ApplicationContextAware {

    private  ApplicationContext beanFactory;

    public DomainClassConverter(){
        this.beanFactory = null;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.beanFactory =applicationContext;
    }

    @Override
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        if(FieldAccessVo.class.isAssignableFrom(targetType.getObjectType())&&targetType.hasAnnotation(PathVariable.class)){
            return  true;
        }
        return false;
    }

    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(Object.class, Object.class));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (source == null || !StringUtils.hasText(source.toString())) {
            return null;
        }
        Class<?> domainType = targetType.getType();
        String daoImpl = domainType.getSimpleName().substring(0,1).toLowerCase()+domainType.getSimpleName().substring(1, domainType.getSimpleName().length())+"Dao";
        try{
            ConfigurableBaseSqlMapDao bean =(ConfigurableBaseSqlMapDao)beanFactory.getBean(daoImpl);
            return bean.findById(Long.valueOf(source.toString()));
        }catch (Exception e1){

            return null;
        }
  }

这里的原意是找到service的实现类,然后带着事务去查数据。可是我发现,即使我找到了service的实现类,这个代理类要想真正被调用,需要method方法,而我底层是泛型,method方法怎么也映射到不。于是,我找到dao对应的方法去调用去获取数据。 第二步,将类型转换器配置到xml中 spring为我们提供了工厂类,我们只要将我们要注册的converter实例放置到工厂类相应参数当中,最后会将转换器放置到上下文中

  <!-- 类型转换及数据格式化 -->
    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
        <set>
            <bean id="domainClassConverter" class="com.framework.demo.web.controller.converter.DomainClassConverter"/>
            <bean id="stringToDateConverter" class="com.framework.demo.utils.converter.StringToDateConverter"/>
        </set>
        </property>
    </bean>

这样转换器就根据matcher匹配之后调用转换方法去做转换动作。 #看看底层 FormattingConversionServiceFactoryBean这个工厂类很重要。他里面有转换器converter和格式转换器formater。最重要的里面有一个FormattingConversionService。这个类图解如下

输入图片说明 图1 放置位置

可以看出这个类其实是一个注册器的派生,既能够注册converter又能够注册formater。它的实例化如下:

this.conversionService = new DefaultFormattingConversionService(this.embeddedValueResolver, this.registerDefaultFormatters);

这样什么converter、formater都在conversionService里面了,那么spring怎么用的呢? 首先:来看看类AnnotationDrivenBeanDefinitionParser 其中的方法getConversionService 将它包装成beanDefinition

private RuntimeBeanReference getConversionService(Element element, Object source, ParserContext parserContext) {
		RuntimeBeanReference conversionServiceRef;
		if (element.hasAttribute("conversion-service")) {
			conversionServiceRef = new RuntimeBeanReference(element.getAttribute("conversion-service"));
		}
		else {
			RootBeanDefinition conversionDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);
			conversionDef.setSource(source);
			conversionDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			String conversionName = parserContext.getReaderContext().registerWithGeneratedName(conversionDef);
			parserContext.registerComponent(new BeanComponentDefinition(conversionDef, conversionName));
			conversionServiceRef = new RuntimeBeanReference(conversionName);
		}
		return conversionServiceRef;
	}

这样它就在上下文中了。在需要转换的类将conversionService注入到类中做类型转换。

看图一发现转换这个类的关系非常紧密,也是为什么spring代码可扩展和可维护性特别强。我们不妨从头到尾看看每一个接口,每一个实现类是组合组织的。

首先是converter的注册中心:接口ConverterRegistry。该接口对外提供注册和注销converter的功能。

然后是formatter的注册中心:接口FormatterRegistry。该接口继承自ConverterRegistry,所以说实现了该接口的实现了肯定也实现了ConverterRegistry。图中可以看出FormattingConversionService类实现了该接口。

第三是接口ConversionService:有了上面的两个接口,那么converter和formatter就能够注册进来了。第三个接口ConversionService就是定义了如何使用这些注册进来的converter和formatter。 第四个就是ConfigurableConversionService接口:该接口扩展了两个接口ConversionService, ConverterRegistry 。将注册converter、formatter的功能和使用converter、formatter的功能结合在了一起。

第四个是实现类GenericConversionService。该实现类实现了converter、formatter注册操作和如何使用converter、formatter的操作。

注意这里面的代码组织。

首先是内部类Converters。这个类对所有注册过来的converter进行统一管理。

第二个就是缓存组织:converterCache。每一次都那么从converters里面查效率低下。配置一个HashMap类型的converterCache可以有效的缓存源类型和目标类型与转换器之间的关系。其中ConverterCacheKey的组织还是很重要的。一定要记住定义equals 和compareTo方法。确保这个key唯一性。

最后,我们发现它还给工厂留了一个是怕。将factory和converter做了一个适配。这样能够将factory“伪装”成converter注册到converters中。

最后一个实现类DefaultFormattingConversionService。该类在GenericConversionService的基础上组合了三个jsr354Present、jsr310Present、jodaTimePresent。将相应类型的converter注册进来。

至此一个简单的类组织接口就介绍完毕。我们平时写业务代码的时候总是觉得可扩展性不足,可维护性捉襟见肘。往往问题出现在代码的组织结构上。我们很少用内部类,将功能细分。往往忘了按照功能 划分各个类的关系。接口是提供服务在载体。只有接口的服务定义的清晰,代码才能够做到可扩展。在本案例能够让我们感觉到代码结构十分清晰,就是因为接口见关系清晰的原因吧。

欢迎加入qq群: 122567875 欢迎关注微信公众号:hyssop的后花园

© 著作权归作者所有

上一篇: Alibaba 面经
下一篇: dubbo remoting(3)
hyssop
粉丝 19
博文 102
码字总数 111521
作品 0
昌平
程序员
私信 提问
小猪佩奇社会人专用服务器,有意思的python小程序,附python代码

最近社会人小猪佩奇挺热门的,大家都在说社会人,那么我们作为IT人,怎么让我们的服务器也蹭一下社会人的热点的?下面来给大家说说。 先来个效果图: 小猪佩奇社会人专用服务器 哈哈,这样是...

Python雁横
2018/05/23
0
0
说说JavaScript的类型转换

最近在重读《JavaScript高级程序设计》,读到数据类型这一节,想到了JavaScript里令程序员抓狂的一个问题——类型转换。因为JS是一门弱类型的语言,在运行时系统会根据需要进行类型转换,而类...

火星上的鱼
01/26
0
0
spring-core组件详解——类型转换系统

类型转换系统,顾名思义,就是负责spring框架中的对象类型转换工作。 类型转换系统体系图如下: 整个类型转换系统包括两个核心接口(ConversionService和ConverterRegistry)和一个核心实现类...

拉风小野驴
2016/04/29
878
0
说说 Spring 的属性编辑器

在 Spring 配置文件中,我们往往通过字面值设置 Bean 各种类型的属性值 ,这个功能是通过属性编辑器实现的。 任何实现了 java.beans.PropertyEditor 接口的类都是属性编辑器 。 它可以将外部...

deniro
2018/05/18
0
0
据说有99%的人都会做错的面试题

这道题主要考察了面试者对浮点数存储格式的理解。另外,请不要讨论该题本身是否有意义之类的话题。本题只为了测试面试者相关的知识是否掌握,题目本身并没有实际的意义。 下面有6个浮点类型变...

androidguy
2018/06/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

程序员随想-关于分享

最早的时候,文字是贵族这些上层人士才会学习的,底层人士没有资格和渠道去学习,同样用文字、图像等其他载体承载的知识大部分也只有贵族阶层才能享受的。后来有了造纸术、印刷术,成本降低,...

Lubby
24分钟前
0
0
聊聊hibernate的session-level repeatable reads

序 本文主要研究一下hibernate的session-level repeatable reads 实例 doInTransaction(session -> { Product product = new Product(); product.setId(1L); product.setQuanti......

go4it
35分钟前
0
0
ubuntu或ubuntu kylin优麒麟中安装QQ、wechat微信、百度网盘

从中国国内的地址下载deepin wine,码云上的。这样网速比较快。然后,按照说明向下安装。 https://gitee.com/wszqkzqk/deepin-wine-for-ubuntu...

gugudu
47分钟前
2
0
基于redis分布式锁实现“秒杀”

最近在项目中遇到了类似“秒杀”的业务场景,在本篇博客中,我将用一个非常简单的demo,阐述实现所谓“秒杀”的基本思路。 业务场景 所谓秒杀,从业务角度看,是短时间内多个用户“争抢”资源...

别打我会飞
今天
14
0
Zookeeper的实践指南

本章重点 1.数据存储2.基于Java API初探Zookeeper的使用3.深入分析Watcher机制的实现原理4.Curator客户端的使用,简单高效 数据存储 事务日志快照日志运行时日志 bin/zookeepe...

须臾之余
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部