其他分享
首页 > 其他分享> > ORM 技术体系-MyBatis

ORM 技术体系-MyBatis

作者:互联网

一、核心 API 介绍
1、Mapper映射器。由一个 Java 接口和 XML 文件(或者注解构成)
2、SqlSession 就相当于一个数据库连接(Connection 对象),可以在一个事务里面执行多条 SQL,然后通过commit、rollback 方法提交或者回滚事务。
3、SqlSessionFactory 可以被认为是一个数据库连接池,里面存储SqlSession对象。单例模式的对象,在应用中被共享。
4、SqlSessionFactoryBuilder 的作用是使用构建者模式创建 SqlSessionFactory 接口对象。

二、全局配置文件、配置文件

三、Mybatis 中的参数绑定
在映射配置文件中向 SQL 语句中绑定参数的语法结构为#{ }和${ }。
#{ } 和 ${ }的区别:
#{ } 解析为一个 JDBC 预编译语句(PreparedStatement)的参数标记符占位符 ?。使用该方式可避免 SQL 注入。
仅 仅 为 一 个 纯 碎 的 S t r i n g 替 换 , 在 M y b a t i s 的 动 态 S Q L 解 析 阶 段 将 会 进 行 变 量 替 换 。 { } 仅仅为一个纯碎的 String 替换,在 Mybatis 的动态 SQL 解析阶段将会进行变量替换。 仅仅为一个纯碎的String替换,在Mybatis的动态SQL解析阶段将会进行变量替换。{ } 在预编译之前已经被变量替换了,这会存在 SQL 注入问题
四、使用 ThreadLocal 存储 SqlSession
如果多个 DML 操作属于一个事务,因为 commit()和 rollback()都是由 SqlSession 完成的,所以必须保证使用一个 SqlSession。但是多个不同的 DML 操作可能在不同类的不同方法中,每个方法中要单独的获取 SqlSession。比如商城下订单时,其实涉及商品库存变化、订单添加、订单明细添加、付款、日志添加等多个 DML 操作,分布在不同类中。如何在多个 DML 操作之间使用同一个 SqlSession 呢,可以使用 ThreadLocal 来存储。保证一个线程中的操作使用的都是一个 SqlSession。
创建 Mybatis 工具类

public class MybatisUtils {
private static ThreadLocal<SqlSession> threadLocal = new
ThreadLocal<>();
private static SqlSessionFactory sqlSessionFactory = null;
static{
//创建 SqlSessionFactory
InputStream is = null;
try{
is = Resources.getResourceAsStream("mybatis-cfg.xml");
}catch (IOException e){
e.printStackTrace();
}
sqlSessionFactory = new
SqlSessionFactoryBuilder().build(is);
}
//获取 SqlSession
public static SqlSession getSqlSession(){
SqlSession sqlSession = threadLocal.get();
if(sqlSession == null){
sqlSession = sqlSessionFactory.openSession();
threadLocal.set(sqlSession);
29}
return sqlSession;
}
//关闭 SqlSession
public static void closeSqlSession(){
SqlSession sqlSession = threadLocal.get();
if(sqlSession != null){
sqlSession.close();
threadLocal.set(null);
}
}
}

@Override
public void deleteUsersById(int userid) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
final int delete =
sqlSession.delete("com.bjsxt.mapper.UserMapper.deleteUsersById",
userid);
}

五、日志处理
日志处理,线上问题追踪、错误排查、基于日志的业务的逻辑统计分析。常用日志框架Log4J、common Logging 、slf4J
Log4j 的日志级别
Log4j 定义了 8 个日志级别(除去 OFF 和 ALL,可以说分为 6 个级别),优先级从低到高 DEBUG、INFO、WARN、ERROR 四个日志级别。

六、分页查询
1、rowbounds ->offset:偏移量,从 0 开始计数 limit:限制条数
List selectUsersRowBounds(RowBounds rowBounds);
2、SQL语句分页:SQL 语句来实现分页处理。在 MySQL 数据库中我们可以使用 limit 实现分页。

<select id="selectUsersLimit" resultType="users">
select * from users limit #{offset},#{limit}
</select>

3、PageHelper使用分页插件
分页查询 API
PageHelper.startPage(int pageNum,int pageSize);
给定分页参数,该方法需要在执行查询之前调用
pageNum:起始的页数,从 1 开始计算。
pageSize:每页显示的条数。
PageInfo 对象
存放分页结果对象
pageInfo.getList() 获取分页查询结果。
pageInfo.getTotal() 获取查询总条数。
pageInfo.getPages() 获取总页数。
pageInfo.getPageNum() 获取当前页。
pageInfo.getSize() 获取每页显示的条数。
使用步骤:

sqlSession.getMapper(UsersMapper.class);
UsersExample usersExample = new UsersExample();
PageHelper.startPage(3,2);
List<Users> list =usersMapper.selectByExample(usersExample);
list.forEach(System.out::println);
PageInfo<Users> pageInfo = new PageInfo<>(list);

七、MyBatis动态代理
------mybatis 主键值回填 ----------------

<insert id="insertUsersGetKey" useGeneratedKeys="true"
keyProperty="userid">
insert into users values(default ,#{username},#{usersex})
</insert>
<insert id="insert" keyColumn="alarm_dispose_id" keyProperty="alarmDisposeId"
parameterType="com.telecomyt.plat.cmac.alarm.entity.AlarmDispose" useGeneratedKeys="true">
insert into alarm_dispose (alarm_content_id........)values ()
------------动态代理原理解析mapperproxy.newInstance//代理对象也就是接口实现类、JDK动态代理-对象创建mapperProxy

------------动态SQL-----------------------

if条件的判断:

<if test="username != null and username != ''">
and username = #{username}
</if>

多选一条件:choose中没有条件满足执行otherwise

<select id="selectUsersByChoose" resultType="users">
select * from users where 1=1
<choose>
<when test="username != null and username != ''">
and username = #{username}
</when>
<when test="usersex != null and usersex != ''">
and usersex = #{usersex}
</when>
<otherwise>
and userid = 1
</otherwise>
</choose>
</select>

根据用户给定的条件进行查询使用 where 标签实现

select * from users
<where>
<if test="userid != 0">
and userid = #{userid}
</if>
<if test="username != null and username != ''">
and username = #{username}
</if>
<if test="usersex != null and usersex != ''">
and usersex = #{usersex}
</if>
</where>

模糊查询

select * from users where username like concat('%',#{name},'%')
<!--根据用户姓名模糊查询-->
<select id="selectUsersByLikeName" resultType="users">
<bind name="likeName" value="'%'+name+'%'"/>
select * from users where username like #{likeName}
</select>

set标签

update users
<set>
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="usersex != null and usersex != ''">
usersex = #{usersex},
</if>
</set>

集合

<select id="selectUsersByIdUseCollection" resultType="users">
select * from users where userid in
<foreach collection="collection" item="userid" open="("
separator="," close=")">
#{userid}
</foreach>
</select>

八、MyBatis缓存
缓存是一般的 ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力,缓存的重要性是不言而喻的。Mybatis 会将相同查询条件的 SQL 语句的查询结果存储在内存或者某种缓存介质当中MyBatis 缓存方式分为一级缓存和二级缓存,同时也可配置关于缓存设置。一级缓存是将结果缓存在 SqlSession 对象中,二级缓存是存储在 SqlSessionFactory 对象中。默认情况下,MyBatis 开启一级缓存,没有开启二级缓存。当数据量大的时候可以借助一些第三方缓存技术来协助保存 Mybatis 的二级缓存数据。

一级缓存也叫本地缓存,MyBatis 的一级缓存是在会话(SqlSession)层面进行缓存的。MyBatis 的一级缓存是默认开启的,不需要任何的配置。MyBatis 在开启一个数据库会话时,会创建一个新的 SqlSession 对象,SqlSession 对象中会有一个新的 Executor 对象。Executor 对象中持有一个新的PerpetualCache 对象;当会话结束时,SqlSession 对象及其内部的 Executor 对象还有 PerpetualCache 对象也一并释放掉。
在这里插入图片描述

二级缓存是 SqlSessionFactory 上的缓存,可以是由一个 SqlSessionFactory 创建的不同的 SqlSession 之间共享缓存数据。默认并不开启。二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis 要求缓存的 POJO 必须是可序列化的, 也就是要求实现 Serializable 接口。在映射配置文件中配置就可以开启缓存了。

二级缓存的特点

  1. 映射语句文件中的所有 select 语句将会被缓存。
  2. 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  3. 二级缓存是以 namespace 为单位的,不同 namespace 下的操作互不影响
  4. 如果在加入标签的前提下让个别 select 元素不使用缓存,可以使用 useCache属性,设置为 false。
  5. 缓存会使用默认的 Least Recently Used(LRU,最近最少使用的)算法来收回

九、多表查询



```java
<select id="selectList" parameterType="com.telecomyt.plat.cmac.alarm.dto.request.AlarmListParam" resultType="com.telecomyt.plat.cmac.alarm.dto.response.AlarmListAllResp">
SELECT
alarm_content.alarm_content_id as alarmContentId,
alarm_content.alarm_describe as alarmDescribe,
alarm_content.alarm_level as alarmLevel,
ahi.asset_id as assetHostId,
ahi.asset_name as assetHostName,
ahi.responsible_telephone as hostManagerPhone,
ahi.maintenance_telephone as hostMaintainPhone,
ahi.manu_facturers as factory,
dtio.dict_name as areaName,
alarm_dispose.alarm_dispose_status as alarmDisposeStatus
<if test="alarmListParam.alarmDisposeStatus == 2">
alarm_dispose.alarm_dispose_time as alarmDisposeTime,
alarm_dispose.alarm_comment as alarmComment,
asset_user.name as alarmDisposeUser
</if>
FROM
alarm_content
INNER JOIN alarm_dispose ON alarm_content.alarm_content_id = alarm_dispose.alarm_content_id
LEFT JOIN asset_user ON alarm_dispose.alarm_dispose_user_id = asset_user.user_id
LEFT JOIN asset_soft_server ass on alarm_content.alarm_service_name = ass.server_name
LEFT JOIN asset_host_info ahi on alarm_content.alarm_service_name = ahi.asset_name
LEFT JOIN dict_type_info dtio ON ahi.area = dtio.dict_id
<where>
alarm_dispose.alarm_dispose_status =#{alarmListParam.alarmDisposeStatus}
<if test="alarmListParam.alarmType !=null ">
AND alarm_content.alarm_type =#{alarmListParam.alarmType}
</if>
<if test="alarmListParam.alarmLevel !=null ">
AND alarm_content.alarm_level =#{alarmListParam.alarmLevel}
</if>
<if test="alarmListParam.alarmDescribe !=null and alarmListParam.alarmDescribe !=''">
AND alarm_content.alarm_describe like concat('%',#{alarmListParam.alarmDescribe},'%')
</if>
</where>
ORDER BY alarm_dispose.alarm_dispose_time desc
limit #{alarmListParam.pageNum},#{alarmListParam.pageSize}


<!--一对多映射关系-->
<resultMap id="usersAndOrdersMapper" type="com.bjsxt.pojo.Users">
<id property="userid" column="userid"/>
<result property="username" column="username"/>
<result property="usersex" column="usersex"/>
<collection property="orders" ofType="com.bjsxt.pojo.Orders">
    <id property="orderid" column="orderid"/>
    <result property="orderprice" column="orderprice"/>
</collection>
<resultMap>

<!--多对多关联查询-->
<resultMap id="usersAndOrdersAndItems" type="com.bjsxt.pojo.Users">
<id property="userid" column="userid"/>
<result property="username" column="username"/>
<result property="usersex" column="usersex"/>
<collection property="orders" ofType="com.bjsxt.pojo.Orders">
<id property="orderid" column="orderid"/>
<result property="orderprice" column="orderprice"/>
<collection property="items" ofType="com.bjsxt.pojo.Items">
<id property="itemid" column="itemid"/>
<result property="itemid" column="itemid"/>
<result property="itemname" column="itemname"/>
<result property="itemprice" column="itemprice"/>
</collection>
</collection>
</resultMap>

<!--根据用户 ID 查询用户与订单以及订单中所包含的商品-->
<select id="selectUsersAndOrdersAndItems"
resultMap="usersAndOrdersAndItems">
select * from users as u,orders as o, orders_items as oi ,items as i
 where u.userid = o.user_id and o.orderid = oi.order_id and
oi.item_id = i.itemid and u.userid = #{userid}
</select>

在这里插入图片描述

或者使用修改全局配置文件配置触发方法

延迟加载,调用get方法后才能进行查询加载,此为延迟加载。

标签:体系,缓存,alarm,dispose,sqlSession,content,SqlSession,ORM,MyBatis
来源: https://blog.csdn.net/weixin_43888912/article/details/117997645