From Hibernate To Mybatis (一)



前言



新公司新项目的持久层用到了Mybatis,在此之前的一两年我一直使用的是Hibernate,关于这两个框架的争议一直存在,有人说Hibernate过于臃肿效率低于Mybatis,也有人说Hibernate优化做的好效率一样很棒,其实我还是认为各有优势的,比如Hibernate的API可以让我们无视数据库移植,HQL也简化了SQL,但Mybatis的灵活性确实略高于Hibernate,只要不是用JDBC,这些个框架整体上应当都是差不多的,无非就是用的好用的坏的问题。刚进公司那阵鸿洋曾经告诉过我,会Hibernate的话学Mybatis也就是几个小时的事情,看来确实如此,不管是整体的架构或者使用方法上都是同出一辙,本篇blog是我学习Mybatis的第一篇,所以就写一个简单的CRUD,当然我的这一系列blog还有一个特点就是会不断和hibernate做比较,一边总结回顾Hibernate,一边学习记录Mybatis。



初识MyBatis



首先看一下Mybatis的工程结构,

技术分享

给我的第一感觉就是轻量级,jar包只需要2个,并无任何依赖,那么再来看看Hibernate的工程结构,

技术分享

相比之下,孰“轻”孰“重”一目了然。再具体观察一下包的结构,不难发现Mybatis下有一个mappers包,里面放的是xml文件,没学过应该也听说过Mybatis的SQL语句是独立写在配置文件中的,并且正是写在这些mapper.xml文件中的,这个后面再细说。不管哪个框架自然应当有一个整体的配置文件,就像Hibernate中的hibernate.cfg.xml一样,我们来看看Mybatis的mybatis-config.xml:

<?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 alias="User" type="com.wl.entity.User" />
	</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://127.0.0.1:3306/testdb2" />
				<property name="username" value="root" />
				<property name="password" value="11fit" />
			</dataSource>
		</environment>
	</environments>

	<mappers>
		<mapper resource="com/wl/mappers/user_mapper.xml" />
	</mappers>
</configuration>	

基本上和Hibernate的配置方法完全一致,数据源、mapper文件(hbm文件),唯独需要注意的一点是这里配置了一个<typeAliases>,这个配置也很好理解,就是为我们的实体Bean配置了一个别名,在后面的mapper.xml中如果需要引入User的话我们就不用带包名了,否则必须写完整的包名+类名。


下面重点内容就是实体类和mapper文件,在Hibernate中,我们的实体类必然会有一个映射文件(hbm.xml)或映射注解(@Entity @Table)相对应,它们声明了我们实体Bean的属性和数据库字段的对应方式、数据类型等等,而在Mybatis中,依旧如此,并且除了这些,Mybatis还将SQL语句也放进了这种配置文件中。下面我们就看看user_mapper.xml这个配置文件:

<?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="com.wl.entity.UserMapper">

	<!--通过<resultMap>映射实体类属性名和表的字段名对应关系 -->
	<resultMap type="User" id="userResultMap">
		<!-- 用id属性来映射主键字段 -->
		<id property="id" column="userId" />
		<result property="name" column="userName" />
		<result property="pwd" column="userPwd" />
		<result property="birthday" column="userBorn" />
	</resultMap>

	<select id="selectUserByID" parameterType="int" resultMap="userResultMap">
		select * from t_user where userId = #{id}
	</select>

	<insert id="insertUser" parameterType="User" useGeneratedKeys="true"
		keyProperty="userId">
		insert into t_user(userName,userPwd,userBorn)
		values(#{name},#{pwd},#{birthday})
	</insert>

	<delete id="deleteUser" parameterType="int">
		delete from t_user where
		userId = #{id}
	</delete>

	<update id="updateUser" parameterType="User">
		update t_user set userName
			= #{name} where userId = #{id}
	</update>
</mapper>

首先来整体看一下,最上面的<resultMap>的配置非常类似于Hibernate中hibernate-mapping的配置,随便找一个Hibernate的hbm文件来看看:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.wl.entity">
	<class name="IDCard" table="t_id_card">
		<id name="id" column="card_id">
			<generator class="native" />
		</id>
		<property name="number" />
		<many-to-one name="person" column="pid" unique="true"></many-to-one>
	</class>
</hibernate-mapping>


即声明了实体属性和数据库字段的对应关系。回过来继续看Mybatis的mapper文件,在下面可以看到<select>、<insert>、<delete>、<update>这些配置,不难理解,每一种数据库语句放进相对应的配置标签中即可。再观察一下这些配置中的参数配置,比如:

<select id="selectUserByID" parameterType="int" resultMap="userResultMap">

不难发现,每一个SQL语句都有一个id,它就是我们调用这条SQL的唯一凭证,再看看最外层的<mapper>中还有一个namespaces的配置,如果我们配置了这个命名空间,那么我们在调用SQL的时候就需要通过命名空间.SQL_ID的组合字符串作为键去调用了。

第二个配置参数是parameterType,顾名思义自然就是参数类型了,这个没什么好说的。

第三个配置参数是resultMap,这个就是重点配置了,回头看看这里引用的“userReultMap”是在哪里定义的?没错,正是在配置文件的最顶端定义的那个和Hibernate映射配置类似的resultMap,配置它的作用就是保证查询字段和实体属性相对应。在Hibernate中我们就是通过配置<property name="xxx"/ cloumn="t_xxx">来保证ORM映射一致,而在Mybatis中依旧如此。当然我们也可以不用配置这个resultMap,那么我们必须在SQL中指定查询字段的别名,以保证和实体Bean中的属性名一致。在resultMap中的type和sql配置中的parameterType中,我们都是直接用的“User”,这里没有加包名自然就归功于我们之前配置的那个<typeAliases>。

还有一点我认为都不用说了,就是SQL语句中的占位符,Hibernate是通过 ? 或者 :param 来占位的,而Mybatis是在SQL语句中通过 #{xxx}来占位的。 这里仅仅讲解了<select>的标准参数配置和含义,如果用到其它的相关配置在官方文档必然有详尽的记录。好了,最后就该写我们的测试类了,并看看我们是如何启动Mybatis又如何去调用SQL语句的:

package com.wl.test;

import static org.junit.Assert.fail;

import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.wl.entity.User;

public class Test {

	private static SqlSessionFactory sqlSessionFactory;
	private static Reader reader;

	static {
		try {
			reader = Resources.getResourceAsReader("mybatis-config.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static SqlSessionFactory getSession() {
		return sqlSessionFactory;
	}

	@org.junit.Test
	public void testQuery() {
		SqlSession session = sqlSessionFactory.openSession();
		try {
			// 查询
			User user = (User) session.selectOne(
					"com.wl.entity.UserMapper.selectUserByID", 3);
			System.out.println("id-->" + user.getId());
			System.out.println("name-->" + user.getName());
			System.out.println("pwd-->" + user.getPwd());
			System.out.println("birthday-->" + user.getBirthday());
			session.commit();
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			session.close();
		}

	}

}

是不是和Hibernate很相似呢,Hibernate中的SessionFactory对应这里的SqlSessionFactory,Hibernate中的Session对应这里的SqlSession,包括session的开启关闭,事务的处理都是一模一样,唯独需要注意的是,在Hibernate中我们会在测试的时候再写HQL或SQL语句,而在Mybatis中我们直接通过配置好的SQL的id去调用指定SQL即可,可以看到确实是通过“命名空间.SQL_ID“的方式去调用的,最后看一下运行结果:

技术分享

可以看到成功运行,没有任何问题,而增删改也都是类似的,下面只贴出核心代码:

	                // 新增
			// User user = new User();
			// user.setName("jack");
			// user.setPwd(111);
			// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			// user.setBirthday(sdf.parse("1992-08-29"));
			// session.insert("com.wl.entity.UserMapper.insertUser", user);

			// 删除
			// session.delete("com.wl.entity.UserMapper.deleteUser", 1);

			// 更新
			// User user = (User) session.selectOne(
			// "com.wl.entity.UserMapper.selectUserByID", 25);
			// user.setName("harry");
			// session.update("com.wl.entity.UserMapper.updateUser", user);



总结



这是学习Mybatis的第一课,仅仅是一些最基本的操作,开发技术上都是相似相通的,而且都是为我们的产品或项目服务的一种工具而已,所以不论Hibernate或者Mybatis,只要效率OK,都是可取的,每一种框架都具备其设计的艺术和美感,我认为。后续根据学习进度和项目进度情况会继续写Mybatis系列blog,也欢迎各位批评指正,共同交流!同时我自己也会不管追求技术,不断学习,永不止步!

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。