栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Mybatis学习笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Mybatis学习笔记

MyBatis 一、框架简介 1.1 框架的概念

​ 框架是软件的半成品,完成了软件开发过程中的通用操作,程序员只需要很少或不需要进行加工就能实现特定的功能,从而简化开发过程,提高效率。

1.2 常用框架
  • MVC框架:简化了Servlet的开发步骤

    • Struts2
    • SpringMVC
  • 持久层框架:完成数据库操作的框架

    • apache DBUtils
    • Hibernate
    • Spring JPA
    • MyBatis
  • 平台框架:整合不同功能的框架

    • Spring

​ SSM - Spring SpringMVC MyBatis

​ SSH - Spring Struts2 Hibernate

1.3 MyBatis介绍

​ MyBatis是一个半自动的ORM框架:

​ 半自动:因为Hibernate是一个全自动的框架

​ ORM:Object Relational Mapping,对象关系映射,将Java中的一个对象于数据表中的一行记录一一对应。

ORM框架提供了实体类于数据表的映射关系,通过映射文件的配置,实现对象的持久化。

  • MyBatis的前身是iBtis,是由apache基金维护的一个开源项目
  • 在2010年时,iBatis迁移到了(代码托管)Google Code,正式更名为MyBatis
  • 在2013年时,MyBatis迁移到了github
  • MyBatis的特点:
    • 支持自定义SQL,支持存储过程
    • 对原有的JDBC进行了封装,几乎消除了所有的JDBC代码(打开连接,加载执行SQL…),开发者只需要关注SQL
    • 支持XML和注解配置方式自动完成ORM操作,实现结果映射
二、MyBatis框架部署

​ 框架部署,就是将框架引入项目中

2.1 创建Maven项目
  • Java工程
  • Web工程
2.2 添加MyBatis依赖
  • 在pom.xml中添加依赖,以下两个依赖都需要
    • Mysql Driver
    • MyBatis

    mysql
    mysql-connector-java
    5.1.47




    org.mybatis
    mybatis
    3.4.6


2.3 创建MyBatis配置文件

​ 在maven项目的main文件夹下有一个resources文件夹,在此文件夹下新建一个mybatis的配置文件。可以先添加Mybatis配置文件模板。


    
    
    
    
        

            
            

            
            
                
                
                
                
            
        
    
    
        
    

三、MyBatis框架使用

​ 案例:学生信息数据库操作

3.1 创建数据表
create table tb_students(
	sid int primary key auto_increment,
	stu_num char(5) not null UNIQUE,
	stu_name VARCHAR(20) not null,
	stu_gender char(2) not null,
	stu_age int not null
)
3.2 创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
    private int stuId;
    private String stuNum;
    private String stuName;
    private String stuGender;
    private String stuAge;
}
3.3 创建DAO接口,定义方法
public interface StudentDAO {
    public int insertStudent(Student s);
    public int deleteStudent(String stuNum);
}
3.4 创建接口映射文件
  • 在resources目录下新建名为mappers的文件夹
  • 在mappers中新建名为StudentMapper.xml的配置文件,需要根据模板
  • 在映射文件中对DAO中定义的方法进行实现


    
    

    
    
        insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
        values (#{stuNum}, #{stuName}, #stuGender, #{stuAge})
    

    
        delete from tb_students where stu_name = #{stuNum}
    


3.5 将映射文件添加到主配置文件

    

四、单元测试
public class StudentDAOTest {
    @Test
    public void insertStudent() {

        try {
            // 加载mybatis配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

            // 会话(连接)工厂,参数为连接信息
            SqlSessionFactory factory = builder.build(is);
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            System.out.println(studentDAO);

            // 插入方法
            int i = studentDAO.insertStudent(new Student(1, "10002", "李四", "男", 22));

            // 手动提交的事务
            sqlSession.commit();
            System.out.println("插入操作返回的结果, " + i);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void deleteStudent() {
    }
}
五、MyBatis的增删改查

案例:学生信息的增删改查

5.1 添加

略,上一节已经演示

5.2 删除

​ 根据学号进行删除操作

  • 在StudentDAO中定义删除方法

  • 在StudentMapper.xml中对接口方法进行“实现”,使用deldete标签

  • 测试类中编写测试代码

    	@Test
        public void deleteStudent() {
            try {
                InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
                SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
                SqlSessionFactory factory = builder.build(in);
                SqlSession session = factory.openSession();
    
                StudentDAO studentDAO = session.getMapper(StudentDAO.class);
    
                int i = studentDAO.deleteStudent("10002");
                session.commit();
    
                System.out.println("删除操作的结果, " + i);
    
            }   catch (Exception e) {
                e.printStackTrace();
            }
        }
    
5.3 修改操作

​ 根据学号(主键)修改其他字段信息

  • 添加接口方法

    public int updateStudent(Student s);
    
  • 添加mapper映射

    	
            update tb_students set
                stu_name = #{stuName},
                stu_gender = #{stuGender},
                stu_age = #{stuAge}
            where
                stu_num = #{stuNum}
        
    
  • 测试方法

    	@Test
        public void testUpdateStudent() {
            try {
                InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
                SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
                SqlSessionFactory factory = builder.build(in);
                SqlSession session = factory.openSession();
                StudentDAO studentDAO = session.getMapper(StudentDAO.class);
                int i = studentDAO.updateStudent(new Student(0, "10001", "赵六", "女", 26));
                session.commit();
                assertEquals(1, i);
            }   catch (Exception e) {
                e.printStackTrace();
            }
        }
    
5.4 查询所有

​ 需要注意查询出来的结果和普通Java类的映射关系,有两种写法。

​ 注意:查询是不需要事务的,写不写无所谓。

  • 给查出来的字段起别名,并使用指定类名

    	
        
        
            select sid, stu_num, stu_name, stu_gender, stu_age
            from tb_students;
        
    
5.5 查询单条记录
	
        select sid, stu_num, stu_name, stu_gender, stu_age
        from tb_students
        limit #{start}, #{pageSize}
    
	@Test
    public void testListStudentByPage() {
        try {
            InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(in);
            SqlSession session = factory.openSession();
            StudentDAO studentDAO = session.getMapper(StudentDAO.class);
            List s = studentDAO.listStudentsByPage(0, 10);
            assertNotNull(s);
            s.forEach(item -> System.out.println(item.toString()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
5.7 查询记录总数
	public int countStudents();

​ 通过resultMap指定当前的返回值类型为int,不然会报错

	
    select  from tb_students

十、分页插件

​ 分页插件是一个独立于MyBatis框架之外的第三方插件PageHelper

10.1 添加分页插件依赖

    com.github.pagehelper
    pagehelper
    5.2.0


10.2 配置问价

​ 在mybatis的主配置文件中通过plugins标签进行配置


    

10.3 使用
	@Test
    public void testPageHelper() {
        StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);

        PageHelper.startPage(2, 4);
        
        List students = studentDAO.listByPageHelper();
        
        PageInfo pageInfo = new PageInfo(students);
        
        students.forEach(item -> System.out.println(item.toString()));
        System.out.println("--------------------------------------------------");
        pageInfo.getList().forEach(item -> System.out.println(item.toString()));
    }
十一、关联映射 11.1 实体关系

​ 实体关系——数据实体,实体关系事知数据与数据之间的关系

​ 例如:用户和角色,房屋和漏洞,订单和商品

实体关系分为以下四种:

  • 一对一:人和身份证,学生和学号,用户基本信息和用户详情

    数据表关系:主键关联(用户表主键和详情主键相同,表示匹配的数据)

  • 一对多:班级和学生

  • 多对一:学生和班级

    数据表关系:在多的那一端添加外键和一的那一端主键关联

  • 多对多:用户和角色,学生和社团

    数据表关系:建立第三章关系表分别和原两张表的主键进行关联

11.2 创建web项目,引入mybatis 11.3 一对一关系

​ 实例:用户——详情

11.3.1 数据表
-- 用户信息表
create table users(
	user_id int primary key auto_increment,
    user_name varchar(20) not null unique,
    user_pwd varchar(20) not null,
    user_realname varchar(20) not null,
    user_img varchar(100) not null
);
-- 用户详情表
create table details(
	detail_id int primary key auto_increment,
    user_addr varchar(50) not null,
    user_tel char(11) not null,
    user_desc varchar(200),
    uid int not null unique,
    -- constraint users foreign key(uid) reference users(user_id)
);
11.3.2 创建实体类
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int userId;
    private String userName;
    private String userPwd;
    private String userRealName;
    private String userImg;

    // 内部对象
    private Detail detail;
}
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Detail {
    private int detailId;
    private String userAddr;
    private String userTel;
    private String userDesc;
    private int userId;
}
11.3.3 插入

​ 插入时需要保证原子性,插入users表的同时也要插入details表。并且在逻辑上使用外键。需要插入users表的时候使用主键回填。


    user_name, user_pwd, user_realname, user_img



    insert into users()
    values (#{userName}, #{userPwd}, #{userRealName}, #{userImg})




    user_addr, user_tel, user_desc, user_id



    insert into details()
    values (#{userAddr}, #{userTel}, #{userDesc}, #{userId})

@Test
public void testUserInsert() {
    User u = new User(0, "nickname", "wadwa", "赵六", "01.jpg", null);
    Detail d = new Detail(0, "武汉", "18600011125", "这是一条签名", 0);
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    try {
        UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
        int i = userDAO.insertUser(u);
        System.out.println("新用户插入, " + i);

        d.setUserId(u.getUserId());		// 设置逻辑外键
        DetailDAO detailDAO = sqlSession.getMapper(DetailDAO.class);
        int j = detailDAO.insertDetail(d);
        System.out.println("详情插入, " + j);

        sqlSession.commit();	// 提交事务
    }   catch (Exception e) {
        e.printStackTrace();
        sqlSession.rollback();	// 回滚事务
    }
}
11.3.4 一对一关联查询

​ 难点在于,关联的映射,有两种映射方式

  • 表连接查询,将内部类属性直接映射

    
        user_name, user_pwd, user_realname, user_img
    
    
    
        
        
        
        
        
        
        
        
        
        
    
    
    
        select detail_id, 
        from details
        where user_id = #{userId}
    
    
  • 在主表中使用子查询,借助assosiation标签

    
        
        
        
        
        
        
    
    
    
        select cid, cname, cdesc, sid, sname, sage
        from classes c inner join students s
        on c.cid = s.scid
        where c.cid = #{classId};
    
    
  • 子查询

    
        
        
        
    
        
    
    
      	SELECT * FROM BLOG
      	WHERe 1 = 1
      	
        	AND title like #{title}
      	
        
        	AND author_name like #{author.name}
      	
    
    
  • choose、when、otherwise:类似于java中的switch,从多个条件中选择一个使用

    
      	SELECT * FROM BLOG
      	
        	
             	state = #{state}
        	
        	
            	AND title like #{title}
        	
        	
            	AND author_name like #{author.name}
        	
      	
    
    

    ​ where标签元素只会在子元素返回任何内容的情况下才插入 “WHERe” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

  • trim:如果where标签定义的字句不满足要求,也可以自定义trim元素来定制where元素的功能。比如和where子句等价的自定义trim元素为:

    
      ...
    
    

    prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有prefixOverrides属性中指定的内容,并且插入prefix属性中指定的内容。

  • set标签:用于动态更新语句。set元素可以动态的包含需要的列,忽略其他更新的列:

    
      	update Author
        
        	username=#{username},
         	password=#{password},
          	email=#{email},
          	bio=#{bio}
        
      	where id=#{id}
    
    

    等价于:

    
      ...
    
    
  • forEach:集合遍历(在构建IN子句时常用)

    
      
      SELECT * FROM BLOG
      WHERe title LIKE #{pattern}
    
    
  • script:要在带注解的映射器接口类中使用动态的SQL,可以使用script元素

    @Update({""})
    void updateAuthorValues(Author author);
    
12.3 #{} 和 ${}
  • ${key}表示获取参数,先获取参数的值拼接到sql语句中,然后再执行该sql。可能引起SQL注入的问题;
  • #{key}表示获取参数,先完成sql语句的编译(预编译),预编译之后再将获取的参数设置到SQL中。即先编译成?占位符。这样可以避免SQL注入的问题。

注意:在使用${key}的时候,默认去参数时会当作是对象来取,传参的时候可以用参数对象或者HashMap,如果是普通的值则必须要声明参数类型,在select标签中要添加parameterType属性,而且在传参的时候必须加上@Param才能获取到,哪怕只有一个参数。


pubilc List<...> ambigousSearch(@Param("keyword") String keyword);
十三、MyBatis日志配置

​ MyBatis作为一个封装好的ORM框架,其运行过程没有办法进行跟踪,为了当开发者了解其执行流程及每个执行步骤完成所有的工作,MyBatis框架本身集成了log4j日志框架,对运行的过程进行记录跟踪。我们只需要对MyBatis进行相关的日志配置,就可以看到MyBatis运行过程中的日志信息。

13.1 添加日志框架依赖

    log4j
    log4j
    1.2.17

13.2 添加日志配置文件
  • 在resources目录下创建名为log4j.properties的文件,必须在该文件夹下创建该名称的文件,因为MyBatis自动加载。

  • 在log4j.properties文件中配置日志输出的方式。

    ​ 这是MyBatis官方的配置

    # 声明日志的输出级别及输出方式
    log4j.rootLogger=DEBUG,stdout
    log4j.logger.org,mybatis.example.BlogMapper=TRACE
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    # 定义打印格式 %t是线程名称  %5p是日志级别
    log4j.appender.stdout.layout.ConversionPattern=[%t] %5p - %n%m
    
13.3 日志级别

​ 在使用日志框架进行输出日志信息的时候,会根据输出的日志信息的重要程度分为5个级别。由宽松到严格。

日志级别说明
DEBUG输出调试信息
INFO输出提示性信息
WARN输出警告信息
ERROR一般性错误信息
FATAL致命性错误信息
十四、数据库连接池配置-整合Druid

连接池:连接池就是用于存储连接对象的一个容器,而容器就是一个集合,且必须是线程安全的,即两个线程不能拿到同一个连接对象。同时还要具备队列的特性:先进先出原则。

使用连接池的好处:避免频繁创建和关闭数据库连接造成的开销,节省系统资源。

​ MyBatis作为一个ORM框架,在进行数据库操作的时候是需要和数据库进行连接的,MyBatis支持基于数据库的连接池的创建方式。

​ 当我们配置MyBatis数据源的时候,只配置了dataSource标签的type属性值为POOLED时,就可以使用MyBatis内置的连接池管理连接。

​ 如果我们想要使用第三方的数据库连接池就需要自己配置。

14.1 常见的连接池
  • JDBC

  • DBCP

  • C3P0

  • Druid:性能比较好,提供了比较便捷的监控系统,在企业中使用的比较多,阿里开源

  • HiKari:性能最好

14.2 添加依赖

    com.alibaba
    druid
    1.2.8

14.3 创建Druid连接池工厂

​ 创建一个类继承PooledDataSourceFactory,在无参构造器中设置dataSource为DruidDataSource

public class DruidDataSourceFactory extends PooledDataSourceFactory {
    public DruidDataSourceFactory() {
        this.dataSource = new DruidDataSource();
    }
}
14.4 将Druid连接池工厂配置给MyBatis数据源

​ 在MyBatis主配置文件中,使用DruidDataSourceFactory。


    
    
    
    
    
        
        
        
        
    

​ 同时,Druid需要的参数是driverClass和jdbcUrl这和POOLED模式不一样。

十五、MyBatis缓存

​ MyBatis是基于JDBC的封装,使得数据库操作更加便捷,MyBatis除了对JDBC进行操作步骤进行封装之外也对其性能进行了优化:

  • 在MyBatis中引入了缓存机制,用于提升MyBatis的检索效率
  • 在MyBatis中引入了延迟加载机制,用于减少对数据库的不必要的访问
15.1 缓存的工作原理
  • 检查缓存中是否存在要查询的数据;
  • 如果存在,则直接从缓存获取数据并返回,减少了访问数据库的次数,大大提升效率;
  • 如果不存在则从数据库中查询,查询之后将数据返回,同时存储到缓存中,一共下次查询使用。
15.2 MyBatis中的缓存

​ MyBatis中的缓存分为一级缓存和二级缓存

15.2.1 一级缓存

​ 一级缓存也叫做SqlSession级缓存,为每个SqlSession单独分配缓存内存,无需手动开启即可直接使用;多个SqlSession的缓存是不共享的。

一级缓存的特性:

  • 如果多次查询使用的是同一个SqlSession对象,则第一次查询之后数据会存放到缓存,后续的查询则直接访问缓存;
  • 如果第一次查询之后,对查询出来的对象进行修改,此修改会影响到缓存,第二次查询会直接访问缓存,造成查询出来的结果于数据库不一致。
  • 当想要跳过缓存直接查询数据库的时候,可以通过sqlSession.clearCache()来清除当前SqlSession对象的缓存。
  • 如果第一次查询之后,第二次查询之前使用当前的sqlSession进行更新,则缓存失效,第二次查询直接访问数据库。

存在的问题

  • 第一次查询之后,进行了修改操作,数据库已经被修改,但是第二次查询的时候依然显示修改前的数据
    • 分析:修改和查询不是同一个线程,因此使用不同的dao对象(使用了不同的SqlSession),因此修改不会导致查询操作的缓存失效;
    • 解决思路有两种
      • 让查询和修改使用相同的SqlSession对象(不太合理);
      • 让每次进行查询操作之后清空缓存。
15.2.2 二级缓存

​ 二级缓存也称为SqlSessionFactory级的缓存。通过同一个factory对象获取的SqlSession可以共享二级缓存;在应用服务器中SqlSessionFactory是 单例的,因此二级缓存的数据可以全局共享。

二级缓存特性:

  • 二级缓存默认没有开启,需要在mybatis-config.xml中的setting标签中进行开启,

    
    	
    
    

    另外在mapper文件内只要使用了chche标签则,则开启了二级缓存。这个标签有一些参数,eviction设置缓存回收策略,取值LRU(默认),FIFO,SOFT,WEAK。flushInterval设置刷新间隔,默认是不清空。readOnly是否只读。size指定大小。type指定自定义缓存的全类名,实现Cache接口即可。

    
    
  • 二级缓存只能缓存实现了序列化接口的对象;

15.2.3 缓存查询数据
  • 先判断二级缓存是否开启,如果没开启,再判断一级缓存是否开启,如果没开启,直接查数据库

  • 如果一级缓存关闭,即使二级缓存开启也没有数据,因为二级缓存的数据从一级缓存获取

  • 一般不会关闭一级缓存

  • 二级缓存默认不开启

  • 如果二级缓存关闭,直接判断一级缓存是否有数据,如果没有就查数据库

  • 如果二级缓存开启,先判断二级缓存有没有数据,如果有就直接返回;如果没有,就查询一级缓存,如果有就返回,没有就查询数据库;

​ 综上所述:先查二级缓存,再查一级缓存,再查数据库;即使在一个sqlSession中,也会先查二级缓存;一个namespace中的查询更是如此;

十六、延迟加载

​ 延迟加载是指,如果在MyBatis执行了子查询(至少查询两次以上),默认只执行第一次查询,当用到子查询的查询结果时,才会触发子查询的执行。如果没有使用子查询的结果,则子查询不会进行。

​ 需要配置MyBatis的主配置文件

  
     
     
  
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/683241.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号