MyBatis学习总结


MyBatis是一个持久层的开源框架,对JDBC进行了封装,可以使程序员专注于sql代码,而不必关注与操作数据库无关的相关JDBC代码。

1.JDBC中存在的问题

  1. 数据库连接创建和释放频繁,造成资源浪费。
  2. Sql语句在代码中硬编码,导致后期维护需要修改代码
  3. JDBC中对于Sql语句的输入参数和输出参数都需要手动设置

2.MyBatis的解决方式

  1. 使用数据库连接池解决该问题
  2. 将sql代码写在mapper.xml文件中,使得sql语句和Java代码分离
  3. 在MyBatis都过对输入参数和输出结果的设置,框架自动封装数据

MyBatis和Hibernate的主要区别

Hibernate:是一个ORM框架,不需要手动配置sql语句,数据库无关性好。
MyBatis:不完全是一个ORM框架,需要手动配置sql语句,数据库无关性不好。

3.MyBatis的基本架构

SqlMapConfig.xml:核心配置文件。
Mapper.xml:用于配置sql语句,该文件需要通过mapper标签在核心配置文件中加载。
SqlSessionFactory:会话工厂对象,用于产生对应的SqlSession对象。
SqlSession:可以通过该对象执行操作数据库的sql语句或者获得Mapper接口的动态代理对象,该对象使用完后需释放,若是修改数据库的语句,需要通过该对象的commit()方法提交事务。
Executor:Mybatis底层的执行器接口,用于操作数据库,他又两个实现类,一个是基本执行器、一个是缓存执行器。
MappedStatement:Mybatis底层的一个语句封装类。在mapper.xml中,一个sql语句与一个MappedStatement对象对应。同时,该类对sql输入参数和输出结果都进行了定义。

其它类:
SqlSessionFactoryBuilder:用于读取SqlMapConfig.xml,并创建SqlSessionFactory。

在这里插入图片描述

Mapper动态代理的开发

基本的开发思路:
1.编写Mapper接口用于确定操作数据库的方法。
2.编写与该Mapper接口对应的Mapper.xml文件,使用Mybatis开发规范完成编写。
3.在SqlMapConfig.xml文件中加入Mapper.xml文件。
4.测试:使用sqlSession的getMapper()方法,获得对应Mapper接口的动态代理对象,调用该对象的方法完成数据库的操作。

开发规范

共四点:
1.Mapper.xml文件中的namespace与mapper接口的类路径相同。
2.Mapper.xml文件中每个statement的id与Mapper接口中定义的方法名相同。
3.Mapper.xml文件中每个statement的parameterType的类型与Mapper接口的参数类型相同。
4.Mapper.xml文件中每个statement的resultType的类型与Mapper接口的返回值类型相同。

4.SqlMapConfig.xml中的元素

SqlMapConfig.xml中配置的内容和顺序如下:
properties(属性):用于加载外部配置文件
settings(全局配置参数)
typeAliases(类型别名):用于为类定义别名,可以单个定义或者批量定义.
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器):加载Mapper.xml文件 。可以单个加载或批量加载。

5.Mapper.xml文件的配置

输入映射和输出映射

获取输入参数的两种方式

1.#{}:使用JDBC中的PreparedStatement完成sql语句的预编译,#{}表示一个占位符。可以接受简单数据类型和POJO属性值。
2${}:表示拼接sql串,可以接收简单数据类型哦POJO属性值(接受的简单数据类型之${}中只能是value)。

输入映射

输入映射的类型可以是简单类型、POJO类型,还有POJO包装对象(即POJO类中的一个属性是另外一个POJO)
。如果一个sql输入参数有多个或者需要一个LIST或者数组,可以用一个POJO类封装输入参数。在Mapper.xml中引入该POJO类完成传参。

POJO包装对象的配置如下

public class QueryVo {
	// 包含其他的pojo
	private User user;

	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
}

Mapper.xml的配置可以参考

<!-- 根据用户名模糊查询 -->
	<select id="findUserByQueryVo" parameterType="pojo.QueryVo"
		resultType="pojo.User">
		select *
		from user
		<!-- 方式一 -->
		where username like '%${user.username}%'
		<!-- 方式二 -->
		<!-- where username like "%"#{user.username}"%" -->
	</select>
输出映射

输出映射可以是简单类型,POJO类型或者POJO列表。

<!-- 根据用户名模糊查询 -->
	<select id="findUserByQueryVo" parameterType="pojo.QueryVo"
		resultType="pojo.User">
		select *
		from user
		<!-- 方式一 -->
		where username like '%${user.username}%'
		<!-- 方式二 -->
		<!-- where username like "%"#{user.username}"%" -->
	</select>
resultMap

如果查询结果中的列名与POJO对象的属性名都一一对应,则使用resultType自动获取查询结果即可,否则需要使用resultMap完成查询结果的映射。resultMap还可以用于一对一及一对多的关联映射。

<!-- resultMap最终还是要将结果映射到pojo上,type就是指定映射到哪一个pojo -->
	<!-- id:设置ResultMap的id -->
	<resultMap type="order" id="orderResultMap">
		<!-- 定义主键 ,非常重要。如果是多个字段,则定义多个id -->
		<!-- property:主键在pojo中的属性名 -->
		<!-- column:主键在数据库中的列名 -->
		<id property="id" column="id" />

		<!-- 定义普通属性 -->
		<result property="userId" column="user_id" />
		<result property="number" column="number" />
		<result property="createtime" column="createtime" />
		<result property="note" column="note" />
	</resultMap>

	<!-- 查询所有的订单数据 -->
	<select id="queryOrderAll" resultMap="orderResultMap">
		SELECT id, user_id,
		number,
		createtime, note FROM `order`
	</select>

关联映射

由下图可见,user实体对于order实体是1对多的映射,order实体对于user实体是一对一的映射。
在这里插入图片描述

一对一映射

需求:现在需要查询每一个订单及其用户信息。则需要映射的结果包括order表和user表的结果。
POJO配置

public class Order {
	// 订单id
	private int id;
	// 用户id
	private Integer userId;
	// 订单号
	private String number;
	// 订单创建时间
	private Date createtime;
	// 备注
	private String note;
	//用户属性
	private User user;
	...
}

Mapper.xml配置
使用resultMap标签完成结果映射

<resultMap type="order" id="OrderUserResultMap">
		<id property="id" column="id" />
		<result property="userId" column="user_id" />
		<result property="number" column="number" />
		<result property="createtime" column="createtime" />
		<result property="note" column="note" />

		<!-- association:配置一对一属性 -->
		<!-- property:order里面的User属性名 -->
		<!-- javaType:属性类型 -->
		<association property="user" javaType="user">
			<!-- id:声明逐渐,表示user_id是关联查询对象的唯一标识 -->
			<id property="id" column="user_id" />
			<result property="username" column="username" />
			<result property="address" column="address" />
		</association>
	</resultMap>

	<select id="queryOrderUserResultMap" resultMap="OrderUserResultMap">
		select o.*,
		u.username,
		u.address
		from orders o left join user u
		on o.user_id = u.id
	</select>
一对多映射

需求:现在需要查询每一个用户及其订单信息。则需要映射的结果包括order表和user表的结果。
POJO配置

public class User implements Serializable {
	private static final long serialVersionUID = 1L;
	private Integer id;
	private String username;// 用户姓名
	private String sex;// 性别
	private Date birthday;// 生日
	private String address;// 地址

	private List<Order> orders;
	....
}

Mapper.xml配置

<!-- 一对多查询 -->
	<resultMap type="user" id="userOrderResultMap">
		<id property="id" column="id" />
		<result property="username" column="username" />
		<result property="birthday" column="birthday" />
		<result property="sex" column="sex" />
		<result property="address" column="address" />

		<!-- 配置一对多的关系 -->
		<collection property="orders" javaType="list" ofType="order">
			<!-- 配置主键,是关联Order的唯一标识 -->
			<result property="number" column="number" />
			<result property="createtime" column="createtime" />
			<result property="note" column="note" />
		</collection>
	</resultMap>
	
	<!-- 一对多关联,查询订单同时查询该用户下的订单 -->
	<select id="queryUserOrder" resultMap="userOrderResultMap">
		select
		u.*,
		o.id oid,
		o.number,
		o.createtime,
		o.note
		from user u left join orders o
		on u.id=o.user_id
	</select>

动态sql

通过if、where、sql片段和foreach标签可以产生动态的sql语句。

if标签

可以根据test属性的返回值,输出标签体中的sql语句

<!-- 根据条件查询用户 -->
<select id="queryUserByWhere" parameterType="user" resultType="user">
	SELECT id, username, birthday, sex, address FROM `user`
	WHERE 1=1
	<if test="sex != null and sex != ''">
		AND sex = #{sex}
	</if>
	<if test="username != null and username != ''">
		AND username LIKE
		'%${username}%'
	</if>
</select>

where 标签
<!-- 根据条件查询用户 -->
<select id="queryUserByWhere" parameterType="user" resultType="user">
	SELECT id, username, birthday, sex, address FROM `user`
<!-- where标签可以自动添加where,同时处理sql语句中第一个and关键字 -->
	<where>
		<if test="sex != null">
			AND sex = #{sex}
		</if>
		<if test="username != null and username != ''">
			AND username LIKE '%${username}%'
		</if>
	</where>
</select>

sql标签

可以将经常使用的算sql用sql标签保存,通过标签加载,实现sql片段的代码重用。

<!-- 声明sql片段 -->
<sql id="userFields">
	id, username, birthday, sex, address
</sql>

<!-- 根据条件查询用户 -->
<select id="queryUserByWhere" parameterType="user" resultType="user">
	<!-- SELECT id, username, birthday, sex, address FROM `user` -->
	<!-- 使用include标签加载sql片段;refid是sql片段id -->
	SELECT <include refid="userFields" /> FROM `user`
	<!-- where标签可以自动添加where关键字,同时处理sql语句中第一个and关键字 -->
	<where>
		<if test="sex != null">
			AND sex = #{sex}
		</if>
		<if test="username != null and username != ''">
			AND username LIKE
			'%${username}%'
		</if>
	</where>
</select>

foreach标签

需求:需要查询输入多个POJO参数时,可以使用foreach标签来传递数组或者List。
如:SELECT * FROM user WHERE id IN (1,10,24)

public class QueryVo implements Serializable{

	private static final long serialVersionUID = 1L;
	
	private List<Integer> ids;
	...
}
<!-- 根据ids查询用户 -->
<select id="queryUserByIds" parameterType="queryVo" resultType="user">
	SELECT * FROM `user`
	<where>
		<!-- foreach标签,进行遍历 -->
		<!-- collection:遍历的集合,这里是QueryVo的ids属性 -->
		<!-- item:遍历的项目,可以随便写,,但是和后面的#{}里面要一致 -->
		<!-- open:在前面添加的sql片段 -->
		<!-- close:在结尾处添加的sql片段 -->
		<!-- separator:指定遍历的元素之间使用的分隔符 -->
		<foreach collection="ids" item="item" open="id IN (" close=")" separator=",">
			#{item}
		</foreach>
	</where>
</select>

6.参考文献

《黑马课程的培训资料》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值