ORM(Object/Relational Mapping):对象关系映射
ORM作用:把持久化对象的保存、修改、删除等操作,转换成对数据库的操作。 数据库一行记录,对应java中的一个对象,多行记录对应一个集合 |
Mybatis:
MyBatis 本是 apache 的一个开源项目 iBatis , 2010年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis 。
1、Mybatis是一个开元的Dao层框架(持久层框架),在mvc的model层 |
2、Mybatis是一个半自动化的ORM框架,(hibernate是个全自动的ORM框架) |
3、Mybatis使用的是接口映射:一个接口对应一个映射文件 |
主要思想:将程序中的大量 SQL 语句抽取出来,配置在配置文件中,以实现 SQL 的灵活配置。 |
专业名词 | 概念 |
---|---|
model: | 数据模型(Dao层)和业务模型(Service层) |
ORM框架: | 可以操作java对象来操作数据库的Dao层框架 |
全自动化ORM框架: | 完全通过对象来操作数据库,没有表的概念。 |
半自动化ORM框架: | 实现了对象关系映射,但是保留了数据底层的操作,比如书写sql语句 |
MyBatis的优点:
- 简单小巧易于上手,方便浏览修改 SQL 语句
- 解除 SQL 与程序代码的耦合
- 提供映射标签,支持对象与数据库的 ORM 字段关系映射
- 提供 xml 标签,支持编写动态 SQL
JDBC和Mybatis对比:
- 减少代码量,少些Dao层
- JDBC中的sql语句是硬编码,Mybatis的sql写到配置文件中,对组件进行了解耦
- 使用Mybatis可以省去编码Dao接口的实现类
映射文件名:
- 不使用Mybatis生成Dao接口:表名+Mapper.xml
- 使用Mybatis生成Dao接口:必须和接口在同包,而且同名
接 口 名: UserMapper 实体名+Mapper
接口映射: <package name="映射文件所在包">
注意: Mybatis主配置文件名SqlMapConfig
可以修改,映射文件名在不需要使用Mybatis生成Dao接口类时可以任意修改
接口方法多参数,而parameterType只能有一个参数?
1、把多个参数封装成一个对象 addOrde(Order order)
2、多参数不封装,使用@Param注解 这种方法不通用,需要所有参数的数据类型都一致
注意: 一个配置文件中不能有相同的id,建议使用第一种
列和属性名不一致如何进行映射?
方法1、给列起别名,优点:简单,缺点:不通用
方法2、通用,可以反复使用
<resultMap type="domain.order" id="orderResultMap">
<id column="order_id" property="orderId" />
<result column="order_receive_address" property="addresss">
</resultMap>
Hibernate和mybatis区别和相同点:
相同点:都是ORM框架,都是基于SessionFactory创建session,一个session对应一个操作单元。
Hibernate | mybatis |
---|---|
全自动化的ORM框架:完全基于对象对数据库进行操作 | 半自动化的ORM框架:需要手动编写sql |
Hibernate是实体映射 | Mybatis是接口映射 |
适合中小型项目,Hibernate做复杂查询不便于优化(如:多表关联),做简单查询语法简洁 | 适合中大型项目,互联网项目(需求变化快),Mybatts是基于sql,便于sql优化 |
Mybatis的功能架构分为三层:
API接口层:提供给外部使用的接口 API,开发人员通过这些本地 API 来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。 |
数据处理层:负责具体的 SQL 查找、SQL 解析、SQL 执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。 |
基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件,为上层的数据处理层提供最基础的支撑。 |
Mybatis框架架构
加载配置:MyBatis 应用程序根据XML配置文件加载运行环境,创建 SqlSessionFactory,SqlSessionFactory,配置来源于两个地方,一处是配置文件,一处是 Java 代码的注解,将 SQL 的配置信息加载成为一个个 MappedStatement 对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。 |
SQL 解析:当 API 接口层接收到调用请求时,会接收到传入 SQL 的 ID 和传入对象(可以是 Map、JavaBean 或者基本数据类型),Mybatis 会根据 SQL 的 ID 找到对应的 MappedStatement,然后根据传入参数对象对 MappedStatement 进行解析,解析后可以得到最终要执行的 SQL 语句和参数。 |
SQL 执行:SqlSession 将最终得到的 SQL 和参数拿到数据库进行执行,得到操作数据库的结果。 |
结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成 HashMap、JavaBean 或者基本数据类型,并将最终结果返回,用完之后关闭 SqlSession。 |
SqlSessionFactory
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 是单个数据库映射关系经过编译后的内存映像。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。SqlSessionFactory 是创建 SqlSession 的工厂。 |
SqlSession
SqlSession 是执行持久化操作的对象,它完全包含了面向数据库执行 SQL 命令所需的所有方法,可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。在使用完 SqlSession 后我们应该使用 finally 块来确保关闭它。 |
Mybatis开发步骤:
- 建立数据库
- 新建web项目,并加载jar包
- 写Mybatis的主配置文件SqlMapConfig.xml,数据库链接参数,导入映射文件
- 创建实体类JavaBean
- 创建实体类映射文件,写sql语句
- 测试类
使用Mybatis进行Dao层开发:
1、传统方式,继续编写Dao接口实现类 |
2、使用Mybatis的Mapper代理,自动生成Dao接口实现类 |
|
<?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>
<!-- 为JavaBean起类别名 -->
<typeAliases>
<!-- 指定一个包名起别名,将包内的 Java 类的类名作为类的类别名 -->
<package name="domain" />
</typeAliases>
<!-- 配置mybatis运行环境 -->
<environments default="development">
<environment id="development">
<!-- type="JDBC" 代表直接使用 JDBC 的提交和回滚设置 -->
<transactionManager type="JDBC" />
<!-- POOLED 表示支持JDBC数据源连接池,由 Mybatis 管理 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<!-- 通过 mapper 接口包加载整个包的映射文件 -->
<package name="mapper" />
</mappers>
</configuration>
简化:可以把数据库配置property属性,提取到db.properties文件中,然后在SqlMapConfig.xml中的<configuration>标签用如下标签引入:
<properties resource="db.properties"/>
并改写配置如下:
db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=root
settings 设置
settings 是 MyBatis 极为重要的设置,它们会改变 MyBatis 的运行时行为,如开启二级缓存、开启延迟加载等。 |
Mybatis的一级缓存:Sqlsession级别的,一个操作对应一个SqlSession |
Mybatis的二级缓存:mapp级别,一个接口/一个映射文件,对应一个二级缓存 |
开启二级缓存的作用:只查一回,以后直接在二级缓存中查取数据,使用于不常更新的数据。 |
默认开启一级、二级关闭
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
typeAliases 类型命名
作用: 减少类完全限定名的冗余
给全路径 com.cfn.User 起了一个别名 User。在映射文件中 parameterType 和 resultType 就可以直接使用别名 User ,无需使用全路径。
<typeAliases>
<typeAliase alias="User" type="com.cfn.User"/>
</typeAliases>
除了上述方法,还可以指定一个包名起别名, MyBatis 会在包名下面搜索需要的 JavaBean,将 Java 类的类名作为类的类别名,如:
指定一个包名起别名, MyBatis 会在包名下面搜索需要的 JavaBean,将 Java 类的类名作为类的类别名。
<typeAliases>
<package name="com.cfn.domain" />
</typeAliases>
typeHandlers 类型处理器
typeHandlers 的作用是实现 JDBC 类型和 Java 类型的之间的转换,MyBatis 中默认的类型处理器基本能满足日常需求,还可以自定义 typeHandlers 。
environments 环境
MyBatis 的环境配置实际是数据源的配置。MyBatis 可以配置多个环境,帮助你将 SQL 映射对应到多种数据库。
注:尽管可以配置多个环境,每个 SqlSessionFactory 实例只能对应一个数据库,有几个数据库就需要创建几个 SqlSessionFactory 实例。
接受环境配置的两个方法:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader, environment,properties);
如果忽略了环境参数,默认环境将会被加载:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader,properties);
transactionManager 事务管理器
MyBatis 中的两种事务管理器,即 type=”[JDBC|MANAGED]”:
JDBC:直接使用 JDBC 的提交和回滚设置
MANAGED:让容器来管理事务的整个生命周期
dataSource 数据源
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
MyBatis 三种內建的数据源类型,即 type=”[UNPOOLED|POOLED|JNDI]”
1、UNPOOLED
UNPOOLED 不支持 JDBC 数据源连接池,实现的只是每次被请求时打开和关闭连接。其包含的属性:
driver:JDBC 驱动的 Java 类的完全限定名,如 MySQL 的 com.mysql.jdbc.Driver
url:数据库的 JDBC URL 地址
username:数据库的用户名
password:数据库的密码
defaultTransactionIsolationLevel:默认的连接事务隔离级别。
2、POOLED
POOLED 支持 JDBC 数据源连接池,利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。
除了有 UNPOOLED 的属性外还有包括 poolMaximumActiveConnections 、 poolMaximumIdleConnections 等属性。
3、JNDI
JNDI 支持外部数据源连接池,它的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。其包含的属性:
initial_context:用来在 InitialContext 中寻找上下文
data_source:引用数据源实例位置的上下文的路径
mappers 映射器
mappers 用于引用已经定义好的映射文件,告诉 MyBatis 去哪寻找映射 SQL 的语句。常见的方法:
1、通过 resource 加载单个映射文件
<mappers>
<mapper resource="com/cfn/mybatis/mapper/UserMapper.xml"/>
</mappers>
2、通过完全限定资源定位符(绝对路径前加上”file:///”)加载单个映射文件
<mappers>
<mapper url="file:///workspace/MyBatisTest/src/com/cfn/mybatis/mapper/UserMapper.xml"/>
</mappers>
3、通过 mapper 接口对象加载单个映射文件
<mappers>
<mapper class="com.cfn.mybatis.mapper.UserMapper"/>
</mappers>
4、通过 mapper 接口包加载整个包的映射文件
<mappers>
<package name="com/cfn/mybatis/mapper" />
</mappers>
注:3和4中需要将 mapper 接口类名和 mapper.xml 映射文件名称保持一致,且在一个目录中。
package domain;
public class Customer {
private Integer customer_id;
private String customer_name;
private String customer_phone;
private String customer_self_pai;
//省略get/set方法
}
<?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="domain">
<select id="findById" parameterType="int" resultType="domain.Customer">
select * from t_customer where customer_id = #{customer_id};
</select>
<delete id="deleteCustomer" parameterType="domain.Customer">
delete from t_customer where customer_id = #{customer_id};
</delete>
<insert id="insertCustomer" parameterType="domain.Customer">
insert into t_customer
values(null, #{customer_name}, #{customer_phone}, #{customer_self_pai});
</insert>
<select id="findOrderSumMoney" resultType="totalMoney">
select o.order_id, sum(d.goods_num*g.goods_price) as total_money
from my_order o
join my_order_detail d on d.order_id = o.order_id
join my_goods g on g.goods_id = d.goods_id
group by o.order_id;
</select>
</mapper>
映射文件顶级元素
映射文件是所有 SQL 语句放置的地方,需要在配置文件的 mappers 标签中引用。 |
resultMap
描述如何从数据库结果集中来加载对象。 |
resultMap 不仅可以对列名进行实体属性名映射,还可以做多表关联 |
resultMap 的设计就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们的关系。 |
resultMap 的子元素包括:
construct:用来将结果注入到一个实例化好的类的构造方法中 |
|
id: 主键映射,一个 ID 结果,标记结果作为 ID |
result:注入到字段或 JavaBean 属性的普通结果,其他数据库表字段到 JavaBean 属性的映射。 |
association:复杂的类型关联,多个结果合成的类型 |
|
collection:复杂类型的集 也可以引用一个外部结果映射 |
discriminator:使用结果值来决定使用哪个结果集 |
|
注意: 在one方有多方集合 ——->collection 在多方有one方实体 ——->association |
resultMap 的属性包括:
id :当前命名空间中的一个唯一标识,用于标识一个 resultMap |
type:类的全限定名, 或者一个类型别名 |
result:注入到字段或 JavaBean 属性的普通结果 |
autoMapping:为这个ResultMap开启或者关闭自动映射,该属性会覆盖全局的属性 autoMappingBehavior。默认值为:unset |
除了用 id & result 将 JavaBean 属性映射到数据库字段外,还可以用构造方法实现映射。 |
首先为 JavaBean Customer 类添加有参的构造方法:
public Customer(Integer customer_id, String customer_name,
String customer_phone, String customer_self_pai) {
super();
this.customer_id = customer_id;
this.customer_name = customer_name;
this.customer_phone = customer_phone;
this.customer_self_pai = customer_self_pai;
}
resultMap 的代码如下:
<resultMap id="userMap" type="User">
<constructor>
<idArg column="customer_id" javaType="int"/>
<arg column="customer_name" javaType="String"/>
<arg column="password" javaType="String"/>
<arg column="customer_phone" javaType="String"/>
<arg column="customer_self_pai" javaType="String"/>
</constructor>
</resultMap>
属性 | 描述 |
---|---|
property | 映射到 JavaBean 的属性 |
column | 数据库的列名或列名的重命名标签 |
javaType | 一个 Java 类的完全限定名,或一个类型别名 。如果你映射到一个 JavaBean , MyBatis 通常可以断定类型。如果你映射到的是 HashMap,应该明确地指定 javaType 来保证所需的行为 |
jdbcType | 所支持的 JDBC 类型,其仅作用在对插入,更新和删除操作允许为空的列。这是 JDBC 的需要,而 MyBatis 不需要。如果直接使用 JDBC 编程,需要指定这个类型且有允许为空的列 |
支持的 JDBC 类型:
BIT、FLOAT、CHAR、TIMESTAMP、OTHER、UNDEFINED、TINYINT、REAL、VARCHAR、BINARY、BLOB、NVARCHAR、SMALLINT、DOUBLE、LONGVARCHAR、VARBINARY、CLOB、NCHAR、INTEGER、NUMERIC、DATE、LONGVARBINARY、BOOLEAN、NCLOB、BIGINT、DECIMAL、TIME、NULL、CURSOR、ARRAY
有时一个单独的数据库查询也许返回很多不同 (但是希望有些关联) 数据类型的结果集。 鉴别器元素就是被设计来处理这个情况的, 还有包括类的继承层次结构,它很像 Java 语言中的 switch 语句。 |
<!-- 下面的例子,当 sex 为 male ,才映射 sex 的属性。 -->
<resultMap id="userMap" type="User">
<id property="id" column="id" javaType="int"/>
<result property="username" column="username" javaType="String"/>
<result property="password" column="password" javaType="String"/>
<discriminator javaType="String" column="sex">
<case value="male" resultType="sexResult">
<result property="sex" column="sex" javaType="String"/>
</case>
</discriminator>
<result property="address" column="address" javaType="String"/>
</resultMap>
select
<select id="findById" parameterType="int" resultType="domain.Customer">
select * from t_customer where customer_id = #{customer_id};
</select>
这个语句被叫做 findById,有一个 int(即 Integer) 型参数,返回一个 Customer 类型的对象。
对应的JDBC语句:
String selectUserById = "select * from customer where customer_id=?";
PreparedStatement ps = conn.prepareStatement(findById);
ps.setInt(1, id);
select 的属性:
属性 | 描述 |
---|---|
id | 命名空间中唯一的标识符,可被其他语句引用 |
parameterType | 传入该语句的参数的完全限定名或别名 |
resultType | 该语句返回类型的完全限定名或别名,注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身 |
resultMap | 外部 resultMap 的命名引用 |
flushCache | 任何语句的调用都会导致本地缓存和二级缓存被清空,默认为 false |
useCache | 导致本条语句的结果被二级缓存,默认为 true |
timeout | 设置驱动器在抛出异常前数据库返回请求结果的秒数,默认为 unset(依赖驱动) |
fetchSize | 尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认为 unset(依赖驱动) |
statementType | 让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认为 PREPARED |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认为 unset(依赖驱动) |
databaseId | 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略 |
resultOrdered | 仅针对嵌套结果 select 语句适用,如果为 true,就是假设包含了嵌套结果集或是分组了,默认为 false |
resultSets | 仅对多结果集适用,它将列出返回的结果集并为每个结果集给一个名称,名称用逗号分隔 |
注: 返回时可以使用 resultType 或 resultMap,但不能同时使用。
insert 、update 和 delete 的属性:
属性 | 描述 |
---|---|
id | 命名空间中唯一的标识符,可被其他语句引用 |
parameterType | 传入该语句的参数的完全限定名或别名 |
flushCache | 任何语句的调用都会导致本地缓存和二级缓存被清空,默认为 false |
timeout | 设置驱动器在抛出异常前数据库返回请求结果的秒数,默认为 unset(依赖驱动) |
statementType | 让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认为 PREPARED |
useGeneratedKeys | (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 |
keyProperty | (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认为 unset |
keyColumn | (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置 |
databaseId | 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略 |
注: useGeneratedKeys、keyProperty 和 keyColumn 仅对 insert 和 update 有用。同时只有数据库支持自增长主键字段(比如 MySQL、SQL Server)才可以设置 useGeneratedKeys=”true”,像 Oracle 则不支持自增长 id,如果设置 useGeneratedKeys=”true” 就会报错。
parameters
在 SQL 映射语句中使用了参数 parameterType , MyBatis 可以使用基本数据类型和 Java 复杂数据类型。 |
1、基本数据类型参数
根据 id 查询用户信息
<select id="selectUserById" parameterType="int" resultType="User">
select * from user where id=#{id}
</select>
2、Java 实体类型参数
插入一条用户信息
<select id="insertOneUser" parameterType="User" >
insert into user(id,username, password, sex, address)
values(#{id}, #{username}, #{password}, #{sex}, #{address})
</select>
3、Map 参数
根据 username 和 sex 查询用户信息
<select id="selectUserByNameAndSex" parameterType="map" resultType="User">
select * from user where name=#{name} and sex=#{sex}
</select>
4、字符串替换
默认情况下,使用 #{} 格式的语法会导致 MyBatis 创建预处理语句属性并安全地设置值(比如?),这样做更安全,更迅速。 如果想直接在 SQL 语句中插入一个不改变的字符串。 比如,像 ORDER BY,可以使用: ORDER BY ${columnName},这里 MyBatis 不会修改或转义该字符串。 |
注: 以上述方法接受用户的输入数据是不安全的,容易造成 SQL 注入攻击。因此,要么不允许用户输入,要么自行转义并检验。
cache
MyBatis 包含一个非常强大的,可配置和定制的缓存特性。 |
默认情况下缓存是未开启的,除了局部的 session 缓存,它可以提高性能,且能解决全局依赖。 |
要开启二级缓存,只需SQL 映射文件中添加一行 cache 标签 |
<cache/><cache/>
语句的效果如下:
映射文件中的所有 select 语句将会被缓存 |
映射文件中的所有 insert,update 和 delete 语句会刷新缓存 |
缓存会使用 Least Recently Used(LRU)算法来收回 |
缓存不会被设定的时间所清空 |
缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用 |
缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改 |
创建一个 FIFO 缓存让其 60s 清空一次,存储512个对象或列表引用,返回的结果只读。因此在不同线程中的调用者之间修改它们会导致引用冲突。
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
缓存可用的回收策略有:
LRU – 最近最少使用的:移出最近较长周期内都没有被使用的对象 |
FIFO – 先进先出:按对象进入缓存的顺序来移除 |
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象 |
WEAK – 弱引用:强制性地移除基于垃圾收集器状态和弱引用规则的对象 |
在不同的命名空间里共享同一个缓存配置或者实例。在这种情况下,可以使用 cache-ref 来引用另外一个缓存。
<cache-ref namespace="com.shiyanlou.mybatis.mapper.UserMapper"/>