|
| 1 | +#通用Mapper3变化 |
| 2 | + |
| 3 | +##精简项目,拆分为二 |
| 4 | + |
| 5 | +本项目2.x版本包含了通用`Mapper<T>`和`EntityMapper`(以及`SqlMapper`)。 |
| 6 | + |
| 7 | +我本人更喜欢`Mapper<T>`这种形式,并且这种形式扩展容易,使用起来方便,和自己手写或者MBG生成的没有区别,所以我更重视`Mapper<T>`的发展。 |
| 8 | + |
| 9 | +因此在3.x版本中将`EntityMapper`(包含`SqlMapper`)独立为另一个项目,这个独立的项目只会完善修复bug,不会有新的功能。建议大家使用`Mapper<T>`。 |
| 10 | + |
| 11 | +`EntityMapper`项目地址:稍后补充。 |
| 12 | + |
| 13 | +##细化接口,拆分为一 |
| 14 | + |
| 15 | +`Mapper<T>`包含了很多通用的方法,但并不是所有人都需要这些方法,也行其中的某些方法不需要,不想用,这在Mapper2.x是没法解决的。 |
| 16 | + |
| 17 | +还有一种情况就是,如果我的业务中需要一个我自己通用的接口方法,如果我开发了一个自己的接口,我可能还要给所有已经存在的Mapper接口(如`CountryMapper`)去继承该接口。 |
| 18 | + |
| 19 | +Mapper3的主要目标就是解决上面两个问题。 |
| 20 | + |
| 21 | +首先是将`Mapper<T>`接口细化,拆分,每一个原接口的方法,都独立为一个接口。 |
| 22 | + |
| 23 | +例如`List<T> select(T record);`方法原来只是`Mapper<T>`中的一个方法,现成成了`SelectMapper<T>`接口中的唯一方法: |
| 24 | + |
| 25 | +```java |
| 26 | +public interface SelectMapper<T> { |
| 27 | + @SelectProvider(type = MapperProvider.class, method = "dynamicSQL") |
| 28 | + List<T> select(T record); |
| 29 | +} |
| 30 | +``` |
| 31 | + |
| 32 | +所有的方法都按照上面的方式进行了拆分。 |
| 33 | + |
| 34 | +拆分后我需要那些方法,我就继承那些方法。<b>你会不会觉得这样变的更麻烦了?</b> |
| 35 | + |
| 36 | +##接口可以自定义搭配继承 |
| 37 | + |
| 38 | +接上面的问题,<b>你会不会觉得这样变的更麻烦了?</b> |
| 39 | + |
| 40 | +我们看看`BaseSelectMapper<T>`接口: |
| 41 | + |
| 42 | +```java |
| 43 | +/** |
| 44 | + * 通用Mapper接口,基础查询 |
| 45 | + * |
| 46 | + * @param <T> 不能为空 |
| 47 | + * @author liuzh |
| 48 | + */ |
| 49 | +public interface BaseSelectMapper<T> extends |
| 50 | + SelectOneMapper<T>, |
| 51 | + SelectMapper<T>, |
| 52 | + SelectCountMapper<T>, |
| 53 | + SelectByPrimaryKeyMapper<T> { |
| 54 | +} |
| 55 | +``` |
| 56 | + |
| 57 | +有没有发现什么,`BaseSelectMapper<T>`中并不包含任何方法,但是继承了4个通用的select查询方法。 |
| 58 | + |
| 59 | +如果我想使用这4种通用的查询方法,我并不需要去一个个继承这4个方法,我只需要继承`BaseSelectMapper<T>`即可。 |
| 60 | + |
| 61 | +如果你已经豁然开朗,你可能知道该如何使用了,如果还迷糊,再看下面的例子。 |
| 62 | + |
| 63 | +<b>为了不影响Mapper2以前版本的使用,`Mapper<T>`接口和以前没什么区别,只是多了一些方法。</b> |
| 64 | + |
| 65 | +```java |
| 66 | +public interface Mapper<T> extends |
| 67 | + BaseMapper<T>, |
| 68 | + ExampleMapper<T>, |
| 69 | + RowBoundsMapper<T> { |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +看上面代码,如果你继承`Mapper<T>`,那么以前使用Mapper2的项目不需要做任何改变。 |
| 74 | + |
| 75 | +<b>总结:你完全可以自定义一个`MyMapper<T>`,然后继承你想要的接口方法,在你自己的项目中,继承你自己的`MyMapper<T>`即可。</b> |
| 76 | + |
| 77 | +熟悉Mapper2多接口的可能会发现一个问题,以Spring中配置Mapper的部分代码为例: |
| 78 | + |
| 79 | +```xml |
| 80 | +<bean class="com.github.abel533.mapperhelper.MapperInterceptor"> |
| 81 | + <property name="properties"> |
| 82 | + <!-- 属性一行一个,具体属性参考mybatis-config.xml中的属性 --> |
| 83 | + <value> |
| 84 | + mappers=com.github.abel533.mapper.Mapper |
| 85 | + </value> |
| 86 | + </property> |
| 87 | +</bean> |
| 88 | +``` |
| 89 | + |
| 90 | +在Mapper2中,如果你继承了多个通用接口,`mappers`需要把所有的通用接口都配置上,中间用逗号`,`隔开。 |
| 91 | + |
| 92 | +像Mapper3中,提供了这么多的接口,难道都要一个个配置上吗? |
| 93 | + |
| 94 | +##继承接口自动注册,只需要配置基础接口 |
| 95 | + |
| 96 | +这句话说着也不顺,举个例子。 |
| 97 | + |
| 98 | +###第一种 |
| 99 | + |
| 100 | +如果我自己的整个项目中只用到了`Mapper<T>`接口,那么只配置一个`mappers=com.github.abel533.mapper.Mapper`即可。 |
| 101 | + |
| 102 | +`Mapper<T>`继承的所有父接口都会自动注册,因为父接口会自动注册,所以`mappers`配置`Mapper<T>`之后,所有的父接口都是可以单独用的。 |
| 103 | + |
| 104 | +也就是说我项目中的接口,可以自由搭配`Mapper<T>`父接口中的所有单独的接口。 |
| 105 | + |
| 106 | +###第二种 |
| 107 | + |
| 108 | +如果我创建了自己的`com.xxx.MyMapper<T>`,并且项目中只用到了自己的`com.xxx.MyMapper<T>`,那么只需要配置`mappers=com.xxx.MyMapper<T>`即可。 |
| 109 | + |
| 110 | +从这一点应该很容易看出来,项目中的代码和通用Mapper完全解耦,如果你不喜欢代码中包含`com.github.abel533`奇怪的包名,你可以自己创建一个基础接口。 |
| 111 | + |
| 112 | +<b>个人建议创建一个自己的通用接口,方便将来的自由扩展和搭配</b> |
| 113 | + |
| 114 | +###第三种 |
| 115 | + |
| 116 | +如果你使用的接口互相没有继承关系,那么你需要把这些接口都配置在`mappers`属性上,和Mapper2一样。 |
| 117 | + |
| 118 | +##极其简单的扩展方式 |
| 119 | + |
| 120 | +除了Mapper2中支持的两种方式外([Mapper2扩展文档](http://git.oschina.net/free/Mapper/blob/master/wiki/mapper/3.ExtendMapper.md)] |
| 121 | + |
| 122 | +增加了一种简单的方式,看下面一个例子: |
| 123 | + |
| 124 | +```java |
| 125 | +public interface InsertListMapper<T> { |
| 126 | + /** |
| 127 | + * 批量插入,支持数据库自增字段,支持回写 |
| 128 | + * |
| 129 | + * @param recordList |
| 130 | + * @return |
| 131 | + */ |
| 132 | + @Options(useGeneratedKeys = true, keyProperty = "id") |
| 133 | + @InsertProvider(type = SpecialProvider.class, method = "dynamicSQL") |
| 134 | + int insertList(List<T> recordList); |
| 135 | +} |
| 136 | +``` |
| 137 | + |
| 138 | +这是一个批量插入的接口,这里限制自增属性为`id`。 |
| 139 | + |
| 140 | +我们看看实现类`SpecialProvider`中的`insertList`(方法名必须和接口方法名一致)方法: |
| 141 | + |
| 142 | +```java |
| 143 | +public String insertList(MappedStatement ms) { |
| 144 | + final Class<?> entityClass = getSelectReturnType(ms); |
| 145 | + //获取表的各项属性 |
| 146 | + EntityHelper.EntityTable table = EntityHelper.getEntityTable(entityClass); |
| 147 | + //开始拼sql |
| 148 | + StringBuilder sql = new StringBuilder(); |
| 149 | + sql.append("insert into "); |
| 150 | + sql.append(table.getName()); |
| 151 | + sql.append("("); |
| 152 | + boolean first = true; |
| 153 | + for (EntityHelper.EntityColumn column : table.getEntityClassColumns()) { |
| 154 | + if(!first) { |
| 155 | + sql.append(","); |
| 156 | + } |
| 157 | + sql.append(column.getColumn()); |
| 158 | + first = false; |
| 159 | + } |
| 160 | + sql.append(") values "); |
| 161 | + sql.append("<foreach collection=\"list\" item=\"record\" separator=\",\" >"); |
| 162 | + sql.append("("); |
| 163 | + first = true; |
| 164 | + for (EntityHelper.EntityColumn column : table.getEntityClassColumns()) { |
| 165 | + if(!first) { |
| 166 | + sql.append(","); |
| 167 | + } |
| 168 | + sql.append("#{record.").append(column.getProperty()).append("}"); |
| 169 | + first = false; |
| 170 | + } |
| 171 | + sql.append(")"); |
| 172 | + sql.append("</foreach>"); |
| 173 | + return sql.toString(); |
| 174 | +} |
| 175 | +``` |
| 176 | + |
| 177 | +从获取表的各项属性后,完全就是一个拼SQL的过程,这个过程需要注意的是,这里拼的是XML中的形式。 |
| 178 | + |
| 179 | +上面就是两次循环列,最后拼个sql,sql形式如下: |
| 180 | + |
| 181 | +```xml |
| 182 | +insert into 表(id,xxx,xxx,...) |
| 183 | +values |
| 184 | +<foreach collection="list" item="record" separtor=","> |
| 185 | +(#{record.id},#{record.xxx},...) |
| 186 | +</foreach> |
| 187 | +``` |
| 188 | + |
| 189 | +相信这种简单的拼字符串难不倒任何一个人,只要你能在xml写出来,就能在这儿拼出来。 |
0 commit comments