第5章〓MyBatis基础知识本章目标 1. 了解ORM的概念及与MyBatis的关系。 2. 掌握MyBatis中常用的API的使用方法。 3. 正确编写MyBatis的配置文件。 4. 正确编写MyBatis中的映射文件。MyBatis本是Apache的一个开源项目iBatis, 2010年这个项目由Apache Software Foundation 迁移到Google Code,并且改名为MyBatis,2013年11月迁移到GitHub。 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis将JDBC进行了封装,避免了直接使用 JDBC操作数据库。MyBatis 可以使用简单的 XML 或注解配置映射类和表之间的关系,将接口和 Java 的 POJO(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。 5.1ORM与MyBatis MyBatis框架是ORM的一种实现。所谓的ORM(ObjectRelational Mapping,对象关系映射)就是为了解决面向对象程序设计与数据库之间不匹配的技术,它通过描述Java对象与数据库表之间的映射关系,自动将Java应用程序中的对象持久化到关系数据库的表中。 对象关系映射是随着面向对象的软件开发方法发展产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象关系映射系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。当开发一个应用程序的时候,如果不使用ORM,则可能会写不少数据访问层的代码,用来从数据库保存、删除、读取对象信息等。通常在数据库编程中写很多方法以完成读取对象数据、改变状态对象等任务,这些代码不可避免会有重复。 ORM解决的主要问题是对象关系的映射。域模型和关系模型都建立在概念模型基础上。域模型是面向对象的,而关系模型是面向关系的。一般情况下,一个持久化类和一个表对应,类的每个实例对应表中的一条记录,类的每个属性对应表的每个字段。 Java Web框架开发技术(Spring+Spring MVC+MyBatis)第5章MyBatis基础知识ORM技术的特点如下: (1) 提高了开发效率。由于ORM可以自动对实体对象与数据库中的表进行字段与属性的映射,所以我们已经不需要一个专用的、庞大的数据访问层。 (2) ORM提供了对数据库的映射,不用SQL直接编码,就能像操作对象一样从数据库获取数据。 从系统结构上看,采用ORM的系统一般都是多层系统,系统的层次多了,效率就会降低。ORM是一种完全的面向对象的做法,而面向对象的做法也会对性能产生一定的影响。 当前的ORM框架产品有很多,常见的主要有Hibernate和MyBatis两个框架,这两个框架的对比见表51。表51Hibernate框架和MyBatis框架的对比对比项目Hibernate框架MyBatis框架自动化程度Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,建立对象与数据库表的映射,是一个全自动的、完全面向对象的持久层框架MyBatis是一个开源对象关系映射框架,是一个半自动化的持久层框架性能 Hibernate 自动生成SQL,有些语句较为烦琐,会多降低性能MyBatis 手动编写SQL,可以避免不需要的查询,提高系统性能对象管理 Hibernate 是完整的对象关系映射的框架,在开发工程中无须过多关注底层实现,只要管理对象即可MyBatis 需要自行管理对象与表的映射关系缓存管理Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表对象映射中配置是哪种缓存MyBatis的二级缓存配置都是在每个具体的表对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制学习难易学习门槛较高容易学习和掌握5.2MyBatis的开发环境5.2.1MyBatis框架的JAR包下载想在Web开发项目中添加MyBatis的支持非常简单,只将MyBatis的JAR包加入构建路径即可。编写本书时,MyBatis的最新版本是mybatis3.5.2,本书中的所有MyBatis案例都基于这个版本。MyBatis的JAR包下载地址为https://github.com/mybatis/mybatis3/releases。单击网址,可打开如图51所示的页面。 图51MyBatis框架JAR包下载页面 在网页中单击mybatis3.5.2.zip超链接即可下载JAR包。下载后将其中的mybatis3.5.2.jar文件复制到工程的lib目录中即可,同时不要忘记添加MySQL的数据库驱动程序。此处,我们添加的驱动程序是mysqlconnectorjava5.1.40bin.jar。除了上面的网站还有一个网站,对开发者会有帮助,网址为http://www.mybatis.org/mybatis3/zh/gettingstarted.html。 这个网站为开发者提供了最新的MyBatis相关技术内容,同时提供了MyBatis框架所需的一些基本知识,网站提供了5种语言,包括简体中文,非常便于查阅。MyBatis中文帮助网站如图52所示。 图52MyBatis中文帮助网站 5.2.2日志信息配置 使用MyBatis的时候,有时需要查看SQL语句执行的详细信息,为此MyBatis提供了非常简单有效的方法——日志。 MyBatis的内置日志工厂提供日志功能。内置日志工厂将日志交给以下其中一种工具作代理: SLF4J; Apache Commons Logging; Log4j 2; Log4j; JDK logging。 MyBatis 内置日志工厂基于运行时自省机制选择合适的日志工具。它会使用第一个查找得到的工具(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。 不少应用服务器(如 Tomcat 和 WebShpere)的类路径中已经包含 Commons Logging,所以在这种配置环境下的 MyBatis 会把它作为日志工具。这将意味着,在诸如 WebSphere 的环境中,它提供了 Commons Logging 的私有实现,你的 Log4J 配置将被忽略。如果你的应用部署在一个类路径已经包含 Commons Logging 的环境中,而你又想使用其他日志工具,可以通过在 MyBatis 配置文件 mybatisconfig.xml 里添加一项 setting 选择别的日志工具,如下面的代码所示。<configuration> <settings> ... <setting name="logImpl" value="LOG4J"/> ... </settings> </configuration>logImpl 可选的值有SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING,或者是实现了接口 org.apache.ibatis.logging.Log 且构造方法是以字符串为参数的类的完全限定名(可以参考org.apache.ibatis.logging.slf4j.Slf4jImpl.java的实现)。 下面的例子将使用Log4J配置完整的日志服务,共两个步骤。 步骤1: 添加 Log4J 的 JAR 包。 因为我们使用的是 Log4J,就要确保它的JAR包在应用中是可用的。要启用 Log4J,只要将 JAR包添加到应用的类路径中即可。对于 Web 应用或企业级应用,需要将 log4j.jar 添加到 WEBINF/lib 目录下;对于独立应用,可以将它添加到JVM 的classpath 启动参数中。 步骤2: 配置 Log4J。 配置 Log4J 比较简单,假如需要记录下面这个映射器接口的日志: Package com.mapper; public interface CustomerMapper { @Select("SELECT FROM customer WHERE id = #{id}") Customer selectCustomer(int id); }在应用的类路径中创建一个名称为 log4j.properties 的文件,文件的具体内容如下: # Global logging configuration log4j.rootLogger=ERROR, stdout # MyBatis logging configuration... log4j.logger.com.mapper.CustomerMapper=TRACE # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n添加以上配置后,Log4J 就会记录com.mapper.Customer的详细执行操作,且仅记录应用中其他类的错误信息。 也可以将日志的记录方式从接口级别切换到语句级别,从而实现更细粒度的控制。下面的配置只对 selectCustomer语句记录日志: log4j.logger. com.mapper.CustomerMapper.selectCustomer=TRACE与此相对,可以对一组映射器接口记录日志,只要对映射器接口所在的包开启日志功能即可。log4j.logger.com.mapper=TRACE某些查询可能会返回庞大的结果,此时只想记录其执行的SQL语句,而不想记录结果该怎么办?MyBatis 中 SQL 语句的日志级别被设为DEBUG(JDK 日志设为 FINE),结果的日志级别为 TRACE(JDK 日志设为 FINER)。所以,只要将日志级别调整为 DEBUG 即可达到目的: log4j.logger.com.mapper=DEBUG要记录日志的是类似下面的映射器文件,而不是映射器接口又该怎么做呢?1. <?xml version="1.0" encoding="UTF-8" ?> 2. <!DOCTYPE mapper 3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5. <mapper namespace="com.mapper.ClazzMapper"> 6. <select id="selectClazzById" resultType="Clazz"> 7. select from tb_clazz where id = #{id} 8. </select> 9. </mapper>如需对 XML 文件记录日志,只对命名空间增加日志记录功能即可。log4j.logger.com.mapper.ClazzMapper =TRACE要记录具体语句的日志可以这样做: log4j.logger.com.mapper.ClazzMapper.selectClazzById=TRACE从上面的配置可以看到,为映射器接口和 XML 文件添加日志功能的语句没有差别。 5.3MyBatis中的API 使用 MyBatis 的主要 Java 接口就是 SqlSession。可以通过这个接口执行命令,获取映射器和管理事务。我们会概括讨论一下 SqlSession 本身,但是首先还是要了解如何获取一个 SqlSession 实例。SqlSessions 是由 SqlSessionFactory 实例创建的。SqlSessionFactory 对象包含创建 SqlSession 实例的所有方法。而 SqlSessionFactory 本身是由 SqlSessionFactoryBuilder 创建的,它可以从 XML、注解或手动配置 Java 代码创建 SqlSessionFactory。 5.3.1SqlSessionFactoryBuilder SqlSessionFactoryBuilder 有9个 build() 方法,每种都允许从不同的资源中创建一个 SqlSession 实例。SqlSessionFactoryBuilder的部分源码如下。1. public class SqlSessionFactoryBuilder { 2. 3. public SqlSessionFactory build(InputStream inputStream) { 4. return build(inputStream, null, null); 5. } 6. public SqlSessionFactory build(Configuration config) { 7. return new DefaultSqlSessionFactory(config); 8. } 9. public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { 10. try { 11. XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); 12. return build(parser.parse()); 13. } catch (Exception e) { 14. throw ExceptionFactory.wrapException("Error building SqlSession.", e); 15. } finally { 16. ErrorContext.instance().reset(); 17. try { 18. inputStream.close(); 19. } catch (IOException e) { 20.//Intentionally ignore. Prefer previous error. 21. } 22. } 23. } 24. }在上面的代码中,XMLConfigBuilder是一个解析XML的工具类,如果调用有参数 environment 的 build() 方法,MyBatis 将会使用 configuration 对象配置这个 environment。当然,如果指定了一个不合法的 environment,就会得到错误提示。如果调用了不带 environment 参数的 build() 方法,就使用默认的 environment(在上面的示例中指定为 default="development" 的代码)。 如果调用了有参数 properties 实例的方法,MyBatis 就会加载那些 properties(属性配置文件)。那些属性可以用${propName} 语法形式多次用在配置文件中。 5.3.2SqlSessionFactory 这里给出的是3个比较常用的生成SqlSessionFactory 的build()方法,下面的代码演示了第3行的build()方法生成SqlSessionFactory示例的应用。1. String resource= "mybatis-config.xml"; 2. InputStream inputStream= Resources.getResourceAsStream(resource); 3. SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder(); 4. SqlSessionFactory factory= builder.build(inputStream); 以上代码是读取mybatisconfig.xml配置文件构建一个InputStream输入流,然后调用SqlSessionFactoryBuilder的build()方法创建一个SqlSessionFactory工厂类。典型的mybatisconfig.xml配置文件如下所示。1. <?xml version="1.0" encoding="UTF-8" ?> 2. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 3. "http://mybatis.org/dtd/mybatis-3-config.dtd"> 4. <configuration> 5. <!--1.配置环境 ,默认的环境id为mysql--> 6. <environments default="mysql"> 7. <!--1.2.配置id为mysql的数据库环境 --> 8. <environment id="mysql"> 9. <!-- 使用JDBC的事务管理 --> 10. <transactionManager type="JDBC" /> 11. <!--数据库连接池 --> 12. <dataSource type="POOLED"> 13. <property name="driver" value="com.mysql.jdbc.Driver" /> 14. <property name="url" 15. value="jdbc:mysql://localhost:3306/mybatis" /> 16. <property name="username" value="root" /> 17. <property name="password" value="root" /> 18. </dataSource> 19. </environment> 20. </environments> 21. <!--2.配置mapper的位置 --> 22. <mappers> 23. <mapper resource="com/mapper/CustomerMapper.xml" /> 24. </mappers> 25. </configuration>配置文件中的<configuration> 标签描述了数据库连接的信息,<mappers>标签描述了映射文件的位置信息。SqlSessionFactory是一个接口,它有两个实现类,分别是SqlSessionFactoryManager和DefaultSqlSessionFactory。前面讲到的几个类之间的关系可用如图53所示的UML类图表示。 图53MyBatis中的主要类和接口之间的关系 在基于MyBatis框架的应用程序中,都是以SqlSessionFactory为中心,它的主要作用是生产MyBatis的核心接口对象SqlSession。 5.3.3SqlSession SqlSession是MyBatis的关键对象,是执行持久化操作的对象,类似于JDBC中的Connection。它是应用程序与持久层之间执行交互操作的一个单线程对象,也是MyBatis执行持久化操作的关键对象。SqlSession对象完全包含对数据库所有执行SQL操作的方法,对JDBC操作数据库进行了封装,可以用SqlSession实例直接执行被映射的SQL语句。每个线程都应该有它自己的SqlSession实例,SqlSession的实例不能被共享,同时也是线程不安全的。绝不能将SqlSession实例的引用放在一个类的静态字段,甚至是实例字段中。也绝不能将SqlSession实例的引用放在任何类型的管理范围中,如放在Servlet中的HttpSession对象中。使用完SqlSession后必须关闭,应该确保使用finally块来关闭它。 SqlSession 类中有20多个方法,将它们按功能进行分组,便于理解。 1. 对数据库进行增、删、改、查的方法 <T> T selectOne(String statement, Object parameter) 查询方法,查询结果是一个泛型对象或为null。 <E> List<E> selectList(String statement, Object parameter) 查询方法,查询结果可以是0到多个泛型对象的集合,也可以为null。 <K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey) 查询方法,查询结果是一个Map集合。 int insert(String statement, Object parameter) 更新方法,执行插入该表中记录的操作。 int update(String statement, Object parameter) 更新方法,执行更新该表中记录的操作。 int delete(String statement, Object parameter) 更新方法,执行删除该表中记录的操作。 seqSession.selectCursor(String statement,Object parameter); 方法中的参数statement是映射文件中对应的元素的id,参数parameter是执行SQL语句所需绑定的动态参数。 selectOne 和 selectList 的区别是,selectOne 必须返回一个对象或 null 值。如果返回值多于一个,就会抛出异常。如果不能确定返回对象的数量,请使用 selectList。如果需要查看返回对象是否存在,一个好的办法是返回一个值即可(0 或 1)。selectMap 稍微特殊一点,因为它会将返回的对象的其中一个属性作为 key 值,将对象作为 value 值,从而将多结果集转为 Map 类型值。 与上面7个方法对应的还有7个重载的方法,除了selectMap()方法有两个参数外,其他的都只有一个参数statement。 <T> T selectOne(String statement); <E> List<E> selectList(String statement); <T> Cursor<T> selectCursor(String statement); <K,V> Map<K,V> selectMap(String statement, String mapKey); int insert(String statement); int update(String statement); int delete(String statement)。 这7个方法中没有动态绑定参数parameter,所以一般是将参数写在映射文件中,这种应用场景不是很多。 如果查询结果是一个集合,要对返回结果的数量进行控制,可以使用以下的重载方法。 <E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds) <K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds) 这两个方法是前面selectList和selectMap两个方法的重载,其中的rowbounds对象参数可以设置返回结果集合的起始位置和数量。 2. 事务控制方法 控制事务作用域有3个方法。如果已经设置了自动提交或正在使用外部事务管理器,这3个方法不会起作用。然而,如果正在使用 JDBC 事务管理器,由Connection 实例控制,那么这3个方法就会派上用场。 void commit(): 事务提交。 void rollback(): 回滚事务。 void close(): 关闭SqlSession对象。 3. 缓存管理方法 MyBatis 使用到两种缓存: 本地缓存(local cache)和二级缓存(second level cache)。 void clearCache(): 清空本地缓存。 4. 获取Mapper接口 上述SqlSession的 insert、update、delete 和 select 方法都很强大,但也有些烦琐,可能会产生类型安全问题,并且对于IDE和单元测试也很不方便。因此,一个更通用的方式是通过使用映射器类执行映射语句。一个映射器类就是一个仅需声明与 SqlSession 方法相匹配的方法的接口类。 <T> T getMapper(Class<T> type): 获取映射器类接口的方法。 Mapper接口的使用后面会详细介绍。 5.4MyBatis的配置文件 MyBatis的配置文件是MyBatis框架的必备文件,包含了影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下。1. <?xml version="1.0" encoding="UTF-8" ?> 2. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"