MyBatis第一章
一、概述
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
它是一个半自动的ORM(对象-关系-映射)框架,需要开发人员自己手写SQL指令来实现。优点是更加灵活。在它之前还有一ORM框架叫Hibernate ,Hibernate是一个全自动的ORM框架,不需要手写SQL指令,只需要配置好,框架会自动生成SQL指令,所以它的配置比较麻烦,而MyBatis的配置相关简单很多。
目前企业开发框架的选择搭建:
SSM(Spring,SpringMVC,MyBatis), 最流行的。
SSH2(Spring,SpringMVC,Hibernate),有,但非常少。
SSH (Spring,Struts2,Hibernate),新项目基本不会选择,老项目在用。
ORM框架的作用就是把Java程序中的实体对象,映射成数据库中的数据,或者把数据库中的数据映射成Java程序中的实体对象。避免了手写JDBC代码来对数据的封装。持久层实现类可以省略,不需要了。
二、特点:
1.简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
2.灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
3.解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
4.提供映射标签,支持对象与数据库的orm字段关系映射
5.提供对象关系映射标签,支持对象关系组建维护
6.提供xml标签,支持编写动态sql。
三、Mybatis的功能架构分为三层:
(1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
(2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
(3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
四、动态SQL
MyBatis 最强大的特性之一就是它的动态语句功能。如果您以前有使用JDBC或者类似框架的经历,您就会明白把SQL语句条件连接在一起是多么的痛苦,要确保不能忘记空格或者不要在columns列后面省略一个逗号等。动态语句能够完全解决掉这些痛苦。
尽管与动态SQL一起工作不是在开一个party,但是MyBatis确实能通过在任何映射SQL语句中使用强大的动态SQL来改进这些状况。动态SQL元素对于任何使用过JSTL或者类似于XML之类的文本处理器的人来说,都是非常熟悉的。在上一版本中,需要了解和学习非常多的元素,但在MyBatis 3 中有了许多的改进,现在只剩下差不多二分之一的元素。MyBatis使用了基于强大的OGNL表达式来消除了大部分元素。
注意:Java开发框架比较多,大部分都是开源的,每个框架的版本更新也比较快,从而也导致有一些意外的出现,主要是版本之间不兼容的问题。企业在开发过程中不会去使用最新版本,用比较成熟的,稳定的版本。
五、配置文件
MyBatis的配置文件有主配置文件,建议命名:(mybatis-config.xml),和映射文件,建议命名:(XXXXMapper.xml)XXXX 表示持久化接口名,例如:StudentDaoMapper.xml
主配置文件配置,所有节点如下:
- properties 属性
- settings 设置
- typeAliases 类型别名
- typeHandlers 类型处理器
- objectFactory 对象工厂
- plugins 插件
- environments 环境
environment 环境变量
transactionManager 事务管理器
dataSource 数据源
properties属性:它的作用是引入属性文件,然后可用${}读取到属性文件中的键值,提供给框架使用。
<properties resource="config/jdbc.properties"> </properties>
<dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> |
settings节点: 是一个配置节点,对MyBatis框架运行所需的参数进行配置,MyBatis的参数其实在有默认值,只在我们需要更改参数值时,才进行配置。在开发中,通常情况下是不需要配置的。正面默认配置:
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/></settings> |
typeAliases 节点:负责给实体对象命名一个别名,简化操作。如果不使用别名,则我们应用某个实体对象时,需要用它的类的全限定名称(com.icss.entity.Student),如果命名了别名,则可以用别名来替代这个全限定名。
<!-- 给实体对象命名别名 --> <typeAliases> <!-- 给Student实体类命名别名,名称是:stu --> <!-- <typeAlias type="com.icss.entity.Student" alias="stu"/> --> <!-- 把这个包中所有的类命名别名,命名规则 :别名与类名相同 --> <!--一般使用这种方式(以包的形式取别名) --> <package name="com.icss.entity"/> </typeAliases> |
typeHandlers节点:它是一个类型处理器,处理执行SQL指令时,所传入的参数,或SQL执行完毕后,返回的结果集的数据在做实体对象映射时的类型的一个处理。
objectFactory节点:MyBatis操作数据时,需要用到一个SqlSession的连接对象,这个对象需要依赖工厂来创建,默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。工厂在一个程序中只需要一个,所以通常把它做成单例模式。工厂对象的创建,需要依赖主配置文件。
plugins属性:这是一个插件属性,MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。
environments 节点:配置MyBatis运行时与数据库交互的环境,它里面可以配置事务管理机制,连接池机制,及数据库连接对象。
<!-- 环境配置,在开发中,通常会配置多个环境:开发环境,运行环境,测试环境,环境是如何与数据库交互的配置 --> <environments default="development"> <environment id="development"> <!-- 事务管理机制,type的取值有2个:JDBC,MANAGED,建议用JDBC --> <transactionManager type="JDBC"/> <!-- 数据源对象,负责与数据库连接 type:表示连接机制,取值有3个:UNPOOLED|POOLED|JNDI --> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> |
最后完整配置。
<?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> <!-- 引入属性文件 --> <properties resource="config/jdbc.properties"/> <!-- 配置MyBatis框架自身的属性参数,通常不需要配置 --> <settings> <setting name="cacheEnabled" value="true"/> </settings> <!-- 给实体对象命名别名 --> <typeAliases> <!-- 给Student实体类命名别名,名称是:stu --> <!-- <typeAlias type="com.icss.entity.Student" alias="stu"/> --> <!-- 把这个包中所有的类命名别名,命名规则 :别名与类名相同 --> <package name="com.icss.entity"/> </typeAliases> <!-- 环境配置,在开发中,通常会配置多个环境:开发环境,运行环境,测试环境,环境是如何与数据库交互的配置 --> <environments default="development"> <environment id="development"> <!-- 事务管理机制,type的取值有2个:JDBC,MANAGED,建议用JDBC --> <transactionManager type="JDBC"/> <!-- 数据源对象,负责与数据库连接 type:表示连接机制,取值有3个:UNPOOLED|POOLED|JNDI --> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <!-- 如果有映射时,对映射文件进行配置 --> <mappers> <!-- 加载单个映射文件 --> <!-- <mapper resource="com/icss/mapping/UserMapper.xml"/> --> <!-- 加载这个包中的所有映射文件 --> <package name="com.icss.mapping"/> </mappers> </configuration> |
六、MyBatis相关的API
SqlSessionFactoryBuilder:这个对象是一个工厂对象,它依赖主配置文件来创建,创建这个对象时,会去加载主配置文件中的配置。构建方式是把主配置文件形成一个输入流,再构建SqlSessionFactory对象。
创建方法:
package com.icss.util; import java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; /** * 创建SqlSessionFactory对象的工厂类 * 向外公开一个方法,返回SqlSession对象 * @author Administrator */ public class SqlSessionFactoryUtil { private SqlSessionFactoryUtil() { } static Object object = new Object(); // 读取主配置文件,把文件生成一个输入流 static InputStream is = null; static { try { is = Resources.getResourceAsStream("config/mybatis-config.xml"); } catch (IOException e) { System.out.println(e.getMessage()); } } static SqlSessionFactory factory = null; // 工厂只需要一个,做成单例模式 private static SqlSessionFactory getSqlSessionFactory() { if (factory == null) { synchronized (object) { if (factory == null) { factory = new SqlSessionFactoryBuilder().build(is); } } } return factory; } //向外公开一个方法,返回SqlSession对象 public static SqlSession getsqlSession() { return getSqlSessionFactory().openSession(); } } |
SqlSession对象:这个对象在这里也称之为会话,但是Servlet中的Session不是同一个概念。这个SqlSession 对象表示一次对数据库的访问,它封装了一系列对数据操作的方法(insert,update,delete,selectOne,selectList)。这个对象从SqlSessionFactory对象中创建。操作完毕也必须要关闭。
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
目前SqlSession所封装的一系列针对数据进行操作的方法已经过时了,现在的用法调用它的:getMapper方法。这个方法返回BlogMapper数据持久化接口的代理对象。然后直接调用代理对象的方法来操作数据。
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
映射文件配置
<?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"> <!-- 映射文件与持久化接口通过namespace属性进行关联,--> <mapper namespace="com.icss.dao.AdminDao"> <!-- 持久化接口的方法名与SQL指令映射的ID值一致 Admin是实体的别名 --> <select id="getAdmins" resultType="Admin"> select * from admin </select> <!-- 添加一条数据,参数是Admin类型,#{loginId}会自动去取参数中的属性值。 --> <insert id="add" parameterType="Admin"> insert into admin values(null,#{loginId},#{loginPwd},#{isAdmin}) </insert> <!-- 查询单个对象,参数是一个string型,返回一个实体对象 --> <select id="getAdminById" parameterType="string" resultType="Admin"> select * from admin where loginId=#{loginId} </select> <!-- 删除,参数是int型,#{id}在执行时,自动加载调用传入的参数值, --> <delete id="delete" parameterType="int"> delete from admin where id=#{id} </delete> <!-- com.icss.dao.AdminDao.delete:delete from admin where id=? --> </mapper> |
业务层类的改造:
package com.icss.service.impl; import java.util.List; import org.apache.ibatis.session.SqlSession; import com.icss.dao.AdminDao; import com.icss.entity.Admin; import com.icss.service.AdminService; import com.icss.util.SqlSessionFactoryUtil; public class AdminServiceImpl implements AdminService { //AdminDao dao = new AdminDaoImpl(); @Override public List<Admin> getAdmins() { // 取到AdminDao持久化接口的代理对象, SqlSession sqlSession = SqlSessionFactoryUtil.getsqlSession(); try { // 创建持久化接口的代理对象 AdminDao dao1 = sqlSession.getMapper(AdminDao.class); return dao1.getAdmins(); // 调用代理对象的方法 } catch (Exception e) { System.out.println(e.getMessage()); } finally { sqlSession.close(); // 关闭会话 } return null; } @Override public int add(Admin admin) { Admin a = getAdminById(admin.getLoginId()); if (a != null) { // 根据用户名查询到对象,表示账号存在 return -1; } else { // 此账号可用,就向数据库插入数据, // 取到AdminDao持久化接口的代理对象, SqlSession sqlSession = SqlSessionFactoryUtil.getsqlSession(); int result = 0; try { // 创建持久化接口的代理对象 AdminDao dao1 = sqlSession.getMapper(AdminDao.class); result = dao1.add(admin); // 调用代理对象的方法 sqlSession.commit(); //增删改必须提交事务 } catch (Exception e) { System.out.println(e.getMessage()); } finally { sqlSession.close(); // 关闭会话 } return result; // 返回结果有0,1两种情况 } } @Override public Admin getAdminById(String loginId) { Admin admin = null; // 取到AdminDao持久化接口的代理对象, SqlSession sqlSession = SqlSessionFactoryUtil.getsqlSession(); try { // 创建持久化接口的代理对象 AdminDao dao1 = sqlSession.getMapper(AdminDao.class); admin = dao1.getAdminById(loginId); // 调用代理对象的方法 } catch (Exception e) { System.out.println(e.getMessage()); } finally { sqlSession.close(); // 关闭会话 } return admin; } @Override public int delete(int id) { // 取到AdminDao持久化接口的代理对象, SqlSession sqlSession = SqlSessionFactoryUtil.getsqlSession(); int result = 0; try { // 创建某个持久化接口的代理对象 AdminDao dao1 = sqlSession.getMapper(AdminDao.class); result = dao1.delete(id); // 调用代理对象的方法 sqlSession.commit(); // 提交事务 } catch (Exception e) { System.out.println(e.getMessage()); } finally { sqlSession.close(); // 关闭会话 } return result; } @Override public int login(Admin admin) { //根据用户名查询对象,没有查询到则返回Null Admin a = getAdminById(admin.getLoginId()); if (a == null) { // 表示用户名错误 return 1; } else if (!a.getLoginPwd().equals(admin.getLoginPwd())) { return 2; // 表示密码错误 } return 3; } } |
七、XML ,dtd
XML叫可扩展性标记语言,也就是说节点是可以任意扩展的,这种文件格式特别适合描述数据结构,此文件非常适合计算机之间传递。节点严格区分大小写,节点有开始和结构,如果节点下没有子节点了,则可以直接 / 结束。一个文档只有一个根节点,然后是子节点,子节点是可以任意扩展的。
是对一个XML文件结构的一种约束,约束包括:节点名称,节点顺序,节点的属性,节点的值的类型,节点的数量。
? :表示0-1个子节点,可以没有,最多有一个。
+ : 表示1-N个子节点,也就是说至少有一个。
* :表示 0—N个子节点。可以没有,有也可以有多个。
八、数据库连接池
与数据库交互时,必须要创建连接对象,操作结束,连接对象必须要关闭。这样会浪费大量的时间来创建连接。连接池的概念是:在程序中先创建一些连接对象,如果要使用连接,则从连接池中拿连接对象,这样就节省了创建连接对象所需的时间。当操作完毕之后,连接关闭,但实际上是没有真正释放掉连接对象,而是把连接对象返还到连接池中。连接池有相关的一些属性:初始连接数 10,最大连接数 100。最大空闲时间 。最小连接数 。最大空闲数 。最小空闲数。
Java 常用的连接池技术有:DBCP 、C3P0、BoneCP、Proxool等。