MyBatis系列一 MyBatis 简介
一、传统的JDBC编程
Java程序都是通过JDBC (Java Data Base Connectivity)连接数据库的,这样我们就可 以通过SQL对数据库编程。JDBC是由SUN公司(SUN公司后被Oracle公司收购)提出的一系列规范,但是它只定义了接口规范,而具体的实现是交由各个数据库厂商去实现的,因为每个数据库都有其特殊性,这些是Java规范没有办法确定的,所以JDBC就是一种典型的桥接模式。
传统的JDBC编程的使用给我们带来了连接数据库的功能,但是也引发了巨大的问题。
public class JdbcExample ( private Connection getConnection() ( Connection connection = null; try ( Class.forName ("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/mybatis?zeroDateTimeBehavior=convertToNull"; String user = “root”; String password = "learn"; connection = DriverManager.getConnection(url,user,password); } catch (ClassNotFoundException | SQLException ex) ( Logger.getLogger(JdbcExample.class.getName()).log(Level•SEVERE, null, ex); return null; } return connection; ) public Role getRole(Long id) ( Connection connection = getConnection (); PreparedStatement ps = null; ResultSet rs = null; try { ps = connection.prepareStatement ("select id, role_name, note from t_role where id = ?"); ps.setLong(1, id); rs = ps.executeQuery(); while(rs.next()) ( Long roleld = rs.getLong("id"); String userName = rs.getString ("role_name"); String note = rs.getString ("note"); Role role = new Role(); role.setld(id); role.setRoleName(userName); role.setNote(note); return role; ) } catch (SQLException ex) ( Logger.getLogger(JdbcExample.class.getName()).log(Level.SEVERE, null, ex); } finally ( this.close(rs, ps, connection); } return null; } private void close(ResultSet rs, Statement stmt, Connection connection) ( try ( if (rs != null && !rs.isClosed()) ( rs.close (); }
catch (SQLException ex) ( Logger.getLogger(JdbcExample.class.getName()).log(Level.SEVERE, null, ex); } try ( if (stmt != null && !stmt.isClosed()) {
stmt.close(); } } catch (SQLException ex) ( Logger.getLogger(JdbcExample.class.getName()).log(Level.SEVERE, null, ex); } try ( if (connection != null && !connection.isClosed()) (
connection.close (); } } catch (SQLException ex) ( Logger.getLogger(JdbcExample.class.getName()).log(Level.SEVERE, null, ex); } } public static void main(String[] args) ( JdbcExample example = new JdbcExample(); Role role = example.getRole(IL); System.err.printIn(nrole_name => n + role.getRoleName()); } }
从代码中我们可以看出整个过程大致分为以下几步:
•使用JDBC编程需要连接数据库,注册驱动和数据库信息。
•操作 Connection,打开 Statement 对象。
•通过Statement执行SQL,返回结果到ResultSet对象。
•使用ResultSet读取数据,然后通过代码转化为具体的POJO对象。
•关闭数据库相关资源。
使用传统的JDBC方式存在一些弊端。
其一,工作量相对较大。我们需要先连接,然 后处理JDBC底层事务,处理数据类型。我们还需要操作Connection对象、Statement对象 和ResultSet对象去拿到数据,并准确关闭它们。
其二,我们要对JDBC编程可能产生的异 常进行捕捉处理并正确关闭资源。对于一个简单的SQL在JDBC中尚且如此复杂,何况是 更为复杂的应用呢?很快这种模式就被一些新的方法取代,于是ORM模型就出现了。不 过所有的ORM模型都是基于JDBC进行封装的,不同的ORM模型对JDBC封装的强度是 不一样的。
二、ORM模型
由于JDBC存在的缺陷,在实际工作中我们很少使用JDBC进行编程,于是提出了对 象关系映射(Object Relational Mapping,简称 ORM,或者 O/RM,或者 O/R mapping)。 那 什么是ORM模型呢?
简单地说,ORM模型就是数据库的表和简单Java对象(Plain Ordinary Java Object, 简称POJO)的映射关系模型,它主要解决数据库数据和POJO对象的相互映射。我们通过这层映射关系就可以简单迅速地把数据库表的数据转化为POJO,以便程序员更加容易理解 和应用Java程序,如图1.1所示。
有了 ORM模型,在大部分情况下,程序员只需要了解Java应用而无需对数据库相关 知识深入了解,便可以写出通俗易懂的程序。此外,ORM模型提供了统一的规则使得数据库的数据通过配置便可轻易映射到POJO上。
三、Hibernate
最初SUN公司推出了 Java EE服务器端组件模型(EJB),但是由于EJB配置复杂,且 适用范围较小,于是很快就被淘汰了。与EJB的失败伴随而来的是另外一个框架的应运而生。它就是从诞生至今都十分流行的Hibernate。
Hibernate 一问世就成了 Java世界首选的ORM模型,它是建立在POJO和数据库表模 型的直接映射关系上的。
Hibernate是建立在若干POJO通过XML映射文件(或注解)提供的规则映射到数据 库表上的。换句话说,我们可以通过POJO直接操作数据库的数据。它提供的是一种全表 映射的模型。如图1・2所示是Hibernate模型的开发过程。相对而言,Hibernate对JDBC的 封装程度还是比较高的,我们己经不需要编写SQL语言(Structured Query Language),只 要使用 HQL 语言(Hibernate Query Langurage)就可以了。
首先我们需要提供hbm.xml文件,制定映射规则。
<?xml version="l.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN” "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- Generated 2015-12-12 22:50:58 by Hibernate Tools 4.3.1 -->
<hibernate-mapping> <class name="com.learn.mybatis.chapterl.po.TRole" table="t_role" catalog="mybatis" optimistic-lock="version"> <id name="id” type="long"> <column name="id" /> <generator class="assigned" /> </id> <property name="roleName" type="string"> <column name="role_name" length="60" /> </property> <property name="note" type="string"> <column name="note" length="512" /> </property> </class> </hibernate-mapping>
这是一个简单的XML文件,它描述的是POJO和数据库表的映射关系。Hibernate通 过配置文件(或注解)就可以把数据库的数据直接映射到POJO上,我们可以通过操作POJO 去操作数据库记录。对于不擅长SQL的程序员来说,这是莫大的惊喜,因为通过Hibernate 你几乎不需要编写SQL就能操作数据库的记录。代码清单1-3是Hibernate的配置信息。
<?xml version="l.0” encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN” "http://hibernate.sourceforge.net/hibernate- configuration-3.0.dtd”> <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name=nhibernate•connection•urln>jdbc:mysql://localhost:3306/mybatis?zero DateTimeBehavior=convertToNull</property> <property name=nhibernate.connection•usernamen>root</property> <property name=Hhibernate.connection.passwordff>learn</property> <mapping resource=,fcom/learn/mybatis/chapter1/po/TUser.hbm.xmlH/> <mapping resource=ncom/learn/mybatis/chapterl/po/TRole.hbm.xmln/> </session-factory〉 </hibernate-configuration〉
别看代码很多,但是全局就是这样的一个XML文件,作为数据库连接信息,配置信 息也相对简易。然后建立Hibernate的工厂对象(SessionFactory),用它来做全局对象,产 生Session接口,就可以操作数据库了。
public class HibernateUtil ( private static final SessionFactory sessionFactory; static ( try ( Configuration cfg = new Configuration().configure ("hibernate.cfg.xml"); sessionFactory = cfg.buildSessionFactory(); } catch (Throwable ex) { System.err.printIn("Initial SessionFactory creation failed." +ex); throw new ExceptionlnlnitializerError(ex); } } public static SessionFactory getSessionFactory() ( return sessionFactory; } }
上面的操作为的是产生Hibernate的SessionFactroy。它作为全局,可以到处引用,那 么剩下来的使用就非常简单了。
public class HibernateExample ( public static void main(String[] args) ( Session session = null; try { session = HibernateUtil.getSessionFactory().openSession();
TRole role = (TRole)session.get(TRole.class, 1L); System.err.printin(nrole_name = >” + role.getRoleName()); ) finally ( if (session != null) ( session.close(); } } ) }
按照代码的方法来实现代码清单1-1的功能有以下好处:
•消除了代码的映射规则,它全部被分离到了 XML或者注解里面去配置。
•无需再管理数据库连接,它也配置在XML里面。
• 一个会话中,不要操作多个对象,只要操作Session对象即可。
•关闭资源只需要关闭一个Session便可。
这就是Hibernate的优势,在配置了映射文件和数据库连接文件后,Hibernate就可以通 过Session操作,非常容易,消除了 JDBC带来的大量代码,大大提高了编程的简易性和可 读性。此外,它还提供级联、缓存、映射、一对多等功能,以便我们使用。正因为具有这 些优势,Hibernate成为了时代的主流框架,被大量应用在各种Java数据库的访问中。Hibernate是全表映射,你可以通过HQL去操作POJO进而操作数据库的数据。
但是Hibernate有缺陷吗?当然有,世界上没有完美无缺的方案。作为全表映射框架, 举个例子来说,如果我们有张账务表(按年分表),比如2015年表命名为bill2015,至IJ了 2016年表命名为bill2016,要动态加映射关系,Hibernate需要破坏底层封装才能做到。又 比如说,一些账务信息往往需要和某些对象关联起来,不同的对象有不同的列,因此列名 也是无法确定的,显然我们没有办法配置XML去完成映射规则。再者如果使用存储过程, Hibernate也是无法适应的。这些都不是致命的,最为致命的问题是性能。Hibernate屏蔽了 SQL,那就意味着只能全表映射,但是一张表可能有几十到上百个字段,而你感兴趣的只 有2个,这是Hibernate无法适应的。尤其是在大型网站系统,对传输数据有严格规定,不 能浪费带宽的场景下就更为明显了。有很复杂的场景需要关联多张表,Hibernate全表逐级 取对象的方法也只能作罢,写SQL还需要手工的映射取数据,这带来了很大的麻烦。此外, 如果我们需要优化SQL, Hibernate也是无法做到的。
我们稍微总结一下Hibernate的缺点:
•全表映射带来的不便,比如更新时需要发送所有的字段。
•无法根据不同的条件组装不同的SQL。
•对多表关联和复杂SQL查询支持较差,需要自己写SQL,返回后,需要自己将数据组装为POJOo
•不能有效支持存储过程。
•虽然有HQL,但是性能较差。大型互联网系统往往需要优化SQL,而Hibernate做不到。
在当今大型互联网中,灵活、SQL优化,减少数据的传递是最基本的优化方法,显然 Hibernate无法满足我们的要求。这时MyBatis框架诞生了,它提供了更灵活、更方便的方 法,弥补了 Hibernate的这些缺陷。
四、MyBatis
为了解决Hibernate的不足,一个半自动映射的框架MyBatis应运而生。之所以称它为 半自动,是因为它需要手工匹配提供POJO、SQL和映射关系,而全表映射的Hibernate 只 需要提供POJO和映射关系便可。
历史上,MyBatis的前身是Apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了 google code,并且改名为 MyBatis02013 年 11 月迁移到 Github, 所以目前MyBatis是由Github维护的。
iBatis 一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBatis 提供的持久层框架包括SQL Maps和DAOCData Access Objects)o它能很好地解决Hibernate 遇到的问题。与Hibernate不同的是,它不单单要我们提供映射文件,还需要我们提供SQL 语句。MyBatis所需要提供的映射文件包含以下三个部分。
- SQL。
- 映射规则。
- POJO。
在MyBatis里面,你需要自己编写SQL,虽然比Hibernate配置得多,但是MyBatis可 以配置动态SQL,这就解决了 Hibernate的表名根据时间变化,不同的条件下列名不一样的 问题。同时你也可以优化SQL,通过配置决定你的SQL映射规则,也能支持存储过程,所 以对于一些复杂的和需要优化性能SQL的查询它更加方便,MyBatis几乎能做到JDBC所能 做到的所有事情。MyBatis具有自动映射功能。换句话说,在注意一些规则的基础上,MyBatis 可以给我们完成自动映射,而无需再写任何的映射规则,这大大提高了开发效率和灵活性。如图1.3所示为MyBatis的0RM映射模型。
让我们看看如何实现JdbcExample的功能。首先是数据库及其他的基础配置
<?xml version=nl. 0n encoding=nUTF-8,1 ?> <!DOCTYPE configuration PUBLIC n-//mybatis.org//DTD Config 3.0//EN” "http://mybatis•org/dtd/mybatis-3-config•dtdn> <configuration> <environments defau11=H deve1opmentn > <environment id=ndevelopmentH> <transactionManager type=nJDBCf,/> <dataSource type=,fPOOLEDn> name=Hdrivern value=f,com.mysql . jdbc . DriverH/> name=nurln value=Hjdbc:mysql://localhost:3306/mybatisM/> <property name="username" value="root"/> <property name="password" value="learn"/> </dataSource> </environment> </environments> <mappers> <mapper resource=ncom\learn\mybatis\chapterl\pojo\role.xmln /> </mappers> </configuration>
这就是MyBatis的基础配置文件。其次是一个映射文件,也十分简单
这里我们给出了 SQL,但是并没有给出映射规则,因为这里我们使用的SQL列名和POJO的属性名保持一致,这个时候MyBatis会自动提供映射规则,所以省去了这部分的配置工作。再者,我们还需要一个接口,注意仅仅是接口,而无需实现类.
public interface RoleMapper ( public Role getRole(Long id); }
为了使用MyBatis,我们还需要建立SqlSessionFactory。
public class MyBatisUtil ( private static SqlSessionFactory sqlSessionFactory = null; public static SqlSessionFactory getSqlSessionFactroy() ( Inputstream inputstream = null; if (sqlSessionFactory == null) ( try ( String resource = f,mybatis_config • xmln; sqlSessionFactory = new SqlSessionFactoryBuilder().build (Resources.getResourceAsStream(resource)); return sqlSessionFactory; ) catch (Exception ex) ( System.err.printIn(ex.getMessage()); ex.printStackTrace(); } ) return sqlSessionFactory; } }
现在我们可以用MyBatis来实现代码清单1-1的功能了
public class MyBatisExample { public static void main(String[] args) ( SqlSession sqlSession = null; try ( sqlSession =MyBatisUtil.getSqlSessionFactroy().openSession ();
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); Role role = roleMapper.getRole(1L); System.err.printIn(nrole_name = >” + role•getRoleName()};
finally ( sqlSession.close ();
}
}
}
这样便完成了 MyBatis的代码编写工作,SQL和映射规则都在XML里面进行了分离,而MyBatis更为灵活。你可以自由书写SQL,定义映射规则。此外,MyBatis提供接口编 程的映射器只需要一个接口和映射文件便可以运行,消除了在iBatis时代需要SqlSession 调度的情况。
五、什么时候用MyBatis
通过对JDBC、Hibernate和MyBatis的介绍,我们有了一些认识。JDBC的方式在目前 而言极少用到,因为你需要提供太多的代码,操作太多的对象,麻烦不说,还极其容易出 错,所以这不是一种推荐的方式,在实际开发中直接用JDBC的场景也是很少的。
Hibernate作为较为流行的Java ORM框架,它确实编程简易,需要我们提供映射的规 则,完全可以通过IDE生成,同时无需编写SQL确实开发效率优于MyBatiso此外,它也 提供了缓存、日志、级联等强大的功能,但是Hibernate的缺陷也是十分明显的,多表关联 复杂SQL,数据系统权限限制,根据条件变化的SQLo存储过程等场景使用Hibernate十 分不便,而性能又难以通过SQL优化。所以注定了 Hibenate只适用于在场景不太复杂,要 求性能不太苛刻的时候使用。
如果你需要一个灵活的、可以动态生成映射关系的框架,那么MyBatis确实是一个最 好的选择。它几乎可以代替JDBC,拥有动态列、动态表名,存储过程都支持,同时提供 了简易的缓存、日志、级联。但是它的缺陷是需要你提供映射规则和SQL,所以它的开发 工作量比Hibernate略大一些。
你需要根据项目的实际情况去选择框架。因为MyBatis具有高度灵活、可优化、易维 护等特点,所以它目前是大型移动互联网项目的首选框架。
下面各章,我们将分别讨论MyBatis的应用、原理和实践。