javaEE-----Mybatis框架

框架:是软件开发中一套解决方案,不同的框架解决不同的问题。框架其实就是某种应用的半成品。

软件开发的三层框架:
1.表现层:用于数据展示
2.业务层:处理业务需求
3.持久层:与数据库进行交互

持久层技术解决方案:JDBC技术(是一种规范)、Spring的JDBCTemplate、Apache的DBUtils(是工具类)、Mybatis(框架)。

Mybatis简介

  • Mybatis框架几乎消除了所有JDBC代码,使开发者只需要关注sql语句本身,无需关注注册驱动,创建连接等繁杂的过程。并且Mybatis也基本不需要手工设置参数和获取检索结果。MyBatis能够使用简单的XML文件格式或注解进行配置。

  • Mybatis使用ORM思想,实现了结果集的封装:把数据库表和实体类及实体类的属性对应起来,让我们可以操作实体类就实现操作数据库表。

  • Mybatis对于数据库的连接采用托管的方式,对于缓存采用两级缓存,对于结果映射采用自动映射的方式,程序的可维护性高。而JDBC对于数据库的连接是采用编码的方式,而且不支持缓存,对于结果映射采用硬编码,程序的可维护性低

  • 每个基于MyBatis的应用都是以一个SqlSessionFactory的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从XML配置文件或一个预先定制的 Configuration的实例构建出 SqlSessionFactory 的实例

Mybatis入门

前期工作:

  • 创建maven
    1.在maven项目中的xml文件中添加Mybatis的jar包依赖
    2.添加JDBC依赖
    3.连接数据库

  • 创建MyBatis配置
    1.在项目中的resource文件夹中添加Mybatis的配置文件(xml)
    2.配置文件中需要配置数据库的信息, driver url username password

Mybatis的配置文件mybatis-config.xml

<?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">
<!--mybatis的主配置文件-->
<configuration>
    <!--配置环境-->
    <environments default="dev">
        <!--mysql环境-->
        <environment id="dev">
            <!--配置事物类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源(连接池)-->
            <dataSource type="POOLED">
                <!--配置数据库的四个基本信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/memo"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!--指定映射配置文件的位置:每个dao独立的配置文件-->
    <mappers>
        <mapper resource="com/github/sweeeeeet/dao/IMemoDao.xml"/>
    </mappers>
</configuration>
  • 编码:
  1. entity包 实体类–>数据库表(实体类中的属性,与表的列名相同,否则就需要在映射配置文件中,对sql语句起别名,或者用resultMap将查询结果的列名于实体类的属性名建立映射)
  2. mapper包 mapper接口 -->数据库操作方法
  3. src/main/resource/mapper 创建mapper.xml定义信息
  4. 编码,创建SqlSessionFactory接口的实现类SqlSessionFactoryBuilder的对象 (从XML配置文件或一个预先定制的 Configuration的实例构建出 SqlSessionFactory 的实例)
  5. 测试代码

环境搭建注意事项:
在mybatis中把持久层的操作接口名称和映射文件也叫做dao
在idea中创建resource目录时,他和java包是不一样的,必须分开创建三次才是三级目录结构
mybatis的映射配置文件的位置必须和mapper(dao)接口的包结构相同
映射配置文件的mapper标签的namespace属性的取值必须是dao(mapper)接口的全限定名
映射文件的操作配置select,id属性必须是dao(mapper)接口中的方法名
遵从了这些规定后,我们在开发中就无需再写dao的实现类

mapper.xml配置:

<?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="com.github.sweeeeeet.dao.IMemoDao">
    <!--配置Dao中的方法(用id标识)查询所有,查询后的结果放在哪里用resultType属性标识:-->
        <!--能够执行sql语句,可以获取PreparedStament-->
    <select id="findAll" resultType="com.github.sweeeeeet.entity.MemoGroup">
        select * from memo_group;
    </select>
 <!--模糊查询操作-->
    <select id="findByName" parameterType="String" resultType="com.github.sweeeeeet.entity.MemoGroup">
    <!--#{id}的方式是使用的占位符的方式(推荐),用到了preparedStament-->
        <!--SELECT * FROM memo_group WHERE name like #{id};-->
          <!--%${value}%的方式是使用的字符串拼接的方式-->
        SELECT * FROM memo_group WHERE name like '%${value}%';
    </select>

</mapper>

读取配置文件利用了dom4j技术解析xml:
selectList方法:
1.根据配置文件的信息创建Connection对象,注册驱动,获取连接。
2.获取预处理对象PreparedStament,此时需要sql语句
3.执行查询ResultSet set=preparedStatement.excuteQuery();
4.遍历结果集用于封装,通过反射获得实体类对象,把结果封装进实体类:由于实体类的属性和表中的列名是一致的,于是我们就可以把表的列名看成实体类的属性名称,就可以使用反射根据名称获取每个属性,并把值都赋进去。
5.返回list

要想让dao中的方法执行,需要提供两个信息:数据库连接信息+映射信息(mapper)。 映射信息包含执行的sql语句和封装结果的实体类的全限定名。
如果有多个dao方法,那么mybatis就会使用map,以key-value的方式存储dao方法和与方法对应的映射,这样多个dao方法就不会混淆。

或是利用java编码构建

try {        
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()           
 .build(            
 //通过classLoader传入文件路径,实现一次编写到处运行的理念
 Resources.getResourceAsReader("mybatis-config.xml")        
 );        
 System.out.println(sqlSessionFactory);    
 } catch (IOException e) {        
 e.printStackTrace();    
 } 
 DataSource dataSource =  new PooledDataSource();//mybatis提供的数据库连接池 
 TransactionFactory transactionFactory = new JdbcTransactionFactory(); 
 Environment environment = new Environment("development", transactionFactory, dataSource); 
 Configuration configuration = new Configuration(environment); 
 configuration.addMapper(MemoGroupMapper.class); 
 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
 

在test中利用main方法测试是否配置成功:

package com.github.sweeeeeet;


import com.github.sweeeeeet.dao.IMemoDao;
import com.github.sweeeeeet.entity.MemoGroup;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * Author:sweet
 * Created:2019/6/30
 */
public class MybatisTest {
    /*
    * 使用main方法进行测试
    * */
    public static void main(String[] args) throws IOException {
      
        //1.读取配置文件(使用类加载器的方式)
        			//使用ServletContext对象的getRealPath()
       InputStream in= Resources.getResourceAsStream("mybatis-config.xml");
      
        //2.创建SqlSeonFactory工厂
        		//利用第三方SqlSessionFactoryBuilder来构建工厂,只需要付钱builder.build(in);即可
        		//构建者模式能把对象创建的细节隐藏,使用者直接调用方法即可调用对象
        SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
        SqlSessionFactory factory= builder.build(in);
     
        //3.使用工厂生产SqlSession对象
        			//降低类之间的依赖关系,进行解耦,不用在每次构建的时候都new对象
        SqlSession session=factory.openSession();
       
        //4.使用SqlSession创建DAO接口的代理对象
        		//创建dao接口实现类使用了代理模式,就能够在不修改源码的基础上对已有的方法增强
        IMemoDao memoDao=session.getMapper(IMemoDao.class);
     
     
        //5.使用代理对象的执行方法
        List<MemoGroup> memoGroups=memoDao.findAll();
        for(MemoGroup m:memoGroups){
            System.out.println(m);
        }
        //6.释放资源
        session.close();
        in.close();
    }
}

使用SqlSession创建DAO接口的代理对象:
IMemoDao memoDao=session.getMapper(IMemoDao.class);
getMapper内部会实现一个类加载器,他使用和被代理对象实现相同的接口
代理的过程:他是一个增强方法,我们需要自己提供一个接口的实现类,在实现类中调用selectList()方法

用annotation注解的方式配置dao映射

1.删除resource目录下的映射配置结构
2.在dao包中对dao接口添加注解

public interface MemoGroupMapper {
    @Select("insert into")
    int insertMemoGroup(MemoGroup memoGroup);
}

3.更改mybatis配置文件中的mapper映射:使用class属性指定dao接口的全限定名

 <!--指定映射配置文件的位置:每个dao独立的配置文件-->
    <mappers>
        <mapper class="com.github.sweeeeeet.mapper.MemoGroupMapper"/>
    </mappers>

不论是xml方式配置映射还是注解方式配置映射,都不需要程序员自己写实现类,mybatis内部会自动创建dao接口的实现类,然后调用实现类中的方法进行dao操作。

Mybatis的配置

1.properties
配置properties可以在标签内部property中配置连接数据库的信息,也可以通过属性引用外部外部配置文件信息。

  • resource属性用于指定配置文件的位置,必须存在与类路径下
  • url属性:要求按照url的写法书写:由协议、主机、端口、uri组成
    • uri:统一资源标示符,在应用中可以唯一的定位一个资源

1.在 properties 元素体内指定的属性⾸先被读取。
2.然后根据 properties 元素中的 resource 属性读取类路径下属性⽂件或根据 url 属性指定的路径读取属性⽂件,并覆盖已读取的同名属性。
3.最后读取作为⽅法参数传递的属性,并覆盖已读取的同名属性。
因此,通过⽅法参数传递的属性具有最⾼优先级,resource/url 属性中指定的配置⽂件次之,最低优先级的是 properties 属性中指定的属性。

2.typeAliases
typeAliase用于配置别名

  • type属性用于指定实体类的权限定名
  • alias属性指定别名,当指定了别名就不再区分大小写

package用于指定要配置的包

  • 当指定之后,该包下的实体类就会注册别名,并且类名就是别名,不再区分大小写

Mybatis的编码映射

1. Select 命令
#{id}进行字符串匹配的方式是使用的占位符的方式(推荐),用到了preparedStament
%${value}%的方式是使用的字符串拼接的方式

  <!--模糊查询操作-->
    <select id="findByName" parameterType="String" resultType="com.github.sweeeeeet.entity.MemoGroup">
    
        <!--SELECT * FROM memo_group WHERE name like #{id};-->
         
        SELECT * FROM memo_group WHERE name like '%${value}%';
    </select>

2. insert, update和delete

	<!--增-->
    <insert id="save" parameterType="com.github.sweeeeeet.entity.MemoGroup">
        insert into memo_group(id,name,created_time,modify_time)values(#{id},#{name},#{create_time},#{modify_time});
    </insert>

    <!--删-->
    <delete id="delete" parameterType="Integer">
        delete from  memo_group where id=#{id};
    </delete>
   

    <!--改-->
     <update id="update" parameterType="com.github.sweeeeeet.entity.MemoGroup">
       update memo_group  set name=#{name},created_time=#{create_time} where id=#{id};
	 </update>

对于需要新增用户ID的返回值的情况:

<!--配置插入数据后,需要获得插入数据id-->
    <insert id="save" parameterType="com.github.sweeeeeet.entity.MemoGroup">
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
    SELECT last_insert_id();
</selectKey>
        insert into memo_group(id,name,created_time,modify_time)values(#{id},#{name},#{create_time},#{modify_time});
    </insert>

Insert,Update,Delete的配置属性信息:
在这里插入图片描述

3. resultMap
resultMap配置查询结果和列名的对应关系:

  • id是这个对应关系的唯一标识,
  • type是对应的实体类

id标签对应主键与实体类的对应
resultMap标签是非主键字段的对应

 <!--配置查询结果和列名的对应关系:id是这个对应关系的唯一标识,type是对应的实体类-->
    <resultMap id="userMap" type="com.github.sweeeeeet.entity.MemoGroup">
        <!--主键的对应:property是实体类中的属性,column是表中的字段-->
        <id property="userid" column="id"></id>
        <!--非主键的对应-->
        <result property="username" column="name"></result>
    </resultMap>

用了resultMap结果映射的mapper,在配置dao接口方法的映射时,需使用resultMap=id来指定返回结果

    <select id="count" resultMap="userMap">
        SELECT count(*) from memo_group ;
    </select>

4.package
用于指定dao接口所在的包,当指定后就不需要再写mapper以及resource或class了

Mybatis连接池

连接池:在实际开发中,连接池可以减少我们获取连接所消耗的时间。连接池就是用于存储连接的一个容器,容器就是一个集合对象,该集合必须是具有队列的特性:先进先出,且是线程安全的,不能两个线程拿到同一个连接。
mybatis 提供的三种方式的配置:
配置的位置:主配置文件SqlMapConfig.xml中的dataSource标签

连接池的分类

type属性表示采用何种连接池方式。type属性的取值:

  • POOLED:采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现:是从池中获取一个连接来用

内部分为空闲连接池和活动池,如果空闲连接池还有连接的话,直接拿出一个来用。如果空闲连接池中的连接已经没有了,就会判断活动连接池中是否已经达到了最大数量,如果已经到达最大数量,就会接着判断活动池中哪个是最先进来的,将它返回回去。

  • UNPOOLED:采用传统的获取连接的方式,虽然也实现javax.sql.DataSource接口,但是并没有使用池的思想:每次创建一个新连接使用
  • JNDI:采用服务器提供的JNDI技术实现,获取DataSource对象,不同的服务器所能拿到的DataSource是不一样的。注:如果不是web或maven的war工程。则是不能使用的。在tomcat服务器中,采用的连接池是dbcp连接池。

Mybatis中的事务

Mybatis中的事务与JDBC中的事务提交过程一致,都需要创建事务,最后进行commit提交。

//使用工厂生产SqlSession对象,并自动提交事务
            session = factory.openSession(true);

(面试中常遇到的关于事物的问题)

  • 事物的四大特性:ACID
    A:原子性Atomicity:表示事务是不可分割的最小单位,原子性保证事务要么全起作用,要么全不起作用
    C:一致性consistency:执行事务前后数据保持一致,多个事务读到同一个数据内容是一致的
    I:隔离性Isolation:进行并发访问数据库时,一个事务不会被其他事务所干扰,与其他事务是隔离的。
    D:持续性Durability:事务对数据库的改变是持久的。

什么是事物?
事务是逻辑上的一组操作,要么全执行,要么全不执行。
不考虑隔离性会产生的三个问题?
脏读:当一个事务正在修改数据库,还没有将数据提交到数据库,另一个事务就对数据库进行了访问,那么另一个事务拿到的数据就称为脏数据。进行操作得到的结果也是不正确的。
幻读:当一个事务进行插入删除操作时,在另一个事务内看到数据个数不一样的情况。例如一个事务读取了几行数据,另一个事务就进行了插入删除操作,那么第一个事务就会发现多了或少了一些原本不存在的数据,就像发生了幻觉一样。
不可重复读:由于事务的修改,另一个事务两次读到的数据是不同的。
解决办法:四种隔离级别
读未提交,读已提交,可重复读,可串行化。

Mybatis动态SQL语句

dao映射配置文件中的标签
1.if
对于一些查询的sql语句,对于查询的子条件并不确定是否存在,因此可以采用动态SQL语句根据传入的参数条件查询。

<if test="逻辑条件">
条件表达式;
</if>
where is_protected = '1'
<if test="title!=null">
 and title like #{title}
</if>

2.where标签
可以对if标签的条件进行判断,当有一个以上的if标签逻辑表达式为true,才去插入where子句。⽽且,若最后的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除。

<select id="queryMemoInfoWithPrivacyAndLikeTitle" resultMap="memoInfoMap">
 select *  from memo_info
<where> 
<if test="title!=null">
 and title like #{title}
</if>
</where>
</select>

3.set元素
set 元素可以被⽤于动态包含需要更新的列,⽽舍去其他的逗号等内容。⽐如:

<update id="updateMemoInfo">
 update memo_info
<set><if test="title!=null">
 title = #{title},
</if> <if test="content!=null">
 content = #{content},
</if>
</set>
 where id = #{id}
</update>

4.choose元素
有些时候,我们不想⽤到所有的条件语句,⽽只想从中择其⼀⼆。mybatis提供了choose元素,类似于Java中的switch语句,在mybatis中利用choose、when、otherwise的结构来表达

<select id="queryMemoInfoWithPrivacyAndLikeTitleOrBackground"
resultMap="memoInfoMap">
 select * from memo_info
 where is_protected = '1'
 <choose>
 <when test="title!=null">
 and title like #{title}
 </when>
 <when test="background!=null">
 and background = #{background}
 </when>
 <otherwise>
 and background = 'WHITE'
 </otherwise>
 </choose>
</select>

5.foreach
当构建in条件语句时,需要遍历集合中的元素,需要用到foreach.

!-- 映射接⼝ -->
List<MemoInfo> queryMemoInfoWithIdsList(List<Integer> ids);
<!-- 命令配置 -->
<select id="queryMemoInfoWithIdsList" parameterType="list"
resultMap="memoInfoMap">
 SELECT *  FROM memo_info
 WHERE id in
 <foreach item="item" index="index" collection="list" open="("
separator="," close=")">
 #{item}
 </foreach>
 </select>

foreach 元素的功能是⾮常强⼤的,它允许你指定⼀个集合,声明可以⽤在元素体内的集合项和索引变量。它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符。
foreach 可以将任何可迭代对象(如列表、集合等)和任何的字典或者数组对象传递给foreach作为集合参数。
当使⽤可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。
当使⽤字典(或者Map.Entry对象的集合)时,index是键,item是值。

6.抽取重复的sql语句–sql标签

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

表之间的关系

1.一对多
2.多对一
3.一对一
4.多对多

Mybatis中的多表操作(重要)

用户和账户
一个用户可以有多个账户,一个账户只能属于一个用户(多个账户也可以属于同一个用户)。
建立多表查询:

  1. 建立两张表:用户类、账户类
  2. 建立两个实体类:用户实体类和账户实体类
  3. 建立两个配置文件:用户的配置文件和账户的配置文件
  4. 实现配置:当我们查询用户时,可以同时得到用户下所包含的账户信息,当我们查询账户的时候,可以同时得到账户的所属的用户信息。

Mybatis中的延迟加载

在真正使用数据时才发起查询,不用的时候不查询
立即加载:不管用不用,只要一调用方法就发起查询。
在对应的四种关系中:
一对多,多对多:通常情况下采用延迟加载
多对一,一对一:通常情况下采用立即加载

缓存:存在于内存中临时数据,使用缓存可以减少和数据库的交互次数,提高执行缓存效率。

适用于缓存的场景:

  1. 经常查询且不常改变的数据。
  2. 数据的正确与否对最终的结果影响不大

不适用于缓存的场景:商品的库存、银行的汇率、股市的涨价

一级缓存:

Mybatis中SqlSession对象的缓存,当我们执行查询后,查询的结果会同时存入到SqlSession为我们提供一块区域中,该区域的结果是一个Map.当我们再次查询同样的数据,mybatis会先去sqlSession中查询是否有,有的话直接拿来用。当sqlSession对象消失时,mybatis的一级缓存也就消失了。当调用sqlSession的修稿commit(),close()等方法时,就会清空一级缓存。

二级缓存

Mybatis中SqlSessionFactory的对象缓存,由同一个SqlSessionFactory对象创建的SqlSessionFactory共享其缓存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值