最近有幸读到《企业架构模式》这本书,需要写作于2003年,已经是十年前,但仔细读过,有些东西现在只知道是这样用,但并不知道为什么要这样做。在看过此书之后,很多东西都能够有一条线进行贯穿,在使用到一些框架时,也知道背后的原因了。
这里面有一篇讲到对象-关系元数据映射的,实际上就是指在一个数据库中一个数据表与一个java中的domain对象之间的映射,在文中提到几种操作,也提到了为什么要这样做。其中,重要的当然是为什么要这样做了,但本篇主要讲期间在mybatis中笔者之前做的一个简单的映射,最终的效果与文中的结果基本上是一致的(因此在进行code时,还是没看过此书,结果发现自己又发明了一个新轮子)。
由于使用到mybatis,所以对模型之间的关系这里并没有涉及,只简单对应于一个数据表一个模型的概念。
通常情况下,我们在数据表中一个数据表user,有2个字段分别为user_name和password.那么在java中,我们会有一个对应的domain文件,如下代码所示:
public class User { private String userName; private String password; }
这里只是一个简单的对应,同时字段user_name对应于userName,这里并不是完全相同的字符串.因此,在mybatis相对应的xml中,我们需要显示的对待mapping操作.如下xml所示:
<insert> insert into user(user_name,password) values(#{userName},#{password}); </insert> <select columnMap=tMap> <!-- tMap中需要定义mapping关系 > select user_name,password from user </select>
这里涉及到一个东西,就是我们需要手动地编写相应的mapping语句,而且涉及到多个地方.比如在insert脚本中,需要编写user_name和userName不同的语句;在select中,还需要手动进行columnMap工作.对于一般的开发人员,使用copy&paste时,这里就会出错.而且一旦涉及到模型属性的变更,比如增加一个属性,表中加一个字段,这里的修改量就较大了,而且一旦涉及到代码还不集中,那就更麻烦了.
本篇即是引入一种特殊的columnMapping对象,并通过自动生成+动态SQL构建,来完成这种操作.参考如下一个insert语句:
<insert> insert into ${table.schema()}.${table.name()}( <foreach collection="columnMappingList" separator="," item="cm"> ${cm.jdbcField} </foreach> ) values( <foreach collection="columnMappingList" separator="," item="cm"> #{e.${cm.javaProperty},jdbcType=${cm.jdbcType}} </foreach> ) </insert>
这里涉及到3个主要的对象,分别是table对象,columnMappingList对象,要操作的e对象.
table对象
一般情况下,一个数据表对应一个数据模型即domain.那么,我们就可以这样进行数据生成,在相应的domain上追加相应的信息,来表示其对应于哪个数据表.如下参考所示:
@Table(schema="schema",name="user") public class User { }
columnMappingList对象
基于之前的table对象,同理我们也可以在相应的domain上,定义一个通用的方法如fetchColumnMapping方法.此方法,可以获取当前对象domain与数据表之间的对应关系,如下参考所示:
mappingMap.put("userName", new ColumnMapping("userName", "user_name", String.class, JdbcType.VARCHAR)); mappingMap.put("password", new ColumnMapping("password", "password", String.class, JdbcType.VARCHAR)); public java.util.Map<String, ColumnMapping> fetchColumnMapping() { return mappingMap; }
要操作的e对象
在这里,我们并没有要求要操作的e对象必须是我们指定的domain的类型,也可以是子类.这里就并没有严格限制必须使用我们指定的这个类.比如我们定义为User类,开发人员也可以自己定义一个UserEx类,只需要继承我们定义的类即可.
insert的具体实现
在这里,相应的信息都已经足够,那么就可以来构建我们所需要的insert的要素了.如下参考所示:
Class<? extends BaseDomain> clazz = getClass(); Table table = 获取相应的参考类上的table数据信息 BaseMapper baseMapper = 动态获取相应的mapper类 List<ColumnMapping> cmList = 获取当前对象的columnMapping集合 ...... baseMapper.save(this, table, cmList);//传递相应的参数
上面的实现相信已经很简单了,提取相应的domain对象,相应的信息都可以获取到,然后直接传递给mapper就可以了.
代码生成CODE GENERATION
这样,就完成了我们所需要的功能.但是从表面上看,这些代码还是需要自己编写,并不过从xml层转移到了domain层.不过,这个可以通过一定的手段进行避免,比如代码生成.比如,我们通过一个普通的生成类,读取相应的数据表结构,并生成我们相应的domain对象即可.具体实现由于版权略过,这个应该不是很难.
后记
上面的描述只是简单地描述了如何通过代码生成解决在mybatis中不能进行自动mapping的问题,同时描述了一上基本的实现思路.这有点感觉像hibernate的意思,但是这里需要注意的是,本文没有一点讲hibernate,不要一讲column mapping就以为是hibernate,这是开发人员一种惯例思维,实际上不管如何实现,其背后的思维是相通的,而重要的也是这种思维.否则,如何离开了hibernate,我们是不是就不能使用mapping呢?这是一个问题.
mybatis应用的场景,一般是业务数据以数据库为主导,通过有专门的dba进行数据以及SQL上指导,这就要求对所有的SQL,要可读,同时要可易于修改.当然简单的境删查改除外,但随着每一次业务变化,都需要我们同时修改相应的sql文件,这个代价毕竟有点大了.通过简单的mapping操作,可以减少这种修改,同时也提交开发效率,减少容易发生的问题.
文中除提到column mapping外,还有一个重要的技术点,即如何实现mybatis mapper的继承化.如在这里,我们希望使用userMapper而不是baseMapper进行操作,这里就会涉及到如何在mapper接口中实现xml相应语句的同步继承.详细实现可参考:http://www.iflym.com/index.php/code/201304240001.html
转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/201310140001.html