mybatis框架知识梳理

mybatis框架知识梳理

Mybatis框架概述:

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

为何使用Mybatis:

传统jdbc访问数据库:

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

​ 2.数据库链接频繁建立、释放,形成系统资源的浪费,影响系统性能,web

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

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

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

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

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

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

解决: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();

相关文章
相关标签/搜索