MyBatis 学习笔记
MyBatis 学习笔记:
官网: http://www.mybatis.org , http://code.google.com/p/mybatis/
=========================
用到的资料和工具:
=========================
1. 从官网下载用户手册: MyBatis 3 User Guide Simplified Chinese.pdf
2. 几个主要的参考教程:
mybatis3 入门学习笔记 (五 star 推荐, 我直接使用了该作者的 SessionFactoryUtil 类)
http://blessht.iteye.com/blog/1097005
有关 sql xml 的写法, 可以参考下面 2 个文章, 讲的很清晰
cms 项目第 9 天 (上)-mybatis 框架
http://blog.sina.com.cn/s/blog_6bcb0a8c0100q6ub.html
cms 项目第 9 天 (下)-mybatis 框架
http://blog.sina.com.cn/s/blog_6bcb0a8c0100q6ud.html
3. 下载 MyBatis 最新的运行包, mybatis-3.0.6-bundle.zip
4. 下载 MyBatis 最新的代码生成工具包, mybatis-generator-core-1.3.1-bundle.zip
5. 下载 MyBatis 的 config 和 map 以及 mybatis-generator 的 DTD 文件.
mybatis 仍使用过时的 DTD 作为 XML 的 schema, 我们写的 XML 每个标签必须按照 DTD 中的声明顺序写,不然会报 XML invalidated 错误. 不知道 MyBatis 为什么不用 XSD 来代替 DTD?
http://mybatis.org/dtd/mybatis-3-config.dtd
http://mybatis.org/dtd/mybatis-3-mapper.dtd
http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd
6. 准备 Eclipse XML Editor 插件 (Visual studio 仅仅支持 xsd, 不支持 dtd), 每次在 Eclipse 要安装这个插件, 总是花好长时间来 google 找个插件的名称, 这次将安装过程记录下来.
在 Eclipse 的菜单 Install new software 中, 选择下面的 site:
Indigo - http://download.eclipse.org/releases/indigo
然后选择 Web, XML, Java EE and OSGi Enterprise Development
最后选择 Eclipse XML Editors and Tools
=========================
使用 mybatis-generator 根据 table, 可生成 POJO/SQL map XML 和 DAO mapper 代码,
注意: 该工具并不负责生成 mybatis 主 config 文件.
=========================
要使用 mybatis-generator 工具, 首先需要手动写一个 generator 的配置文件, 可以参考 mybatis-generator-core-1.3.1-bundle.zip 包里的文档, 假定我们已经写好了名为 generatorConfig.xml 的配置文件 , 使用 mybatis-generator 方法是:
1. 首次生成 POJO/DAO Interface/xml mapping 的命令,
java -jar mybatis-generator-core-x.x.x.jar -configfile generatorConfig.xml -overwrite
2. 如有几个表重构结构后, 可以使用如下命令, 重新生成相关的 POJO/DAO Interface/xml mapping 文件,
java -jar mybatis-generator-core-x.x.x.jar -configfile generatorConfig.xml -overwrite -tables table1,table2,table3
以下是一个 mybatis-generator 配置文件的内容,
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "../res/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<classPathEntry
location="D://corp_files/trunk/workspace/MyProject/lib/postgresql-9.1-901.jdbc4.jar" />
<context id="MyProject" targetRuntime="MyBatis3" >
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<jdbcConnection driverClass="org.postgresql.Driver"
connectionURL="jdbc:postgresql://localhost:5432/postgres" userId="postgres"
password="abc123!">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<javaModelGenerator targetPackage="MyProject.model"
targetProject="c:/src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="false" />
</javaModelGenerator>
<sqlMapGenerator targetPackage="MyProject.xml"
targetProject="c:/src">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER"
targetPackage="MyProject.dao" targetProject="c:/src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<table tableName="user_info" schema="public" domainObjectName="UserBean">
</table>
<table tableName="dept_info" schema="public" domainObjectName="DeptBean">
</table>
</context>
</generatorConfiguration>
=========================
编写 mybatis config 文件:
=========================
基本作用就是配置 JDBC 连接的有关信息,比如 URL、用户名、密码等等, 下面的配置文件是从网上拷贝了一份.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 配置管理器 -->
<configuration>
<!-- 别名 -->
<typeAliases>
<typeAlias type="org.leadfar.mybatis.Person" alias="Person" />
</typeAliases>
<!-- 配置数据库连接信息 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/mybatis" />
<property name="username" value="root" />
<property name="password" value="leadfar" />
</dataSource>
</environment>
</environments>
<!-- 映射文件定位 -->
<mappers>
<mapper resource="org/leadfar/mybatis/PersonMapper.xml" />
</mappers>
</configuration>
=========================
使用 mybatis-generator 生成 POJO/SQL map XML 和 DAO mapper 代码
=========================
首先看一下生成的代码, POJO 很简单, 就不贴了,
生成的 DAO interface(有删减)
public interface cronMapper {
int deleteByPrimaryKey(String cronCode);
//....
}
生成的 sql mapping 配置文件 (有删减)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="MyProject.dao.public.cronMapper" >
<delete id="deleteByPrimaryKey" parameterType="java.lang.String" >
delete from public.caw_cron
where cron_code = #{cronCode,jdbcType=VARCHAR}
</delete>
</mapper>
从上面看出, 生成的 DAO 代码其实都是 interface (如 cronMapper), 而不是具体的 class, 那么现在就有一个疑问了, 我们该如何使用这个接口呢? 是否需要自己写代码来实现该接口呢?
答案是, 不需要! 我们直接使用 MyBatis 的 SqlSession 的 getMapper() 方法就可以动态的获得一个已经实现了 cronMapper 接口的对象. 见下面的代码,
如何使用 cronMapper 接口, 进行 DAO 操作,
SqlSession session = sqlSessionFactory.openSession();
try {
cronMapper mapper = session.getMapper(cronMapper.class);
mapper.deleteByPrimaryKey("your_code")
// do other work
} finally {
session.close();
}
至于 MyBatis 的 SqlSession 的 getMapper(), 为什么能为我们返回一个实现 cronMapper 接口的对象, 应该是使用了 JDK 的动态代理或者拦截技术吧.
在知道了如何使用 MyBatis 生成的 DAO 接口代码后, 还有一个问题是, 是否还需要自己的 DAO 代码层? 我觉得仍然需要, 原因有两个:
1. 如果直接在 Business Logic 层 (BLL) 使用 Mybatis 的 sqlSessionFactory.openSession(), BLL 代码显得不太简洁, 如果我们在自己的的 DAO 层封装了 openSession()代码, BLL 层代码就简洁多了.
2. 很多时候我们的查询会复杂一些, 比如在权限管理模块, 一个用户对象可以有多个角色对象. 我们期望的方案是, 在用户对象内部有一个角色对象集合. MyBatis 可通过 resultMap 支持这种 one-to-many 的 mapping, 其后果是 sql mapping 的 xml 要复杂很多. 我个人觉得, 这种情况, 不如不用 mybatis 这些高级特性, MyBatis 仅仅完成 UserEntityDAO 和 RoleEntityDAO 的简单 mapping, 然后自己实现一个 User DAO 对象, 将 UUserEntityDAO 和 RoleEntityDAO 在代码中做封装.
=========================
可以将 MyBatis 的 sqlSessionFactory 和 getSession() 封装在一个工具类中. 这样自定义的 DAO 类可能很方便地使用.
摘自 http://blessht.iteye.com/blog/1097005
=========================
public class SessionFactoryUtil {
private static final String RESOURCE = "com/mybatisdemo/config/Configuration.xml";
private static SqlSessionFactory sqlSessionFactory = null;
private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();
static {
Reader reader = null;
try {
reader = Resources.getResourceAsReader(RESOURCE);
} catch (IOException e) {
throw new RuntimeException("Get resource error:"+RESOURCE, e);
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
/**
* Function : 获得 SqlSessionFactory
*/
public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}
/**
* Function : 重新创建 SqlSessionFactory
*/
public static void rebuildSqlSessionFactory(){
Reader reader = null;
try {
reader = Resources.getResourceAsReader(RESOURCE);
} catch (IOException e) {
throw new RuntimeException("Get resource error:"+RESOURCE, e);
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
/**
*
* Function : 获取 sqlSession
*/
public static SqlSession getSession(){
SqlSession session = threadLocal.get();
if(session!=null){
if(sqlSessionFactory == null){
getSqlSessionFactory();
}
// 如果 sqlSessionFactory 不为空则获取 sqlSession,否则返回 null
session = (sqlSessionFactory!=null) ? sqlSessionFactory.openSession(): null;
}
return session;
}
/**
* Function : 关闭 sqlSession
*/
public static void closeSession(){
SqlSession session = threadLocal.get();
threadLocal.set(null);
if(session!=null){
session.close();
}
}
}
=========================
问题 1: 如何 mapping 数据表的 char(1) 和 java 的 boolean 类型
摘自 http://javaeenotes.blogspot.com/2011/10/mybatis-and-oracle-database.html (通过 bing 搜索引擎可以看到 cache)
=========================
针对这种需求, 在 SQL XML 中, 不能直接使用 resultType, 必须有个 resultMap, 在对应的 boolean 字段节点上, 指定 typeHandler.
<resultMap id="BaseResultMap" type="MyProject.model.public.cron" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Wed Nov 30 17:34:29 CST 2011.
-->
<id column="job_code" property="jobCode" jdbcType="VARCHAR" />
<result column="enabled" property="enabled" jdbcType="CHAR" typeHandler="com.xxx.sample.BooleanTypeHandler" />
<result column="description" property="description" jdbcType="VARCHAR" />
</resultMap>
实现一个 com.xxx.sample.BooleanTypeHandler 类,
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
/**
* BooleanTypeHandler 类, 处理完成 char(1) 与 Boolean 类型的转换
*/
public class BooleanTypeHandler implements TypeHandler<Boolean>
{
private static final String YES = "Y";
private static final String NO = "N";
private Boolean valueOf(String value) throws SQLException
{
if (YES.equalsIgnoreCase(value))
return new Boolean(true);
else
return new Boolean(false);
}
@Override
public Boolean getResult(ResultSet resultSet, String name) throws SQLException
{
return valueOf(resultSet.getString(name));
}
@Override
public Boolean getResult(CallableStatement statement, int position) throws SQLException
{
return valueOf(statement.getString(position));
}
@Override
public void setParameter(PreparedStatement statement, int position, Boolean value, JdbcType jdbcType) throws SQLException
{
statement.setString(position, value.booleanValue() ? YES : NO);
}
}
=========================
问题 2: Insert 语句中, 如何使用 Oracle 的 sequence 对象
摘自 http://javaeenotes.blogspot.com/2011/10/mybatis-and-oracle-database.html (通过 bing 搜索引擎可以看到 cache)
=========================
Unlike other databases like MySQL, the Oracle database does not provide the functionality to generate primary keys automatically when inserting rows.
public interface ClientMapper {
@Insert("INSERT INTO client (id, name) VALUES (#{id}, #{name})")
@SelectKey(
keyProperty = "id",
before = true,
resultType = Integer.class,
statement = {"SELECT client_seq.nextval AS id FROM dual"})
public Integer insertClient(Client client);
}
=========================
最后, 列出几个不错的参考文章
=========================
mybatis3 入门学习笔记 (五 star 推荐)
http://blessht.iteye.com/blog/1097005
有关 sql xml 的写法, 可以参考下面 2 个文章, 讲的很清晰
cms 项目第 9 天 (上)-mybatis 框架
http://blog.sina.com.cn/s/blog_6bcb0a8c0100q6ub.html
cms 项目第 9 天 (下)-mybatis 框架
http://blog.sina.com.cn/s/blog_6bcb0a8c0100q6ud.html
MyBatis+Spring 基于接口编程的原理分析
http://denger.iteye.com/blog/1060588
MyBatis 缓存机制深度解剖 / 自定义二级缓存
http://denger.iteye.com/blog/1126423
使用 Mybatis Generator 自动生成 Mybatis 相关代码
http://qiuguo0205.iteye.com/blog/819100
log4jdbc 日志框架介绍, 因为 mybatis 基本上是使用 preparedStatement, 所以打出的日志, sql 和参数值是分开的, 可读性不好, log4jdbc 可以解决这个问题.
http://badqiu.iteye.com/blog/743100
http://indure.chinaunix.com/space.php?uid=78707&do=blog&id=1631151
官网:http://code.google.com/p/log4jdbc/wiki/FAQ
几个个英文教程, 写得还算可以吧.
http://javaeenotes.blogspot.com/2011/10/mybatis-and-oracle-database.html (通过 bing 搜索引擎可以看到 cache)
http://mybatis.co.uk/index.php/2010/09/mybatis-simple-and-complete-example.html
http://blog.idleworx.com/2010/06/idleworxs-mybatis-tutorials.html