学习笔记_MyBatis基础回顾及高级应用

MyBatis的相关概念

1、特点:MyBatis是一个基于ORM半持久轻量级框架;

	a、ORM:对象/关系数据库映射(Object/Relation Mapping),实体类与数据库形成对应关系,操作实体类等于操作数据库表
	b、半自动:手动编写SQL语句,可以优化SQL语句比全自动不可优化的SQL语句执行效率更高
	c、轻量级:启动过程中所需资源的较少

2、使用方式:

a、XML
b、注解

3、历史:iBatis---->MyBatis(Apache---->Gougle)

4、优势:

a、可以优化SQL语句提高执行效率
b、SQL语句与JAVA编码分开,解决了硬编码问题,使其功能边界清晰,一个专注业务,一个专注数据

MyBatis基本应用

1、开发步骤:

a、添加MyBatis的坐标
b、创建数据库表
c、根据数据库表编写实体类
d、编写映射配置文件(SQL、返回值等)
e、编写核心配置文件
f、编写测试类

2、映射配置文件分析

a、<!DOCTYPE......> ----> 映射文件DTD约束头
b、<mapper namespace="userMapeer"></mapper> ----> mapper:跟标签 namespace:命名空间与SQL语句id一起组成唯一标识符 
c、<selsect id="findUser" resultType=”....“>......</selsect>、<insert id="saveUser" parameterType="">......</insert>、<update id="upadeUser" parameterType="">......</update>、<delete id="deleteUser" parameterType="">......</delete> ----> SQL操作标签表示:查询操作、添加操作、修改操作、删除操作
d、以上标签中......略写了需要执行的SQL语句,如:select * from user
e、以上标签中的id与命名空间一起组成SQL语句的唯一标识
f、以上标签中resultType表示查询结果对应的返回实体类
g、以上标签中parameterType表示查询所需参数类型,可以是实体类,也可以是基础数据类型,如查询参数类型是实体类则SQL语句中的参数名应与实体类中属性名一致,如查询参数类型是基本参数类型且只有一个参数则SQL语句中参数名可以随便命名
h、以上标签中SQL语句查询参数标识符有两种#{}和${},#{}所用居多, #{}将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号,#{}能够很大程度防止sql注入,${}将传入的数据直接显示生成在sql中,${}无法防止sql注入,一般用于传入数据库对象,例如表名,一次排序使用order by时应使用${},一般能用#{}就不用${}
i、动态SQL标签
	1)、if标签:根据实体类的不同取值,使用不同的SQL语句进行查询。在多条件组合查询中会用到,不为空拼接,为空不拼接。【where标签:】不用自己控制where 1=1,MyBtais提供where标签会自动拼接
<select id="findByConditional" parameterType="user" resultType="user">
	SELECT * FROM user
	<where>
		<if test="id != null">
			AND id = #{id}
		</if>
		<if test="username != null">
			AND username = #{username}
		</if>
	</where>
</select>
	2)、foreach标签:循环执行sql的拼接操作,多值查询,【collection属性:】表示参数类型,数据传array,集合传list,【opend属性:】表示开始位置,如id in(,【close属性:】表示结束位置,如),【item属性:】表示当前参数名,如id,【separator属性:】表示拼接占位符,如, 
<select id="findByIds" parameterType="list" resultType="user">
	SELECT * FROM user
	<where>
		<foreach collection="array" open="id in(" close=")" item="id" separator=",">
			#{id}
		</foreach>
	</where>
</select>
	3)、SQL语句抽取sql标签-include标签:sql标签抽取重复的SQL语句,include标签使用sql标签,【refid属性】等sql标签id
<sql id="selectUser">
	SELECT * FROM user
</sql>
<select id="findByIds" parameterType="list" resultType="user">
	<include refid="selectUser"></include>
	<where>
		<foreach collection="array" open="id in(" close=")" item="id" separator=",">
			#{id}
		</foreach>
	</where>
</select>

3、核心配置文件分析

a、configuration配置
b、properties属性:将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件(jdbc.properties)【使用EL表达式${}引用文件信息】该标签应该是configuration的第一个子标签
<properties resource="jdbc.properties"></properties>
......
<dataSource type="POOLED">
	<property name="driver" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</dataSource>
c、typeAliases类型别名:为一个JAVA类全限定类名起一个别名,有两种方式,给单个实体类起别名,给多个实体类起别名:
<typeAliases>
	<!-- 给单个实体类起别名 -->
	<!--<typeAlias type="com.lagou.study.pojo.User" alias="user"></typeAlias>-->
	<!-- 通过包给多个实体类起别名,别名就是实体类的类名,且不区分大小写 -->
	<package name="com.lagou.study.pojo"/>
</typeAliases>

映射配置文件内容由:

<select id="findAll" resultType="com.lagou.study.pojo.User">
        SELECT * FROM user
    </select>

变为:

<select id="findAll" resultType="user">
        SELECT * FROM user
    </select>

Mybatis给基本数据类型提供了默认的别名:

别名实体类
stringString
longLong
intInteger
doubleDouble
booleanBoolean

映射配置文件内容由:

<delete id="deleteUser" parameterType="java.lang.Integer">
        DELETE FROM user WHERE id =#{abc}
    </delete>

变为:

<delete id="deleteUser" parameterType="int">
        DELETE FROM user WHERE id =#{abc}
    </delete>
d、typeHandlers类型处理器
e、objectFactory对象工厂
f、plugins插件
g、envionments环境:数据库环境配置,支持多环境配置,default属性表示默认环境id
	1)、environment环境变量:id属性指定当前环境名称
		~transacrionManager事务管理器:type属性,指定事物管理类型【JDBC】
		~dataSource数据源:type属性指定当前数据源类型:
			UNPOOLED:不使用连接池,每次被请求的时候打开和关闭联机
			POOLED:使用连接池
			JNDI:为了能在图EJB或应用服务器这容器中使用,容器可以集中或在外部配置数据源,然后防止一个jNDI上下文引用
h、databaseaprovider数据库厂商标识
i、mappers映射器:加载映射的,有四种加载方式
	1)、使用相对于路径的资源引用<mapper resource="/com/lagpu/userMapper.xml" />
	2)、使用完全限定资源定位符(URL)<mapper url="file:///var/mapper/userMapper" />
	3)、使用映射起借口实现类的完全限定类名<mapper class="com.lagou.mapper.usermapper" />*注解方式可使用,单个加载*
	4)、将包内的映射器接口实现全部注册为映射器<package name="com.lagou.mapper" /> *此处需要注意,接口与映射配置文件需要同包同名* *能同时加载注解模式和配置文件模式*

4、Mybatis响应API介绍

a、Resources工具类:配置文件的加载,把配置文件加载成字节输入流
b、SqlSessionFactoryBuilder:解析配置文件,并创建sqlSessionFactory工厂
c、生产sqlSession:sqlSessionFactory.openSession(),默认开启一个事物,但该事物不会自动提交,在进行增删该操作时,要手动提交事物。sqlSessionFactory.openSession(true)使用构造方法可以自动提交
d、sqlSession调用方法:查询所有selectList、查询单个selectOne、添加insert、修改update、删除delete

5、Mybatis的Dao层实现

a、传统开发方式:编写Dao接口、编写DaoImpl实现类
b、代理开发方式:仅编写Dao接口,使用JDK动态代产生代理对象,由代理对象执行原实现类操作,但需要以下规范:
	1)、mapper.xml文件中的namespace与mapper接口的全限定名相同
	2)、mapper接口方法名和mapper.xml中定义的每个statement的id相同
	3)、mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
	4)、mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

MyBatis复杂映射开发

1、一对一(使用用户表和订单表作为示例)
用户表和订单表的的关系为,一个用户由多个订单,一个订单只属于一个用户
需求:查询一个订单,与此同时查询出订单所属用户信息
在这里插入图片描述
【resultMap标签】:手动配置实体属性与表字段的映射关系,【id属性】:rusultMap标识,【type属性】:要封装到的实体类型:

	a、【result标签】:实体类与数据库表字段对应关系----【property标签】:实体类属性----【column标签】:数据表字段名
	b、【association标签】:实体类中的实体类属性的配置----【property标签】:实体类属性----【javaType标签】:实体类型:
		1)、【resilt标签】::实体类中的实体类属性与数据库表字段对应关系----【property标签】:实体类属性----【column标签】:数据表字段名
<resultMap id="orderMap" type="com.lagou.study.pojo.Order">
	<result property="id" column="id"></result>
	<result property="orderTime" column="orderTime"></result>
	<result property="total" column="total"></result>
	<association property="user" javaType="com.lagou.study.pojo.User">
		<result property="id" column="uid"></result>
		<result property="username" column="username"></result>
	</association>
</resultMap>
<select id="findOrderAndUser" resultMap="orderMap">
        SELECT * FROM orders o, user u WHERE o.uid = u.id
</select>

2、一对多(使用用户表和订单表作为示例)
用户表和订单表的的关系为,一个用户由多个订单,一个订单只属于一个用户
需求:查询所有用户,与此同时查询出每个用户具有的订单信息
【collection标签】:配置实体类中的集合属性的标签----【propert属性】:实体类中的集合属性名称----【ofType属性】:对应集合的泛型的全路径

<resultMap id="userMap" type="com.lagou.study.pojo.User">
	<id property="id" column="id"></id>
	<result property="username" column="username"></result>
	<collection property="orderList" ofType="com.lagou.study.pojo.Order">
		<id property="id" column="oid"></id>
		<result property="orderTime" column="ordertime"></result>
		<result property="total" column="total"></result>
	</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
	SELECT u.*, o.id as oid, o.ordertime, o.total FROM user u LEFT JOIN orders o ON o.uid = u.id
</select>

3、多对多(使用用户表和角色表作为示例)
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
需求:查询用户同时查询出该用户的所有角色
在这里插入图片描述

<resultMap id="userRoleMap" type="com.lagou.study.pojo.User">
	<id property="id" column="id"></id>
	<result property="username" column="username"></result>
	<collection property="roleList" ofType="com.lagou.study.pojo.Role">
		<result property="id" column="id"></result>
		<result property="rolename" column="rolename"></result>
		<result property="roleDesc" column="roleDesc"></result>
	</collection>
</resultMap>
<select id="findUserAndRole" resultMap="userRoleMap">
	SELECT * FROM user u LEFT JOIN sys_user_role ur ON ur.userid = u.id
	LEFT JOIN sys_role r ON r.id = ur.roleid
</select>

MyBatis注解开发

1、Mybatis常用注解

@Inster:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以约@Result一起使用,封装多个结果集
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
***【@beFore注解】:在被@Before注解的方法会在@Tset之前被执行***

单表增删改查:

@Insert("INSERT INTO user (id, username) VALUES (#{id}, #{username})")
public void addUser(User user);

@Update("UPDATE user SET username = #{username} where id = #{id}")
public void updateUser(User user);

@Select("SELECT * FROM user")
public List<User> selsetUser();

@Delete("DELETE FROM user WHERE id = #{id}")
public void deleteUser(Integer id);

@Results注解、@Result注解、@One注解、@Many注解组合完成复杂关系的配置

注解说明
@Results代替标签resultMap盖住姐中可以使用单个@Results直接,也可以使用@Result集合。使用格式:@Results({@Result{}, @Result{}})或@Results(@Result())
@Result代替了id标签和result标签@Result中属性介绍:column:数据库列名prepert:需要装配的属性名one:需要使用的@One注解(@Result(one=@One))many:需要使用的@many注解(@Result(many=@Many))
@One代替assocition 标签
@Many代替collection标签

一对一查询:

@Results({
            @Result(property = "id", column = "id"),
            @Result(property = "orderTime", column = "orderTime"),
            @Result(property = "total", column = "total"),
            @Result(property = "user", column = "uid", javaType = User.class, one = @One(select = "com.lagou.study.mapper.IUserMapper.findUserById"))
    })
    @Select("SELECT * FROM orders")
    public List<Order> findOrderAndUser();


@Select("SELECT * FROM user WHERE id = #{id}")
    public User findUserById(Integer id);

上述代码执行顺序:

a、执行SELECT * FROM orders 语句
b、将执行结果放入Result中
c、根据@One配置地址执行子查询语句,其中该列对应的cloumn就是子查询的where条件

一对多查询

@Select("SELECT * FROM user")
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "roleList", column = "id", javaType = List.class, many = @Many(select = "com.lagou.study.mapper.IOrderMapper.findOrderBuUid")),
    })
    public List<User> findAll();


@Select("SELECT * FROM orders WHERE uid = #{uid}")
    public List<Order> findOrderBuUid(Integer uid);

多对多查询

@Select("SELECT * FROM user")
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "roleList", column = "id", javaType = List.class, many = @Many(select = "com.lagou.study.mapper.IRoleMapper.findRoleByUserId"))
    })
    public List<User> findUserAndRole();


@Select("SELECT * FROM sys_role r, sys_user_role ur WHERE ur.roleid = r.id AND ur.userid = #{userId}")
    public List<Role> findRoleByUserId(Integer userId);

这里需要对sql语句做好拆解

MyBatis缓存

概念:缓存就是内存中的数据,常常来自对数据库查询结果的报讯,使用缓存,可以避免频繁的与数据库进行交互,进而提高响应速度。内存的读写是很快的

MyBatis提供了对缓存的支持,分为一级缓存和二级缓存,二级缓存要比一级缓存大:
1、一级缓存:是SqlSession级别的缓存,在操作数据库是需要构造sqlSsession对象,在对象中有一个数据结构(HashMap)用户存储缓存数据,不同的sqlSession之间的缓存数据区域(HashMap)是互不影响的:
默认开启状态:在一个SqlSession中,对表根据id进行两次查询,查看发出sql情况。
进行SQL查询时,首先到一级缓存中查询是否有对应的数据,有:直接返回,没有:查询数据库,同时将查询出的结果存到一级缓存中,cachaeKey:statementId,params(当前参数),bountSql(封装需要执行的SQL语句),rowBounds(分页对象),cacheValue:结果对象
两次查询中间做增删改操作,并进行了事物提交,会刷新一级缓存,第二次查询会重新查询数据库,也可使用clearCache手动刷新缓存,目的在于让缓存中存储的是最新信息,避免脏数据

执行测试代码

public void firstLevelCache(){
        // 第一次查询USER
        User user1 = userMapper.findUserById(1);

        // 更新数据
        User user = new User();
        user.setId(1);
        user.setUsername("lilei");
        userMapper.updateUser(user);
        sqlSession.commit();//提交事物


        // 第二次查询USER
        User user2 = userMapper.findUserById(1);

        //sqlSession.clearCache();//手动刷新缓存

        // 第三次查询USER
        User user3 = userMapper.findUserById(1);

        System.out.println(user1 == user2);
        Sys
}

执行结果

10:45:59,229 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@782859e]
10:45:59,234 DEBUG findUserById:159 - ==>  Preparing: SELECT * FROM user WHERE id = ? 
10:45:59,327 DEBUG findUserById:159 - ==> Parameters: 1(Integer)
10:45:59,380 DEBUG findUserById:159 - <==      Total: 1
10:45:59,383 DEBUG updateUser:159 - ==>  Preparing: UPDATE user SET username = ? where id = ? 
10:45:59,385 DEBUG updateUser:159 - ==> Parameters: lilei(String), 1(Integer)
10:45:59,387 DEBUG updateUser:159 - <==    Updates: 1
10:45:59,389 DEBUG JdbcTransaction:70 - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@782859e]
10:45:59,510 DEBUG findUserById:159 - ==>  Preparing: SELECT * FROM user WHERE id = ? 
10:45:59,511 DEBUG findUserById:159 - ==> Parameters: 1(Integer)
10:45:59,514 DEBUG findUserById:159 - <==      Total: 1
false
true

2、二级缓存:是mapper(namespace)级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的
手动开启配置:配置文件中进行配置,基于配置文件形式的sql在mapper未见中中添加cache标签,基于注解形式的sql在接口文件中添加注解@CacheNamespace
二级缓存缓存的不是对象,是对象中的数据。二级缓存的方式是在第二次查询的时候底层重新创建类User对象将缓存的数据放入对象中,由于二级换的戒指多样,不一定只存在缓存中,也有可能存在硬盘中,因此再取缓存的话,需要反序列化,所以MyBatis中的pojo都要实现Serializablc接口
useCache:用来设置是否禁用二级缓存的。基于配置文件形式的SQL,在statement中配置useCache=false可以禁用当前select语句的二级缓存,默认情况是true,基于注解形式的SQL,在对应的方法上添加@Options(useCache=fasle)同样其默认值为true
flushCache:刷新缓存,默认情况下为true

执行测试代码:

public void secondLevelCache(){
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();

        IUserMapper mapper1 = sqlSession1.getMapper(IUserMapper.class);
        IUserMapper mapper2 = sqlSession2.getMapper(IUserMapper.class);
        IUserMapper mapper3 = sqlSession3.getMapper(IUserMapper.class);

        //第一次请求查询
        User user1 = mapper1.findUserById(1);
        //清空一级缓存,以便查看二级缓存是否生效
        sqlSession1.close();
        //第二次请求查询
        User user2 = mapper2.findUserById(1);

        System.out.println(user1 == user2);
}

执行结果:

10:40:02,774 DEBUG IUserMapper:62 - Cache Hit Ratio [com.lagou.study.mapper.IUserMapper]: 0.0
10:40:02,793 DEBUG JdbcTransaction:137 - Opening JDBC Connection
10:40:03,274 DEBUG PooledDataSource:406 - Created connection 379303133.
10:40:03,275 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@169bb4dd]
10:40:03,279 DEBUG findUserById:159 - ==>  Preparing: SELECT * FROM user WHERE id = ? 
10:40:03,373 DEBUG findUserById:159 - ==> Parameters: 1(Integer)
10:40:03,438 DEBUG findUserById:159 - <==      Total: 1
10:40:03,452 DEBUG JdbcTransaction:123 - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@169bb4dd]
10:40:03,453 DEBUG JdbcTransaction:91 - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@169bb4dd]
10:40:03,454 DEBUG PooledDataSource:363 - Returned connection 379303133 to pool.
10:40:03,480 DEBUG IUserMapper:62 - Cache Hit Ratio [com.lagou.study.mapper.IUserMapper]: 0.5
false

MyBatis自带的二级缓存的底层依然是HashMap
MyBatis再带的二级缓存,无法实现分布式缓存,如想解决该问题,则需找一个分布式的缓存,专门用来缓存数据,这样不同服务器要的缓存数据都往这里存,常用的分布式缓存框架有:redis、memcached、ehcache等等

3、二级缓存整合redis
MyBatis提供了一个针对cache接口的redis实现类,该类存在mybatid.redis包中,源码分析:
无论是MyBatis自带的缓存实现类还是Redis的缓存实现类或者自写的,其实现的都需要实现Cache这个接口,RedisCache给了redis默认配置信息,可以通过配置reids.proprtties对象修改默认配置信息,这个文件的文件名和数据名是固定的:

redis.host=localhost
redis.port=6379
redis.connectionTimeout=5000
redis.password= 
redis.database=0
			a、如何完成Redis缓存存取:putObject()、getObject()使用的是Jsdis的hset和hget,存值:Hash的key值、项的key值,数据信息,取值:Hash的key值、项的key值
			b、用的Redis哪种存储结构:使用了redis的hset方法,因此底层数据结构是Hash

MyBatis插件

1、一般情况下,开源框架都会i提供插件或其他形式的拓展点,供开发者自行扩展,好处:
	a、增加框架灵活性
	b、开发者可以结合是继续求对框架进行拓展,使其能够更好的工作
MyBatis插件机智可以实现f嗯也、分表,监控等功能,由于插件和业务无关,业务也无法感知插件,因此可以无感植入插件,在无形中增强功能

2、MyBatis在四大组件处分别提供了简单易用的插件扩展机制(Executor、StatementHandler、ParameterHandler、ResultSetHandler):
	a、Executor:执行器,负责增删改查的行为
	b、StatementHandler:SQL语法构建器,完成SQL的自编译
	c、ParameterHandler:参数处理器,处理参数
	d、ResultSetHandler:结果集处理器,处理返回结果集
所谓的MyBatis的插件就是对这四大核心对象进行拦截,就是其拦截器,用来增强核心对象的功能,增强功能本质上是借助于底层动态代理实现的,或者说MyBatis的四大对象都是代理对象
MyBatis所允许拦截的方法如下:
	a、Executor: update、query、commit、rollback等
	b、StatementHandler:prepare、parametrize、batch、update、query等
	c、ParameterHandler:getParameterObject、handleOutParameters等
	d、ResultSetHandler:handleResultSets、handleOutParameters等

3、MyBatis插件原理:
	在四大对象插件的时候,每个创建出来的对象不是直接返回的,而是通过interceplorChain.pluginAll(paeameterHandler)的方法对创建的对象进行处理
	获取到所有的interceptor{拦截器}{产检需要实现的接口}:使用Interceptor.plugin(target)返回target包装后的对象,本质是调用jdk动态代理为当前对象产生一个代理对象

4、第三方插件
	a、pageHelper分页插件:将分页的复杂操作进行封装,使用简单的方法即可获得分页的相关数据 
		导入通用的PageHelper坐标
		在mybatis核心配置文件中配置PageHelper插件
	b、通用mapper插件:解决单表增删改查重复书写的问题(多个表的单表的增删改查非常相似),开发人员不需要编写sql,不需要在dao中增加方法,只要写好实体类,鞥支持响应的增删改查方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值