mybatis框架知识梳理

mybatis框架知识梳理

Mybatis框架概述:

​ mybatis 通过 简单的xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象(POJO类)和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。

为什么使用Mybatis:

传统jdbc访问数据库:

​ 1.未经封装,使用jdbc原始方法(加载驱动,获取连接)进行对数据库数据的crud操作

​ 2.数据库连接频繁创建、释放,造成系统资源的浪费,影响系统性能,

 解决:可使用数据库连接池技术

​ 3.Sql语句在代码中属于硬编码,而实际应用sql语句经常变化,需改变java代码,不利于代码维护

 解决:将 Sql 语句配置在 XXXXmapper.xml 文件中与 java 代码分离。

​ 4.使用prepareStatement向占位符传参存在硬编码,因为参数不定,条件可多可少,需修改代码不易维护。

解决:Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的类型。 

​ 5.结果集解析存在硬编码(查询列名),若sql变化(参数),导致解析代码变化,系统不易维护,可将数据库记录封装为POJO类对象。

解决:Mybatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的类型 

Mybatis入门准备:

环境搭建:

1.创建maven工程,导入依赖坐标。(mybatis,mysql,log4j,junit)
2.编写实体类Usr
3.编写持久层接口UserDao
4.获取log4j.properties到resource目录下
5.编写持久层接口的映射文件UserDao.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">

<!--namespace属性为dao接口的全限定类名-->
<mapper namespace="com.itheima.dao.IUserDao">
  
<!--配置查询所有-->
<select id="findAll" resultType="com.itheima.domain.User">
    select * from user
</select>
  
</mapper>
6.编写SqlMapConfig.xml(Mybatis运行环境的配置信息,dom4j解析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="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息,创建连接对象 -->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
      <!-- 基于xml -->
        <mapper resource="com/itheima/dao/IUserDao.xml"/>
      <!-- 基于注解 -->
      <mapper class="com.itheima.dao.IUserDao.xml"/>
    </mappers>
</configuration>
7.编写测试类
public class MybatisTest {

    /**
     * 入门案例
     * @param args
     */
    public static void main(String[] args)throws Exception {
        //1.读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂(不是自己创建的,构建者模式)
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂生产SqlSession对象
        SqlSession session = factory.openSession();
        //4.使用SqlSession创建Dao接口的代理对象
        IUserDao userDao = session.getMapper(IUserDao.class);
        //5.使用代理对象执行方法
        List<User> users = userDao.findAll();
        for(User user : users){
            System.out.println(user);
        }
        //6.释放资源
        session.close();
        in.close();
    }
}

tips:

1.读取配置文件

第一个:使用类加载器,只能读取类路径的配置文件

第二个:使用servletContext对象的getRealPath()

2.创建工厂mybatis:构建者模式

优势:把对象的创建细节隐藏,使使用者直接调用方法即可拿到对象

3.生产SqlSession:工厂模式

优势:解耦,降低类之间的依赖。使用工厂生产对象,解决类之间的依赖,web开发每次修改改动源码,影响开发效率,使用工厂生产对象,不需要重新编译,部署,启动服务器。

4.创建Dao接口实现类:代理模式

优势:在不修改源码基础上对已有方法进行增强

增强对象的功能:

设计模式:一些通用的解决固定问题的方式

1)装饰模式

2)代理模式

静态代理:有一个类文件描述代理模式

动态代理:在内存中形成代理类

5.prepareStament用法

tips:使用jdbc连接数据库执行sql语句时(执行许多SQL语句的JDBC程序产生大量的Statement和PreparedStatement对象。)

1.当sql语句含有多个参数,多次使用时,使用preparestament将sql进行初始化,提到数据库中进行预编译,提高效率;

2.可以替换变量,sql语句可以包含占位符?,把?替换成变量,ps.setString()....

3.安全性,有效防止SQL注入的问题:传递给PreparedStatement对象的参数可以被强制进行类型转换,使开发人员可以确保在插入或查询数据时与底层的数据库格式匹配。(不太懂)

总结:

​ PreparedStatement: 数据库会对sql语句进行预编译,下次执行相同的sql语句时,数据库端不会再进行预编译了,而直接用数据库的缓冲区,提高数据访问的效率(但尽量采用使用?号的方式传递参数),如果sql语句只执行一次,以后不再复用。 从安全性上来看,PreparedStatement是通过?来传递参数的,避免了拼sql而出现sql注入的问题,所以安全性较好。

在开发中,推荐使用 PreparedStatement

6.什么是SQL注入,怎么防止SQL注入?

 所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。

怎么防止SQL注入,使用存储过程来执行所有的查询;检查用户输入的合法性;将用户的登录名、密码等数据加密保存。

映射配置文件

crud标签:

1.sql语句使用#{}字符

相当于占位符,里面内容为基本类型时随意写,若参数为对象,则#{}里使用ognl(对象图导航语言)表达式,语法格式为#{对象.对象}的方式

{user.username}它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用getUsername()方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略 user.而直接写 username。

2.Mysql中获取id

添加记录,使用insert标签。只有参数,没有返回值。
如果Mysql数据想获取当前记录的ID,需要配置:
selectKey
当运行了添加的方法后,mybatis会自动将返回的ID,设置给参数对象parameterType配置的对象

<insert id="saveUser" parameterType="com.itheima.domain.User">
    <!-- 配置插入操作后,获取插入数据的id -->
    <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
        select last_insert_id();
    </selectKey>
    insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday});
</insert>
3.模糊查询

​ 我们在配置文件中没有加入%来作为模糊查询的条件,所以在传入字符串实参时,就需要给定模糊查询的标
识%。 配置文件中的#{username}也只是一个占位符,所以 SQL 语句显示为“?”。

​ 模糊查询另一种查询方式:'%${value}%' 代替#{}

4.{}与${}的区别
#{}表示一个占位符号
通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,
#{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类
型值, #{}括号中可以是 value 或其它名称。
${}表示拼接 sql 串
通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, ${}可以接收简
单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值, ${}括号中只能是 value。

Mybatis参数深入:

parameterType:基本类型与string,采用包名.类名,如Java.Lang.String

​ 包装类采用全限定分类名

特殊情况:实体类属性名称与数据库列名不一致,查询结果为null

​ MySql在windows系统中不区分大小写,映射配置修改如下:

<!-- 配置查询所有操作 -->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user
</select>

改为:

使用别名查询
<!-- 配置查询所有操作 -->
<select id="findAll" resultType="com.itheima.domain.User">
select id as userId,username as userName,birthday as userBirthday,
sex as userSex,address as userAddress from user
</select>

思考:
如果我们的查询很多,都使用别名的话写起来岂不是很麻烦,有没有别的解决办法呢? 如下

resultMap结果类型:

<!-- 建立 User 实体和数据库表的对应关系
type 属性:指定实体类的全限定类名
id 属性:给定一个唯一标识,是给查询 select 标签引用用的。
-->
<resultMap type="com.itheima.domain.User" id="userMap">
<id column="id" property="userId"/>
<result column="username" property="userName"/>
<result column="sex" property="userSex"/>
<result column="address" property="userAddress"/>
<result column="birthday" property="userBirthday"/>
</resultMap>
id 标签:用于指定主键字段
result 标签:用于指定非主键字段
column 属性:用于指定数据库列名
property 属性:用于指定实体类属性名称

对应映射配置

<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
select * from user
</select>

SqlMapConfig.xml配置更新

properties标签:需把jdbcConfig.properties提取出来,放到resources路径下

typeAliases标签:

pakage标签:

<!-- mybatis的主配置文件 -->
<configuration>
  
    <properties resource="jdbcConfig.properties"></properties>
  <!--使用typeAliases配置别名,它只能配置domain中类的别名 -->
    <typeAliases>
       <!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
        <package name="com.itheima.domain"></package>
    </typeAliases>
  
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息 -->
                <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>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
       <!-- package标签是用于指定dao接口所在的包,当指定了之后就不需要在写mapper以及resource或者class了 -->
        <package name="com.itheima.dao"></package>
    </mappers>
</configuration>

动态Sql语句

动态SQL目的是为了更好的重用SQL语句的片段或者灵活的生成SQL语句。本质上,就是SQL语句字符串的拼接。

if进行判断,test属性为true,就拼接上标签中的SQL语句
test中就是OGNL表达式
    1. 不要加 #{}
    2. 逻辑运算,要使用  and,or
    3. 调用对象的方法:  list.size()

    <select id="findUserByCondition" resultMap="userMap" parameterType="user">
        select * from user
        <where>
            <if test="userName != null">
                and username = #{userName}
            </if>
            <if test="userSex != null">
                and sex = #{userSex}
            </if>
        </where>
    </select>
<update id="updateUser" parameterType="user">
          update user
          <set>
              <if test="username !=null and username !=''">
                username = #{username} ,
              </if>
              <if test="username !=null">
                sex = #{sex},
              </if>
              <if test="username !=null">
              password = #{password},
              </if>
          </set>
          where id=#{id}
      </update>
foreach 进行循环的集合,必须通过包装的对象提供。
<select id="findInIds" resultType="user" parameterType="queryvo">
    <where>
      <if test="ids != null and ids.size() > 0">
          <foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
          #{uid}
          </foreach>
      </if>
    </where>
</select>
将SQL中,相同的内容提取出来,在多个地方进行引用。
 1. SQL语句中的字符串
 2. 动态SQL中的标签

抽取SQL语句
    <sql id="defaultUser">
        select * from user
    </sql>
使用SQL
    <select id="findAll" resultMap="userMap">
        <include refid="defaultUser"></include>
    </select>

简化sql片段

<!-- 抽取重复的语句代码片段 -->
<sql id="defaultSql">
select * from user
</sql>

<!-- 配置查询所有操作 -->
<select id="findAll" resultType="user">
<include refid="defaultSql"></include>
</select>

一对一查询

使用 resultMap,定义专门的 resultMap 用于映射一对一查询结果。
通过面向对象的(has a)关系可以得知,我们可以在 Account 类中加入一个 User 类的对象来代表这个账户
是哪个用户的。

一对一映射配置

<resultMap id="accountUserMap" type="account">
    <id property="id" column="aid"></id>
    <result property="uid" column="uid"></result>
    <result property="money" column="money"></result>
    <!-- 一对一的关系映射:配置封装user的内容-->
    <association property="user" column="uid" javaType="user">
        <id property="id" column="id"></id>
        <result column="username" property="username"></result>
        <result column="address" property="address"></result>
        <result column="sex" property="sex"></result>
        <result column="birthday" property="birthday"></result>
    </association>
</resultMap>

一对多查询

<resultMap type="user" id="userMap">
<id column="id" property="id"></id>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<!-- collection 是用于建立一对多中集合属性的对应关系
ofType 用于指定集合元素的数据类型
-->
<collection property="accounts" ofType="account">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
  </collection>
</resultMap>
<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
select u.*,a.id as aid ,a.uid,a.money from user u left outer join account
a on u.id =a.uid
</select>
</mapper>
collection
部分定义了用户关联的账户信息。表示关联查询结果集
property="accList":
关联查询的结果集存储在 User 对象的上哪个属性。
ofType="account":
指定关联查询的结果集中的对象类型即 List中的对象类型。此处可以使用别名,也可以使用全限定名。

多对多查询

<!--定义 role 表的 ResultMap-->
<resultMap id="roleMap" type="role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<collection property="users" ofType="user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</collection>
</resultMap>
<!--查询所有-->
<select id="findAll" resultMap="roleMap">
select u.*,r.id as rid,r.role_name,r.role_desc from role r
left outer join user_role ur on r.id = ur.rid
left outer join user u on u.id = ur.uid
</select>
</mapper>

延迟加载

  1. 什么是延迟加载,为什么要进行延迟加载
    1. 在加载当前对象的时候,对于对象的关联的属性对象或者属性集合,是否立即查询?不立即查询,就是延迟加载,也叫做懒加载。
  2. 在mybatis中如何设置延迟加载(association、 collection 具备延迟加载功能)。
    1. 在当前应用中,开启延迟加载
//SqlMapConfig.xml
    <!--配置参数-->
    <settings>
        <!--开启Mybatis支持延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>  <!-- 默认就是true -->
        <setting name="aggressiveLazyLoading" value="false"></setting>
    </settings>

​ 2.一对一

<!-- 一对一的关系映射:配置封装user的内容
        select属性指定的内容:查询用户的唯一标识:当前mapper配置的sid(namespace+id)
        column属性指定的内容:用户根据id查询时,所需要的参数的值
        -->
        <association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById"></association>

<select id="findAll" resultMap="accountMap">
select * from account
</select>
select: 填写我们要调用的 select 映射的 id
column : 填写我们要传递给 select 映射的参数

​ 3.一对多

<!-- collection 是用于建立一对多中集合属性的对应关系
ofType 用于指定集合元素的数据类型
select 是用于指定查询账户的唯一标识(账户的 dao 全限定类名加上方法名称)
column 是用于指定使用哪个字段的值作为条件查询
-->
<collection property="accounts" ofType="account"
select="com.itheima.dao.IAccountDao.findByUid"
column="id">
</collection>


<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
select * from user
</select>
<collection>标签:
主要用于加载关联的集合对象
select 属性:
用于指定查询 account 列表的 sql 语句,所以填写的是该 sql 映射的 id
column 属性:
用于指定 select 属性的 sql 语句的参数来源,上面的参数来自于 user 的 id 列,所以就写成 id 这一
个字段名了

缓存

缓存的使用

  1. 缓存的作用和缓存使用的时机

    1. 缓存是为了减少了数据库的交互,提交系统的性能
    2. 适合使用缓存的情况:
      1. 经常查询的数据,并且不会经常发生变化。
      2. 允许数据出现不同步的情况
  2. mybatis的缓存

    1. 一级缓存

      SqlSession范围的缓存,同一个SQLSession对象,可以使用同一个缓存。当使用session查询相同的数据(同一方法,相同参数),查询出同一个对象。

      当session关闭,调用clearCache()方法,执行增删改操作时,会被清空。

    2. 二级缓存

      SQLSessionFactory范围的缓存,一般应用只会创建一个SQLSessionFactory对象,当前应用中,所有的操作都可以使用二级缓存的数据。不同的session查询相同的数据(同一方法,相同参数)可以从二级缓存中获取,二级缓存只缓存了数据的内容,每个session会拿到不同的对象,拥有相同的属性。

      二级缓存默认是不启用的,需要手动开启。

//SqlMapConfig.xml
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

//对应的mapper文件中
<cache />

//当前配置的mapper文件的对应需要缓存的SQL上
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="user" useCache="true">
  select * from user where id = #{uid}
</select>

Mybatis常用注解

@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
@CacheNamespace:实现注解二级缓存的使用

  1. 搭建开发环境

    如果使用了注解开发,就不能再使用xml配置。

  2. 单表增删改查操作

  3. 对象属性和表字段不一致的配置

    @Select("select * from user")
        @Results(id="userMap",value={
                @Result(id=true,column = "id",property = "userId"),
                @Result(column = "username",property = "userName"),
                @Result(column = "address",property = "userAddress"),
                @Result(column = "sex",property = "userSex"),
                @Result(column = "birthday",property = "userBirthday")
        })
        List<User> findAll();
      //重复使用resultmap
          @Select("select * from user  where id=#{id} ")
          @ResultMap("userMap")
          User findById(Integer userId);

    4.对象关系映射配置

    1. 一对一

          @Select("select * from account")
          @Results(id="accountMap",value = {
                  @Result(id=true,column = "id",property = "id"),
                  @Result(column = "uid",property = "uid"),
                  @Result(column = "money",property = "money"),
                  @Result(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER))
          })
      //property  对象属性的名称
      //column  后面配置的select查询的参数
      //one 属性是对象
        //  select 查询的方法
        //   fetchType 是否延迟加载    FetchType.EAGER 立即加载
          List<Account> findAll();

      2.一对多

         @Select("select * from user")
          @Results(id="userMap",value={
                  @Result(id=true,column = "id",property = "userId"),
                  @Result(column = "username",property = "userName"),
                  @Result(column = "address",property = "userAddress"),
                  @Result(column = "sex",property = "userSex"),
                  @Result(column = "birthday",property = "userBirthday"),
                  @Result(property = "accounts",column = "id",
                          many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
                                      fetchType = FetchType.LAZY))
          })
      // many 对象的属性是集合  fetchType = FetchType.LAZY 延迟就加载
          List<User> findAll();

猜你喜欢

转载自www.cnblogs.com/mll-yf/p/11355994.html
今日推荐