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、实际应用意义不大
基于上面的几点,我放弃了对其的改进,不过在这个过程中有不少有用的知识点需要记录下来。另外补充的是,官方有一个自动生成的插件:
http://code.google.com/p/mybatis/downloads/list?can=3&q=Product%3DGenerator 一样的不好用,还是自己写sql比较好。
一、MyBatis和Spring结合
MyBatis和Spring结合非常简单,配置也不复杂,除了Spring和Mybatis的jar包外还需要插件包mybatis-spring-1.0.1.jar ,上面的jar都可以在
http://code.google.com/p/mybatis/下载到。
现在有一个项目如图:
关注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:
<mapper namespace="org.mybatis.jpetstore.persistence.CategoryMapper">
<cache />
<select id="getCategoryList" resultType="Category">
SELECT
CATID AS categoryId,
NAME,
DESCN AS description
FROM CATEGORY
</select>
</mapper>
最后就是在spring的配置文件中配置就可以了:
直接用Spring的scans扫描整个包,我感觉这种方式即简单又方便,感觉很清爽,而且扩展性也很好。
<!-- scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.mybatis.jpetstore.persistence" />
</bean>
二、利用freemarker生成动态SQL
下面的内容需要对freemarker有一定的认识,至于什么是freemarker,看官方的文档:
http://nchc.dl.sourceforge.net/project/freemarker/chinese-manual/FreeMarker_Manual_zh_CN.pdf 这份文档非常详细,只有你想不到,没有没提到的。
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点吧,直接上代码:
生成的结果:
<sql id="${table.sqlName}_Where_Clause" >
<where>
<choose>
<when test="useAnd">
<#list table.columns as column>
<if test="${column.columnNameFirstLower} != null" >
<#if column.javaType == "java.util.Date" >
<![CDATA[
AND trunc(tb.${column.sqlName},'MI') = trunc(<@mapperEl column.columnNameFirstLower/> ,'MI')
]]>
<#else>
AND tb.${column.sqlName} = <@mapperEl column.columnNameFirstLower/>
</#if>
</if>
</#list>
<#list table.columns as column>
<#if column.javaType == "java.lang.String">
<if test="${column.columnNameFirstLower}LinkLike != null" >
AND tb.${column.sqlName} LIKE <@mapperLike column.columnNameFirstLower/>
</if>
<#elseif column.javaType == "java.util.Date" >
<if test="${column.columnNameFirstLower}BeginTime != null" >
AND trunc(tb.${column.sqlName},'MI') >= trunc(<@mapperBegin column.columnNameFirstLower/>,'MI')
</if>
<if test="${column.columnNameFirstLower}EndTime != null" >
AND trunc(tb.${column.sqlName},'MI') <= trunc(<@mapperEnd column.columnNameFirstLower/>,'MI')
</if>
<#else>
</#list>
</when>
<otherwise>
<#list table.columns as column>
<if test="${column.columnNameFirstLower} != null" >
OR tb.${column.sqlName} = <@mapperEl column.columnNameFirstLower/>
</if>
</#list>
<#list table.columns as column>
<#if column.javaType == "java.lang.String">
<if test="${column.columnNameFirstLower}LinkLike != null" >
OR tb.${column.sqlName} LIKE <@mapperLike column.columnNameFirstLower/>
</if>
<#elseif column.javaType == "java.util.Date" >
<if test="${column.columnNameFirstLower}BeginTime != null" >
OR trunc(tb.${column.sqlName},'MI') >= trunc(<@mapperBegin column.columnNameFirstLower/>,'MI')
</if>
<if test="${column.columnNameFirstLower}EndTime != null" >
OR trunc(tb.${column.sqlName},'MI') <= trunc(<@mapperEnd column.columnNameFirstLower/>,'MI')
</if>
<#else>
</#list>
</otherwise>
</choose>
<if test="extraConditions != null">
<@jspEl 'extraConditions'/>
</if>
</where>
<if test="orderBy!= null">
<@jspEl 'sortColumns'/>
</if>
<if test="groupBy!= null">
<@jspEl 'groupColumns'/>
</if>
</sql>
<sql id="category_Where_Clause" >
<where>
<choose>
<when test="useAnd">
<if test="catid != null" >
AND category.catid = #{catid}
</if>
<if test="name != null" >
AND category.name = #{name}
</if>
<if test="descn != null" >
AND category.descn = #{descn}
</if>
<if test="catidLinkLike != null" >
AND category.catid LIKE #{catid}
</if>
<if test="nameLinkLike != null" >
AND category.name LIKE #{name}
</if>
<if test="descnLinkLike != null" >
AND category.descn LIKE #{descn}
</if>
</when>
<otherwise>
<if test="catid != null" >
OR category.catid = #{catid}
</if>
<if test="name != null" >
OR category.name = #{name}
</if>
<if test="descn != null" >
OR category.descn = #{descn}
</if>
<if test="catidLinkLike != null" >
OR category.catid LIKE #{catid}
</if>
<if test="nameLinkLike != null" >
OR category.name LIKE #{name}
</if>
<if test="descnLinkLike != null" >
OR category.descn LIKE #{descn}
</if>
</otherwise>
</choose>
<if test="extraConditions != null">
${extraConditions}
</if>
</where>
<if test="orderBy!= null">
${orderBy}
</if>
<if test="groupBy!= null">
${groupBy}
</if>
</sql>
然后是对于javaBean的生成模板:${className}Bean.java
<#assign className = table.className>
<#assign classNameLower = className?uncap_first>
package ${basepackage}.model;
import java.io.Serializable;
import ${basepackage}.model.BaseEntitySupport
public 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>
<#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};
}
</#list>
}
生成结果CategoryBean .java:
import java.io.Serializable;
import org.mybatis.jpetstore.persistence.model.BaseEntitySupport
public 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基本就成型。
可是,就如上面说的,这个生成工具没有什么实际的意义,所以还是作为瞎折腾的产物吧。
本文介绍了如何使用Freemarker编写一个MyBatis的Mapper.xml自动生成工具。虽然最终因无法满足所有需求而放弃,但分享了结合MyBatis和Spring的配置,以及利用Freemarker生成动态SQL的过程,包括模板文件编写和变量处理等细节。
853

被折叠的 条评论
为什么被折叠?



