Mybatis基本原理与使用详解

本文探讨了JDBC的代码冗余和手动对象映射的问题,介绍了Mybatis如何通过减少代码量、自动封装结果和提供安全的参数传递解决这些问题。重点讲解了如何使用Mybatis进行 Dao 开发,包括配置、接口定义、Mapper文件编写和SqlSession的运用,以及其动态代理和缓存机制。
摘要由CSDN通过智能技术生成

1.JDBC的缺陷是什么?

1.代码冗余。加载驱动,创建连接对象,数据库操作对象,关闭对象等操作对应每个数据库操作都是一样的。

2.对应ResultSet结果集,我们要遍历他然后手工创建Java对象装入集合,比较麻烦。

3.数据库操作和业务代码混合。

2.Mybatis是什么?为什么要放弃JDBC而使用Mybatis?

Mybatis可以看做增强版jdbc,底层也是用jdbc操作数据库但Mybatis帮我们减轻了开发的复旦。不需要我们重复编写创建Connection,Statement,PreparedStatement,ResultSet对象的代码。不需我们关注这些对象的关闭,自动帮我们关闭。还有自动将ResultSet中的记录封装成java对象的集合返回给我们。让开发者专注于SQL语句的编写。

3.传统Dao方式使用Mybatis操作数据库的基本步骤(未使用动态代理为我们创建Dao接口实现类对象)

1.加入依赖:mybatis依赖,数据库驱动。

2.创建Dao接口,定义操作数据库的方法。

3.创建Dao接口的同名Sql映射文件(Mapper文件)。

4.创建Dao接口实现类:方法中获得SqlSession对象操作数据库。

5.创建主配置文件:配置数据库数据源,mapper文件的位置等。

public interface StudentDao {
    List<Student> findAll();
}
<?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.zhaojisu.dao.StudentDao">
    <select id="findAll" resultType="com.zhaojisu.entity.Student">
        select name,age from student;
    </select>
</mapper>
public class StudentDaoImpl implements StudentDao {
    @Override
    public List<Student> findAll() {
        String path="mybatis.xml";
        InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
        SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(stream);
        SqlSession sqlSession = factory.openSession();
        List<Student> list = sqlSession.selectList("com.zhaojisu.dao.StudentDao.findAll");
        sqlSession.close();
        return list;
    }
}
public class test {
    @Test
    public void testSelectAsList(){
        StudentDao studentDao=new StudentDaoImpl();
        List<Student> all = studentDao.findAll();
        all.forEach(a->{
            System.out.println(a);
        });
    }
}

4.SqlSessionFactoryBuilder类 SqlSessionFactory 接口 SqlSession 接口  

SqlSessionFactoryBuilder类 :SqlSessionFactory 的 创 建 , 需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方 法 。 由 于 SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将 该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。

SqlSessionFactory 接口:SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用 只需要一个该对象即可。创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。

➢ openSession(true):创建一个有自动提交功能的 SqlSession

➢ openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交

➢ openSession():同 openSession(false)

SqlSession 接口:SqlSession接口对象用于对数据库进行操作。一个SqlSession对应一次数据库会话,一次会话以SqlSession的创建开始,以SqlSession的关闭结束。SqlSession不是线程安全的,每次使用完都需要关闭它。

5. 传统 Dao 开发方式的分析

从上面我们自己写的Dao接口实现类中发现,实现类其实没有干什么实质性的工作,核心就通过SqlSession接口的API,根据mapper文件中的namespace和id定位某条sql语句操作数据库。若Dao接口中有多个方法,实现类方法中大部分代码都是冗余的,处理SqlSession调用的API不同。所以可以使用Myabtis动态代理方式为我们创建Dao接口的实现类对象,省略了Dao接口实现类的编写过程,让开发人员关注Mapper文件中Sql语句的编写。

6. SqlSession.getMapper(Class<T> type)

public class AppTest {
    @Test
    public void t() {
        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis.xml");
        SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        SqlSession sqlSession = factory.openSession();
        StudentDao mapper = sqlSession.getMapper(StudentDao.class);
        System.out.println(mapper);
        List<Student> all = mapper.findAll();
        all.forEach(a->{
            System.out.println(a);
        });
    }
}

 方法返回Dao接口实现类的对象。类型为org.apache.ibatis.binding.MapperProxy。采用的是jdk动态代理的方式。mybatis根据传入的Dao接口的字节码,匹配所以mapper文件中的namespace。然后根据mapper文件中的sql语句创建Dao接口的实现类对象。

 7.myabtis为我们创建Dao接口实现类的要求

1.Mapper文件中<mapper>标签中的namespace为Dao接口的全限定类名。

2.Mapper文件中<select><update><insert><delete>标签中id为Dao接口的方法名。

3.Dao接口中不能有重载方法的定义。

4.Dao接口名和Mapper文件名最好一致,方便我们自己对应。

 8.mybatis传参

1.一个普通类型的参数:#{任意名}

         接口中的方法:Student findOne(String name);

         sql:<select id="findOne" resultType="com.zhaojisu.entity.Student">

                   select * from student where name=#{aaaa}

                 </select>

2.多个参数:使用@Param注解

          接口中的方法:Student findOne2(@Param("name") String name, @Param("age") Integer age);

           sql:<select id="findOne2" resultType="com.zhaojisu.entity.Student">

                     select * from student where name=#{name} and age=#{age}

                    </select>

3.多个参数:使用Java对象传参  #{对象的属性名}

            接口中的方法:Student findOne3(Student student);

            sql:<select id="findOne3" resultType="com.zhaojisu.entity.Student">

                    select * from student where name=#{name} and age=#{age}

                    </select>

4.多个参数,且参数有java对象,普通类型等:使用@Param注解

           接口中的方法:List<Student> selectOne(@Param("age") Integer age, @Param("stu") Student student);

           sql:<select id="selectOne" resultType="com.zhaojisu.entity.Student">

                    select * from t_student where age>#{age} or name=#{stu.name};

                   </select>

5.按位传参:#{arg0}  #{arg1}.....

6.Map<String,Object>集合传参: #{key}

9. ${}

${}表示字符串替换。使用的是Statement对象。有sql注入的风险。通常用来替换表名或者列名。

10. #与$的区别 面试题

1.#是站位符,底层采用的是PreparedStatement。#{}编译之后变成?。效率高。

2.使用#避免了sql注入,比较安全。

3.$表示字符串替换,底层采用的是Statement。先将${}表示的字符串拼接然后执行sql。效率低。

4.使用$有sql注入的风险,在保证安全的情况下,有时需要进行sql注入。

5.$经常用来替换表名,列名。

11.resultType 

返回的java对象类型。mybatis在封装结果时,先调用类的无参构造然后将查出的字段的值赋给同名属性。没有则不赋值。

12. resultMap

 用于当表中字段与类的属性名不一致时,手动将字段对应属性。

<resultMap id="stu" type="com.zhaojisu.entity.Student">
    <id column="name1" property="name"/>
    <result column="age1" property="age"/>
</resultMap>

主键字段用id标签,其他用result标签

13 动态Sql:使用动态Sql时,传入的参数要用java对象

<if test=""> </if>

<select id="selectStudentIf" resultType="com.bjpowernode.domain.Student">
           select id,name,email,age from student where 1=1
    <if test="name != null and name !='' ">
           and name = #{name}
    </if>
    <if test="age > 0 ">
            and age &gt; #{age}
     </if>
</select>

if容易造成语法错误,所以在后面加一个不影响查询结果且恒成立的条件。

 <where><if></if>....</where>

<select id="selectStudentWhere" resultType="com.bjpowernode.domain.Student">
           select id,name,email,age from student
 <where>
 <if test="name != null and name !='' ">
            and name = #{name}
 </if>
 <if test="age > 0 ">
            and age &gt; #{age}
 </if>
 </where>
</select>

使用where标签,在有查询条件时,可以自动添加上 where 子句;没有查询条件时,不会添加 where 子句。需要注意的是,第一个if标签中的 SQL 片断,可以不包含 and。不过,写上 and 也不错, 系统会将多出的 and 去掉。但其它中 SQL 片断的 and,必须要求写上。否则 SQL 语句将拼接出错。

<foreach collection="" item="" open="" close="" spearator="">

</foreach>

<select id="selectStudentForList" 
resultType="com.bjpowernode.domain.Student">
             select id,name,email,age from student
 <if test="list !=null and list.size > 0 ">
             where id in
 <foreach collection="list" open="(" close=")" 
item="stuid" separator=",">
             #{stuid}
 </foreach>
 </if>
</select>

foreach标签一般用于in子句。

collection表示参数中的数组或List集合。

item表示数组或集合中的一个元素。

open表示循环开始前的字符

close表示循环结束后的字符

spearator表示元素之间的字符。

 <sql></sql>用于sql语句的复用

14.PageHelper

PageHelper.startPage(PageNum,PageSize);分页效果只对该方法执行后的第一条查询产生影响。

PageHelper分页过程:

先count(*)计算查询出来的全部记录数,然后在sql语句后面加入limit ?,?进行分页。

15.mybatis缓存

一级缓存

一级缓存是SqlSession级别的,称为本地缓存。默认开启。在同一次会话中使用mybatis会缓存一条查询语句的结果。如果在会话内使用相同的查询语句会直接到缓存里面得到结果。通常,一个事务可视为一个会话。

 二级缓存

在mapper文件中使用<cache type=""/>标签。type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口,且提供一个接受 String 参数作为 id 的构造器。

public interface Cache {
  String getId();
  int getSize();
  void putObject(Object key, Object value);
  Object getObject(Object key);
  boolean hasKey(Object key);
  Object removeObject(Object key);
  void clear();
}

 select语句自动加入缓存调用Cache接口的putObject方法,inset update delete自动删除缓存调用Cache接口的Clear方法。

 16.cache-ref

对某一命名空间的语句,只会使用该命名空间的缓存进行缓存或刷新。 但你可能会想要在多个命名空间中共享相同的缓存配置和实例。要实现这种需求,你可以使用 cache-ref 元素来引用另一个缓存。

如<cache-ref namespace="com.someone.application.data.SomeMapper"/>

17.insert update返回自增主键的值

insert update标签中有userGeneratedKeys keyProperty keyColum属性

userGeneratedKeys表示是否取出数据库内部自增生成的主键。

keyProperty指定将取出的主键值赋给传入对象的哪个属性。

keyColumn指定哪个字段是主键。

insert,update操作的参数必须是java对象。

插入一行 

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})
</insert>

插入多行 传入一个 Author 数组或集合,并返回自动生成的主键。

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username, password, email, bio) values
  <foreach item="item" collection="list" separator=",">
    (#{item.username}, #{item.password}, #{item.email}, #{item.bio})
  </foreach>
</insert>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值