MyBatis

概述

持久层技术的底层解决方案:
JDBC技术:Connection,PreParedStatement,ResultSet
JDBC简单回顾:

Class.forName("com.mysql.jdbc.Driver") // 加载驱动
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); // 获取连接

Statement stmt = conn.createStatement();  // 获取statement
PreparedStatement ptmt = conn.preparedStatement(sql);  // 预编译sql语句

ResultSet rs1 = stmt.executeQuery("SELECT user_name, age FROM imooc_goddess");  // 执行sql语句
ResultSet rs2 = ptmt.execute();

rs.getString(itemName);  // 获取结果
rs.getInt(itemName);
rs.next();

mybatis是一个Java语言的持久层框架,封装了JDBC的细节,使开发者只需关注SQL语句本身,无需关心注册驱动,创建连接,创建statement等繁琐的过程。
并使用ORM思想实现了结果集的封装。
ORM:
object relational mapping 对象关系映射
ORM就是把数据表和实体类以及实体类中的属性对应起来,让使用者可以通过操作实体类实现操作数据表。

入门案例

工程搭建:

  1. 创建maven工程并导入相关依赖
  2. 创建实体类和持久层接口
  3. 创建mybatis的主配置文件,在xml配置文件中完成环境配置。
  4. 创建映射配置文件。
    映射配置文件位置必须和dao接口的包名结构相同。
    映射配置文件中mapper标签中的namespace属性,必须是dao接口的全限定类名。
    映射文件中的操作配置,id属性的取值必须是dao接口中的方法名,resultType属性写名返回值的封装类型
<mapper namespace="com.meituan.domain.User">  <!-- 对应的实体类的全限定类名 -->
	<select id="findAll" resultType="com.meituan.domain.User">  <!-- 接口中的方法名,返回值封装类型 -->
  	select * from user;
  </select>
  <insert id="saveUser" parameterType="com.meituan.domain.User">
  	insert into user(username, sex, age, address, birthday) value(#{username}, #{sex}, #{age}, #{address}, #{birthday});
    <!-- 在出入语句执行之后获取插入id,相当于在执行插入操作之后再执行一条语句 -->
    <selectKey keyProperty="id" keyColumn="id" resultType="int" order="after">
    	select last_insert_id();
    </selectKey>
  </insert>
  <update id="updateUser" parameterType="com.meituan.domain.User">
  	update user set (username=#{username}, sex=#{sex}, age=#{age}, address=#{address}, birthday=#{birthday}) where id=#{id};
  </update>
  <delete id="deleteUser" parameterType="java.lang.Integer">  <!-- 此处的输入类型可以写java.lang.Integer或int -->
  	delete from user where id=#{uid};
  </delete>
  
  <!-- 在使用模糊查询时,在调用方法时添加%,这种方法更常用,因为利用到了预处理preparedStatement,效率更高 -->
  <!-- List<User> users = userDao.findByName("%王%"); -->
  <select id="findByName" parameterType="string" resultType="com.meituan.domain.User">
  	select * from user where username like #{name};
    select * from user where username like '${name}';  <!-- 此时模糊查询时不需要% -->
  </select>
  
  <select id="count" resultType="int">
  	select count(id) from user;
  </select>
</mapper>

mybatis使用步骤:

  1. 读取配置文件
  2. 创建SqlSessionFactory工厂
  3. 使用工厂生产SqlSession对象
  4. 使用SqlSession对象创建Dao接口的代理对象
  5. 使用代理对象执行方法
  6. 释放资源
InputStream in = Resources.getResourceAsStream(xmlPath);  // 读取配置文件
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();  // 构建者模式
SqlSessionFactory factory = builder.build(in);  // 创建工厂,工厂模式
// autoCommit参数默认为false,我们可以将其设置为true,省略提交事务的步骤,但一般不采用,因为我们一般要将多个SQL语句组成一个事务,整体提交。
SqlSession session = factory.openSession(autoCommit=false);  // 生产Session对象
IUserDao userDao = session.getMapper(IUserDao.class);  // 创建Dao接口的代理对象
List<User> users = userDao.findAll();  // 使用代理接口执行方法
session.commit();  // 提交事务
session.close();
in.close();  // 释放资源

获取配置文件路径的安全方法:

  1. 使用类加载器,可以读取类路径的配置文件。
  2. 使用ServletContext对象的getRealPath()方法获取当前程序运行的路径。
    mybatis支持用户不用代理接口,自己写实现类,但会增加许多冗余代码,一般不用。

注解配置

首先需要在主配置文件中的mapper标签下,使用class属性指定添加注解的全限定类名。在使用xml配置文件时需要使用resource属性指定映射配置文件的包地址

<mappers>
	<mapper class="com.meituan.dao.IUserDao" />
</mappers>

SQL输入参数

输入参数:
在执行语句时,mybatis的输入可以是基本数据类型int,string或自定义数据类型。
当输入自定义数据类型时,在SQL语句中需要使用#{}或KaTeX parse error: Expected 'EOF', got '#' at position 36: …会自动利用OGNL进行解析。 #̲{}和{}的区别:#{}符号会在编译时将SQL语句中的对应字段变成?,通过prepareStatement中的set方法来赋值。能够有效避免SQL注入,更推荐使用。
${}符号只是在编译阶段将相应字段替换成变量值。
OGNL表达式:
(Object Graphic Navigation Language)对象图导航语言
这是一种通过对象的取值方式来获取数据的语言,在写法上忽略了get。
例如,通过类获取对象:user.getUsername();
通过OGNL表达式获取:user.username

  1. 当我们需要通过两个以上的类进行查询时,可以将这些类重新封装成另一个查询类(vo),再将这一查询类作为输入参数输入到SQL语句中。
  2. 解决实体类中的属性和表中的字段名称不同的问题:
    在SQL语句上作修改,为查询后的字段取别名,此方法效率高,但需要需改每一条SQL语句。
    添加配置,将查询后的字段和实体类中的属性一一对应,此方法效率低,但SQL语句无需修改,操作更方便。
<mapper namespace="com.meituan.domain.User">
	<select id="findByVo" parameterType="com.meituan.domain.QueryVo" resultType="com.meituan.domain.User">
  	select * from user where username like #{user.username};
  </selcet>
  
  <!-- 方法1. 当实体类中的属性名和表中的词条名不同时,可以通过给查询后的词条起别名的方法,将查询结果封装到实体类中 -->
  <select id="findAll" resultType="com.meituan.domain.User">
  	select id as userId, username as userName, sex as userSex, age as userAge, birthday as userBirthday from user;
  </select>
  
  
  <!-- 方法2. 将实体类中的属性和查询字段一一对应 -->
  <!-- 此时,方法的返回值需要配置成resultMap="userMap",此方法效率不高,通常不用 -->
  <resultMap id="userMap" type="com.meituan.domain.User">
		  <id property="userId" column="id" /id>  <!-- 主键的配置,property为属性名,column为字段名 -->
      <result property="userName" column="username" /result>
      <result property="userSex" column="sex" /result>  <!-- 非主键的配置,property为属性名,column为字段名 -->
      <result property="userAge" column="age" /result>
      <result property="userBirthday" column="birthday" /result>
  <resultMap>
</mapper>

连接池

连接池一般用集合实现,连接池中存储着一些连接,他们在各个线程需要访问数据库时被各个线程调用,调用结束后放回连接池。类似于线程池。
连接池的特点是必须线程安全,且实现队列的特性。
可以在实际开发中节省许多创建连接的时间。
mybatis中的连接池:
mybatis提供了三种配置连接池的方式:
在主配置文件中,利用dataSource标签中的type属性配置连接池。
mybatis中的三种连接池配置:
POOLED:传统的javax.sql.DataSource规范中的连接池
UNPOOLED:不使用连接池,采用传统的获取连接方式
JNDI:采用服务器提供的JNDI技术来实现,获取DataSource 对象。如果不是web或者maven的war工程师不能使用的。
mybatis中连接池的调用原理:
mybatis中维持了两个连接池,分别是空闲池和活动池。
相应伪代码:

if 空闲池中有连接:
	将空闲池中的连接拿出
else if 活动池未满:
	在活动池中创建一个新的连接
else:
	将最旧的(oldest)连接返回

Mybatis中的事务

事务的ACID特性:

  1. 原子性(Atomicity):事务的内部操作不可分割,一个事务要么全部成功,要么全部失败,并且失败时不能对数据库产生任何影响。
  2. 一致性(Consistency):事务的执行使数据库从一个状态转换成另一个状态,但不能影响数据库整体的稳定。
  3. 隔离性(Isolation):当多个用户同时访问数据库时,各个事务之间不能相互影响。
  4. 持久性(Durability):事务正确执行后,对数据库的影响是永久性的。

数据库的三范式:
第一范式:表中的字段具有原子性,即不可分割
第二范式:在第一范式的基础上,表中的所有非主键字段应该完全依赖于主键,即根据主键可以唯一的确定其他字段。
第三范式:在第二范式的基础上,表中的所有其他字段之间不存在依赖关系。

MyBatis中是使用sqlSession对象中的commit和rollback方法实现事务的提交和回滚。

动态SQL语句

MyBatis中可以动态的配置我们需要的SQL语句,具体可以通过以下标签实现:
if标签
我们可以通过if标签动态配置SQL语句,具体用法为

<select id="findByCondition" resultType="com.meituan.domain.User" parameterType="com.meituan.domain.User">
	select * from user where 1=1
  <if test="username != null">  <!-- if中test属性后面的是Java语句,username对应的是类中的属性名。 -->
  	and username = #{username}  <!-- 可能接上的SQL语句,所以与要用and。前一个username是字段名称,后一个username是类中的属性名称 -->
  </if>
  <if test"sex != null">
  	and sex = #{sex}
  </if>
</select>

select标签内为完整的SQL语句,当if条件成立时,将if标签内的语句与原语句拼接,否则忽视。
where标签
在使用if标签时,为了保证在if条件都不成立时SQL语句也能正常执行,我们通常要在SQL语句后面加上where 1=1,在MyBatis中我们可以利用where标签来去掉where 1=1操作

<select id="findByCondition" resultType="com.meituan.domain.User" parameterType="com.meituan.domain.User">
	select * from user
  <where>
  	<if test="username != null">
    	and username = #{username}
    </if>
    <if test="sex != null">
    	and sex = #{sex}
    </if>
  </where>
</select>

foreach标签
在我们需要进行类似select * from user where id in(47, 48, 49);的查询时,foreach标签可以帮助我们动态配置SQL语句

<select id="findUsersById" parameterType="com.meituan.domain.IdList" resultType=>
	select * from user
  <where>
  	<if test="ids != null and ids.size() > 0">  <!-- 此处的与也用and -->
  		<foreach collection="ids" open="and id in (" close=")" item="id" seperate=",">
    		#{id}
    	</foreach>
  	</if>
  </where>
</select>

在实际项目中,我们还可以通过sql标签提取重复的sql语句

<sql id="defalutSelect">
	select * from user
</sql>

<select id="findByCondition" resultType="com.meituan.domain.User" parameterType="com.meituan.domain.User">
	<include refid="defaultSelect" />
  <where>
  	<if test="username != null">  <!-- if中test属性后面的是Java语句,username对应的是类中的属性名。 -->
  		and username = #{username}  <!-- 可能接上的SQL语句,所以与要用and。前一个username是字段名称,后一个username是类中的属性名称 -->
  	</if>
  	<if test"sex != null">
  		and sex = #{sex}
  	</if>
  </where>
</select>

注解配置

mybatis中的注解大致可以分为三类:

  1. 增删改查
    @Insert @Update @Select @Delete
  2. 结果集映射
  3. 缓存
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值