MyBatis是 iBatis的新项目名,是一个java的持久化框架,和目前一家独大的Hibernate相比MyBatis显得比较的被冷漠。两个都是好框架,大家习惯用哪个而已。因为之前一直是用iBatis,现在看到新版的MyBatis想来学习一下。
MyBatis的官网有很详细的user guides,而且还有中文,看一两天就可以上手:
新版的一个重点特性是新增动态SQL(dynamic SQL),简单来说就是能够使用while、if、elseif、loop,就是在Mapper.xml中加入条件控制,基于这个不算新的功能,我妄想写一个生成工具对每个表生成一份可以完全实现80%数据库操作的mapper.xml,最终工具是产生了,但是达不到理想的效果,原因有几方面:
1、不能满足所有的需求
2、不能生成级联的mapper
3、生成的mapper很大,解析起来非常耗时,即效率不好
4、实际应用意义不大
基于上面的几点,我放弃了对其的改进,不过在这个过程中有不少有用的知识点需要记录下来。另外补充的是,官方有一个自动生成的插件: 一样的不好用,还是自己写sql比较好。
一、MyBatis和Spring结合
MyBatis和Spring结合非常简单,配置也不复杂,除了Spring和Mybatis的jar包外还需要插件包mybatis-spring-1.0.1.jar ,上面的jar都可以在 下载到。
现在有一个项目如图:
![](http://static.oschina.net/uploads/space/2013/0508/111126_Xx7c_1043369.png)
关注java/org.mybatis.jpetstore.persistence和resource/org.mybatis.jpetstore.persistence两个包,java下的是dao的接口,resource下的是数据表的Mapper。
里面有一个表如下:
create table category ( catid varchar(10) not null, name varchar(80) null, descn varchar(255) null, constraint pk_category primary key (catid));
现在你需要对这个表根据id进行select操作,首先需要定义一个接口CategoryMapper.java:
public interface CategoryMapper { Category getCategory(String categoryId);}
然后需要新建一个Mapper,CategoryMapper.xml:
最后就是在spring的配置文件中配置就可以了:
直接用Spring的scans扫描整个包,我感觉这种方式即简单又方便,感觉很清爽,而且扩展性也很好。 二、利用freemarker生成动态SQL
下面的内容需要对freemarker有一定的认识,至于什么是freemarker,看官方的文档:
这份文档非常详细,只有你想不到,没有没提到的。
velocity跟freemarker两个是类似的java模板工具,前者没有宏定义,后者有,在没有什么特别复杂需求的时候建议用前者(纯属我个人观点)
Mapper生成工具具体流程:
通过jdbc读取数据库——》返回表名(table.sqlName)、所有字段的list(table.columns)、每个字段的名字(column.sqlName)、每个字段的类型(column.javaType) ——》对信息进行处理生成一些变量类名(className)、首字母小写的变量名(column.columnNameFirstLower)、大写的变量名(column.constantName)——》编写flt模板文件——》生成Mapper.xml。
需要说明的几点:
(1)、在Mybatis的Mapper.xml中是用#{value}和${value}显示变量的值,对于#{value},MyBatis会把value里面的双引号、单引号去掉,${value}则会把变量中的值原封不动的替换。
(2)、在freemarker中也是用${value}这种形式来显示变量的,因此用freemarker生成Mapper.xml需要达到的效果是${value}解析后得到${XXX}或者#{XXX}
为了解决上面的第二点,有必要定义两个宏:
<#macro mapperEl value>${r"#{"}${value}}</#macro>用来生成#{value}
<#macro jspEl value>${r"${"}${value}}</#macro> 用来生成${value}
SQL语句分析:
sql太复杂了,下面只讲where的部分,在sql语句中有很大一部分是condition部分,where部分组成大概有下面几点:
(1)、当column是string时,column = #{value}
(2)、当column是date时, trunc(column, 'MI') = trunc(#{value}, 'MI')
(3)、条件之间有 AND OR 两种关系
(4)、like 的条件连接,需要再根据(1)、(2)两点来分类
(5)、除去上面的外加一个extraConditions,允许用户写一些好复杂的条件
大概就5点吧,直接上代码:
生成的结果: <#list table.columns as column> <#if column.javaType == "java.util.Date" > ,'MI') ]]> <#else> AND tb.${column.sqlName} = <@mapperEl column.columnNameFirstLower/> <#list table.columns as column> <#if column.javaType == "java.lang.String">AND tb.${column.sqlName} LIKE <@mapperLike column.columnNameFirstLower/> <#elseif column.javaType == "java.util.Date" >AND trunc(tb.${column.sqlName},'MI') >= trunc(<@mapperBegin column.columnNameFirstLower/>,'MI') AND trunc(tb.${column.sqlName},'MI') <= trunc(<@mapperEnd column.columnNameFirstLower/>,'MI') <#else><#list table.columns as column> OR tb.${column.sqlName} = <@mapperEl column.columnNameFirstLower/> <#list table.columns as column> <#if column.javaType == "java.lang.String">OR tb.${column.sqlName} LIKE <@mapperLike column.columnNameFirstLower/> <#elseif column.javaType == "java.util.Date" >OR trunc(tb.${column.sqlName},'MI') >= trunc(<@mapperBegin column.columnNameFirstLower/>,'MI') OR trunc(tb.${column.sqlName},'MI') <= trunc(<@mapperEnd column.columnNameFirstLower/>,'MI') <#else><@jspEl 'extraConditions'/> <@jspEl 'sortColumns'/> <@jspEl 'groupColumns'/>
AND category.catid = #{catid} AND category.name = #{name} AND category.descn = #{descn} AND category.catid LIKE #{catid} AND category.name LIKE #{name} AND category.descn LIKE #{descn} OR category.catid = #{catid} OR category.name = #{name} OR category.descn = #{descn} OR category.catid LIKE #{catid} OR category.name LIKE #{name} OR category.descn LIKE #{descn} ${extraConditions} ${orderBy} ${groupBy}
然后是对于javaBean的生成模板:${className}Bean.java
<#assign className = table.className> <#assign classNameLower = className?uncap_first> package ${basepackage}.model;import java.io.Serializable;import ${basepackage}.model.BaseEntitySupportpublic class ${className}Bean extends BaseEntitySupport implements java.io.Serializable{ private static final long serialVersionUID = 8751282105532159742L; <#list table.columns as column> private ${column.javaType} ${column.columnNameLower}; <#list table.columns as column> public ${className} set${column.columnName}(${column.javaType} ${column.columnName}) { this.${column.columnNameLower} = ${column.columnName}; return this; } public ${column.javaType} get${column.columnName}() { return this.${column.columnNameLower}; } }
生成结果CategoryBean .java:
import java.io.Serializable;import org.mybatis.jpetstore.persistence.model.BaseEntitySupportpublic class CategoryBean extends BaseEntitySupport implements java.io.Serializable{ private static final long serialVersionUID = 8751282105532159742L; private java.lang.String catid; private java.lang.String name; private java.lang.String descn; public Category setCatid(java.lang.String Catid) { this.catid = Catid; return this; } public java.lang.String getCatid() { return this.catid; } public Category setName(java.lang.String Name) { this.name = Name; return this; } public java.lang.String getName() { return this.name; } public Category setDescn(java.lang.String Descn) { this.descn = Descn; return this; } public java.lang.String getDescn() { return this.descn; }}
工具生成的代码大概就是这个样子,只要加上insert、update、select、delete基本就成型。
可是,就如上面说的,这个生成工具没有什么实际的意义,所以还是作为瞎折腾的产物吧。