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

Mybatis

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

Mybatis

1、认识mybatis

现在操作数据库使用的是:jdbc技术,比较麻烦,操作步骤多

mybatis是一个简化jdbc操作的一个框架。

什么是框架?框架是程序的一个半成品,无需程序员进行设计,只需要在框架的基础上完成自己的功能即可,简化了开发,提升开发效率

就像简历的模板

由于java是开源的,所以框架很多,在同一个技术上会出现多个框架

ORM框架是一类简化JDBC技术的框架的统称

O:Object 对象 java中的对象

R: Relationship 关系 数据库中的表

M: mapping 映射(关联)

ORM框架的原理:通过把java中的对象和数据库中的表进行关联后,从而进行JDBC简化操作的框架

ORM:mybatis hibernate jdbctemplate 等

mybatis框架的特点:

1、基于sql语句的框架

2、轻量级的半自动的框架(hibernate全自动,无需编写sql)

3、依赖数据库(不同数据库下,有不同的SQL语句)

4、对JDBC封装的持久化ORM框架


2、搭建mybatis的环境 1、新建maven项目 2、添加jar依赖

mysql mybatis


    
    
        org.mybatis
        mybatis
        3.4.6
    

    
    
        mysql
        mysql-connector-java
        5.1.47
    
3、编写配置文件 mybatis-cnfig.xml

需要有文件头,是mybatis官方提供的

 

mybatis-3-config.dtd是一个xml格式的文件,其作用就是限制引入该dtd文件的文件的格式

例如:必须包含哪些标签,标签的名字、顺序等




    
    
        
        
            
            
            
            
                
                
                
                
                
                
            
        
        
            
            
        

    


3、使用mybatis 1、新建实体类

【注意】实体类的属性名最好和表中的列名要一致(不区分大小写)

package com.qf.mybatispro2105.pojo;

import java.io.Serializable;

public class Dept implements Serializable {
    //确保无参构造方法和全参构造方法二选一

    public Dept() {
    }

    public Dept(int deptNo, String dName, String loc) {
        this.deptNo = deptNo;
        this.dName = dName;
        this.loc = loc;
    }

    private int deptNo;
    private String dName;
    private String loc;

    public int getDeptNo() {
        return deptNo;
    }

    public void setDeptNo(int deptNo) {
        this.deptNo = deptNo;
    }

    public String getdName() {
        return dName;
    }

    public void setdName(String dName) {
        this.dName = dName;
    }

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }
}
2、编写dao---接口
package com.qf.mybatispro2105.dao;

import com.qf.mybatispro2105.pojo.Dept;

public interface DeptDao {
    //添加的方法
    int addDept(Dept dept);
}

3、编写接口所对应的映射文件

mybatis在使用过程中有两种方式:

方式1:基于接口的注解方式(用的少,不推荐)

方式2:基于接口的映射文件方式(推荐)

创建在resources目录下 DeptMapper.xml




    
    
    
        
        insert into dept(deptno,dname,loc)
        values
        (#{deptNo},#{dName},#{loc})
    
4、把映射文件要注册到mybatis的配置文件中

mybatis-config.xml添加配置如下


    

mysql数据库自动提交事务---每条语句就是一个事务

mybatis关掉了mysql中自动提交事务的功能,需要手动提交事务

事务的本质:数据库缓存技术,执行到了缓存中

手动通过代码去控制事务称为编程式事务,有缺点:控制事务提交、回滚的代码分散在各个代码中

5、测试
package com.qf.mybatispro2105.test;

import com.qf.mybatispro2105.dao.DeptDao;
import com.qf.mybatispro2105.pojo.Dept;
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;

public class TestDept {
    public static void main(String[] args) {
        //mybatis的工作流程
        SqlSession sqlSession=null;
        try {
            //1、通过流读取mybatis的配置文件信息
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

            //、建SqlSessionFactoryBuilder,目的是为了创建SqlSessionFactory
            SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();

            //3、构建出SqlSessionFactory,目的是为了创建SqlSession对象
            SqlSessionFactory factory=builder.build(inputStream);

            //4、创建SqlSession对象
            sqlSession=factory.openSession();

            //mybatis是通过sqlSession操作数据库的
            //通过SqlSession对象得到接口的实现类对象(代理对象---mybatis内部使用了代理设计模式)
            DeptDao deptDao=sqlSession.getMapper(DeptDao.class);
            //调用方法
            Dept dept=new Dept(9010,"java2105部","设计城906室");
            int result=deptDao.addDept(dept);
            //手动提交事务
            sqlSession.commit();
            if(result==1){
                System.out.println("添加部门成功");
            }else{
                System.out.println("添加部门失败");
            }
        } catch (IOException e) {
            e.printStackTrace();
            //回滚事务
            sqlSession.rollback();
        }
    }
}

测试的方法:了解

package com.qf.mybatispro2105.test;

import com.qf.mybatispro2105.pojo.Dept;
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;

public class TestDept2 {
    public static void main(String[] args) throws IOException {
        InputStream inputStream= Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
        SqlSessionFactory factory=builder.build(inputStream);
        SqlSession sqlSession=factory.openSession();
        Dept dept=new Dept(9011,"aaa","aaaaaa");
        //直接调用sqlSession对象的方法
        try{
            int result= sqlSession.insert("com.qf.mybatispro2105.dao.DeptDao.addDept",dept);
            sqlSession.commit();
            if(result==1){
                System.out.println("添加部门成功!");
            }else{
                System.out.println("添加部门失败!");
            }
        }catch (Exception ex){
            sqlSession.rollback();
        }
    }
}


4、细化mybatis的配置

 原因:在映射文件中直接使用了类名,没有添加包名,而且,在mybatis的配置文件中也没有进行实体类的配置

1、1:配置别名

    
    
    
    

typeAliases的配置做了两件事

1、告知实体类的位置

2、给实体类命名别名,默认情况下别名就是类名的首字母小写

1、2:配置数据库信息

1、创建db.properties文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db2105pro?useUnicode=true&characterEncoding=utf-8
username=root
password=root

2、在mybatis配置文件的开始,引入db.properties文件

3、修改数据库连接信息的配置,使用${}读取db.properties文件的内容




    
    
    
    
        
        
        
        
    
    
    
        
        
            
            
            
            
                
                
                
                
                
                
            
        
        
            
            
        

    
    
    
        
    

1、3:配置日志文件

在开发过程中,想了解mybatis的执行过程,mybatis内部使用了log4j日志框架,来记录mybatis的工作过程,

可以通过设置log4j,把mybatis的运行过程打印到控制台上,便于调试程序

log4j具体内容,后面会具体学习,只会配置即可

1、创建log4j.properties 文件,【注意】必须是这个名字

2、复制进去配置信息

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

3、添加log4j的jar依赖


		log4j
		log4j
		1.2.17

4、在使用的时候,控制台打印mybatis的信息

1、4:配置mapper文件的存储位置

原因:

1、没有注册到mybatis的配置文件里

2、路径存储错误,默认到resources路径下查找映射的mapper文件

在pom.xml文件的最后,添加如下配置


    
        
            src/main/java
            
                *.xml
                **代表多级目录-->
            
            true
        
    

在mybatis的配置文件中修改如下:


    
    

把mapper文件的编码方式由UTF-8 改为utf8,mapper文件里的中文注释就会被支持


5、查询的实现

        select deptno,dname,loc
        from dept
        where dname=#{arg0} and loc=#{arg1}
    

    
    
        select deptno,dname,loc
        from dept
        where dname=#{name} and loc=#{location}
    
    
    
        select carid,carnumber,money,cartype,dept,gas,safeman,yeartime,
                        displacement,model,datepurchase,datereg,status,deptcarperson,
                        finaltime,nextyeartime,power,totalmass,fare,onboarddata,
                        remarks,filledby,fillingtime
        from car
    

    
         select carid,carnumber,money,cartype,dept,gas,safeman,yeartime,
                        displacement,model,datepurchase,datereg,status,deptcarperson,
                        finaltime,nextyeartime,power,totalmass,fare,onboarddata,
                        remarks,filledby,fillingtime
        from car
        where carid=#{ci} and money=#{mo}
    

    
         select carid,carnumber,money,cartype,dept,gas,safeman,yeartime,
                        displacement,model,datepurchase,datereg,status,deptcarperson,
                        finaltime,nextyeartime,power,totalmass,fare,onboarddata,
                        remarks,filledby,fillingtime
        from car
        where carid=#{carId} and money=#{money}
    

    
    select deptno,dname,loc
    from dept
    where loc like CONCAt('%',#{loc},'%')


8、主键回填

订单表:order

id 订单id 自增

ordertime 订单时间

userID 下单人

totalprice 总价

1 2022-4-1 taoxiankun 200

订单详情表:orderdetail

id 自增主键

orderid 订单编号

goodsid 商品编号

1 1 kele

2 1 keyboard

主从表结构:

主表:订单基本信息

从表:订单的商品信息

对于主从表结构,在添加时应该在一个事务内,先插入主表,获得自增主键,然后在插入从表是要使用这个主键

如何在插入一个表后,获得我插入的主键?

使用主键回填的方式

主键:int类型,自增主键回填


    
    
    
        
        select LAST_INSERT_ID()
    
    insert into `order`(ordertime,totalprice,userid)
    values
    (#{orderTime},#{totalPrice},#{userId})



    
    insert into `order`(ordertime,totalprice,userid)
    values
    (#{orderTime},#{totalPrice},#{userId})

【说明】

1、以上两种方式,只适用于int自增主键

2、本质是执行mysql的函数LAST_INSERT_ID()来获取的主键

主键:字符串类型,需要自己调用数据库的UUID函数,产生主键

顺序: 先产生主键,回填给实体类的属性

然后 执行insert ,获取刚才回填的主键




    
        
            

            select REPLACE(UUID(),'-','')
        

        insert into order2 (id,name)
        values (#{id},#{name})
    


9、封装一个工具类

原则:

1、复用

2、用好作用域

package com.qf.mybatispro2105.util;

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;

public class MybatisUtil {
    //SqlSessionFactoryBuilder:作用域是局部变量级别,构建完SqlSessionFactory,就可以销毁
    //SqlSessionFactory:作用域时程序级别,是static,不断的创建sqlSession
    //sqlSession:是会话级别的,访问数据库之前创建,访问数据库之后销毁
    //声明session工厂
    private static SqlSessionFactory factory;
    //声明局部线程类,存放SqlSession
    private static final ThreadLocal tl=new ThreadLocal();
    static {

        //读取配置文件
        try {
            InputStream inputStream= Resources.getResourceAsStream("mybatis-config.xml");
            //局部
            SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
            factory=builder.build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //获得SqlSession的方法
    private static SqlSession openSession(){
        //到局部线程类中获取
        SqlSession session=tl.get();
        //判断是否获取到
        if(session==null){
            //则创建
            session=factory.openSession();
            //存入到局部线程类中
            tl.set(session);
        }
        return session;
    }
    //关闭session
    private static  void closeSession(){
        //到局部线程类里获取要关闭的sqlSession
        SqlSession session=tl.get();
        session.close();
        //从局部线程类删除
        tl.remove();   //删除当前线程ID的sqlsessiion
    }
    //提交事务
    public static void commit(){
        SqlSession sqlSession=MybatisUtil.openSession();
        sqlSession.commit();
        //关闭sqlSession
        MybatisUtil.closeSession();
    }
    //回滚事务
    public static void rollback(){
        SqlSession sqlSession=openSession();
        sqlSession.rollback();
        closeSession();
    }
    //获取接口实现类对象的方法
    public static  T getMapper(Class clazz){
        SqlSession sqlSession=openSession();
        return sqlSession.getMapper(clazz);
    }
}

测试工具类

package com.qf.mybatispro2105.util;

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;

public class MybatisUtil {
    //SqlSessionFactoryBuilder:作用域是局部变量级别,构建完SqlSessionFactory,就可以销毁
    //SqlSessionFactory:作用域时程序级别,是static,不断的创建sqlSession
    //sqlSession:是会话级别的,访问数据库之前创建,访问数据库之后销毁
    //声明session工厂
    private static SqlSessionFactory factory;
    //声明局部线程类,存放SqlSession
    private static final ThreadLocal tl=new ThreadLocal();
    static {

        //读取配置文件
        try {
            InputStream inputStream= Resources.getResourceAsStream("mybatis-config.xml");
            //局部
            SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
            factory=builder.build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //获得SqlSession的方法
    private static SqlSession openSession(){
        //到局部线程类中获取
        SqlSession session=tl.get();
        //判断是否获取到
        if(session==null){
            //则创建
            session=factory.openSession();
            //存入到局部线程类中
            tl.set(session);
        }
        return session;
    }
    //关闭session
    private static  void closeSession(){
        //到局部线程类里获取要关闭的sqlSession
        SqlSession session=tl.get();
        session.close();
        //从局部线程类删除
        tl.remove();   //删除当前线程ID的sqlsessiion
    }
    //提交事务
    public static void commit(){
        SqlSession sqlSession=MybatisUtil.openSession();
        sqlSession.commit();
        //关闭sqlSession
        MybatisUtil.closeSession();
    }
    //回滚事务
    public static void rollback(){
        SqlSession sqlSession=openSession();
        sqlSession.rollback();
        closeSession();
    }
    //获取接口实现类对象的方法
    public static  T getMapper(Class clazz){
        SqlSession sqlSession=openSession();
        return sqlSession.getMapper(clazz);
    }
}


10、主键回填的应用

下订单时:写入订单表,订单详情表

要求:

1、在一个事务内

2、先插入订单主表,主键回填后,插入订单从表

在MVC分层下实现

dao:插入订单主表的方法---主键回填

插入订单从表的方法

service:控制事务,调用插入订单主表及插入订单从的dao方法

v: 构建商品集合

package com.qf.mybatispro2105.service.impl;

import com.qf.mybatispro2105.dao.OrderDao;
import com.qf.mybatispro2105.dao.OrderDetailDao;
import com.qf.mybatispro2105.pojo.Order;
import com.qf.mybatispro2105.pojo.OrderDetail;
import com.qf.mybatispro2105.service.OrderService;
import com.qf.mybatispro2105.util.DateUtil;
import com.qf.mybatispro2105.util.MybatisUtil;

import java.util.List;

public class OrderServiceImpl implements OrderService {
    //获取dao操作对象
    private OrderDao orderDao= MybatisUtil.getMapper(OrderDao.class);
    private OrderDetailDao orderDetailDao=MybatisUtil.getMapper(OrderDetailDao.class);
    public int setOrder(List details,String userId) {
        //先生成订单
        Order order=new Order();
        order.setUserId(userId);
        order.setTotalPrice(50);
        order.setOrderTime(DateUtil.getTime());
        int count=0;
        try{
            //插入订单
            int orderResult=orderDao.addOrder2(order);
            //经过一步执行,主键已经回填
            //遍历插入订单从表
            for(OrderDetail detail:details){
                //把订单表里的订单编号,赋值给订单详情独享
                detail.setOrderId(order.getId());
                //插入订单详情
                count+=orderDetailDao.addOrderDetail(detail);
            }
            //提交事务
            MybatisUtil.commit();


        }catch (Exception ex){
            //回滚事务
            MybatisUtil.rollback();
        }
        return count;
    }
}


11、ORM映射

针对查询操作而言

【面试题】当查询结果的列名和实体类的属性名不一致时,该如何处理?

方式1:sql命名别名,让别名和实体类的属性名一致
private int dno; //deptNo
private String name;  //dname
private String location;  //loc

命名别名


        select empno,ename,job,com,sal,joindate,mgrno,emp.deptno,
        dname,loc
        from emp
        inner join dept on emp.deptno=dept.deptno
    

完成一对多映射:

一个类中的属性是集合类型,称为一对多映射





    
    
        
        
        
        
        
        
        
        
        
        
        
        
        

        
    

    
    
        
        
        
    
    
    
        select empno,ename,job,com,sal,joindate,mgrno,emp.deptno,
        dname,loc,phone,code,content
        from emp
        inner join dept on emp.deptno=dept.deptno
        left join address on emp.empno=address.userid
    

完成多对多映射

使用场景

学生表:student:stuno,stuname

课程表:course:courseno,coursename

学生和课程之间就是多对多的关系:一个学生可以选多门课程,一门课程也可以被多个学生选

中间表:选课表,通过中间表把学生和课程之间的多对多的关系进行体现

stu_counse: id stuno courseno

多对多查询3表联查

Students实体类

package com.qf.mybatispro2105.pojo;

import java.util.List;

public class Students {
    public Students() {
    }

    public Students(String stuNo, String stuName) {
        this.stuNo = stuNo;
        this.stuName = stuName;
    }

    public Students(String stuNo, String stuName, List courseList) {
        this.stuNo = stuNo;
        this.stuName = stuName;
        this.courseList = courseList;
    }

    private String stuNo;
    private String stuName;
    //所选课程的属性
    //一对多
    private List courseList;

    public List getCourseList() {
        return courseList;
    }

    public void setCourseList(List courseList) {
        this.courseList = courseList;
    }

    public String getStuNo() {
        return stuNo;
    }

    public void setStuNo(String stuNo) {
        this.stuNo = stuNo;
    }

    public String getStuName() {
        return stuName;
    }

    public void setStuName(String stuName) {
        this.stuName = stuName;
    }
}

Course实体类

package com.qf.mybatispro2105.pojo;

import java.util.List;

public class Course {
    public Course() {
    }

    public Course(String courseNo, String courseName) {
        this.courseNo = courseNo;
        this.courseName = courseName;
    }

    public Course(String courseNo, String courseName, List studentsList) {
        this.courseNo = courseNo;
        this.courseName = courseName;
        this.studentsList = studentsList;
    }

    private String courseNo;
    private String courseName;
    //选本门课的学生
    //一对多
    private List studentsList;

    public List getStudentsList() {
        return studentsList;
    }

    public void setStudentsList(List studentsList) {
        this.studentsList = studentsList;
    }

    public String getCourseNo() {
        return courseNo;
    }

    public void setCourseNo(String courseNo) {
        this.courseNo = courseNo;
    }

    public String getCourseName() {
        return courseName;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }
}

由于是双向的一对多关系,就是多对多的关系

查询:

1、查询学生,同时查询学生所选课程




    
        
        
        
    
    
        
        
    
    
        select c.courseno,c.coursename,s.stuno,s.stuname
        from course c
        left join stucourse sc on c.courseno=sc.courseno
        inner join students s on s.stuno=sc.stuno
    


12、动态sql

动态sql语句,SQL语句会根据条件进行动态的组装、拼接

java中的逻辑判断,也能使用sql语句的动态拼接,但是比较麻烦,mybatis提供了一些动态sql标签,简化编程。

动态sql的原理:ognl

动态SQL本质就是sql语句的拼接

每个动态标签都是sqlnode类,对标签进行判断、进行拼接

sql标签:把重复使用的sql或部分sql语句使用sql标签进行定义,需要的引入即可



    
        select deptno,dname,loc
        from dept
    


    select deptno,dname,loc


    
在外部xml文件中声明复用的sql,引入时:命名空间.sql的ID namespace.id

if标签:判断

where标签 :相当于where关键字

【作用】

1、判断是否添加where关键字

2、过滤第一个条件前面的 and 或or等关键字

Set标签:在Update语句中使用

    update dept
    
        
            dname=#{dName},
        
        
            loc=#{loc}
        
    
    where deptno=#{deptNo}

【作用】

1、相当于update语句中的set关键字

2、过滤掉 多余的,

trim标签:用来替代where、set标签

替代where

替换set标签


    update dept
    
    
    
        
            dname=#{dName},
        
        
            loc=#{loc},
        
    
    where deptno=#{deptNo}

foreach标签:用于循环
参数描述取值
collection容器类型list、array、map
open起始符(
close结束符)
separator分隔符,
index下标号从0开始,依次递增
item当前项任意名称(循环中通过 #{任意名称} 表达式访问)


    delete from dept
    where deptno in
    
    
        #{dno}
    

【补充】为了提升效率,一条sql插入多个值

int addOrderDetail2(List list);
-- 一条语句如何插入多行值,这样提升效率
insert into orderdetail(orderid,goodsid,num)
VALUES
(5,'test1',1),
(5,'test2',1),
(5,'test3',1)

    insert into orderdetail(orderid,goodsid,num)
    values 
    
        (#{detail.orderId},#{detail.goodsId},#{detail.num})
    


 13、mybatis的缓存技术(查询)

mybatis把从数据库中查询到的结果,给缓存到一个“地方”,下次再需要这个结果时,mybatis就不从数据库重新查询,而是到缓存里获取查询结果,目的是提高查询效率

mybatis根据缓存的地方不同,分为一级缓存、二级缓存

一级缓存:缓存在session中,是默认的缓存

当sqlsession没有关闭及更新的情况下缓存数据有效

sqlsession缓存失效的场景:

1、提交事务

2、close

3、flush

二级缓存:缓存在“文件中”,跨session, 本质是文件的序列化

【要求】实体类及相关内容必须实现Serializable接口

开启二级缓存配置:

在mybatis的配置文件中mybatis-config.xml

【注意】配置的顺序


     

在mapper映射文件中还需要配置

 

如果某个方法不使用二级缓存,做如下配置


    select deptno,dname,loc from dept where deptno=#{deptNo}


18、mybatis的执行器---扩充

执行器:executor sqlsession通过执行器executor来执行sql语句

mybatis的executor分为以下下3种:

1、ExecutorType.SIMPLE:简单执行器,每次使用时创建,使用完毕销毁

2、ExecutorType.REUSE:可重复使用执行器,每次使用时创建,创建完毕后,存储在map集合中,使用后不销毁,下次继续使用

3、ExecutorType.BATCH:批处理执行器,一次可执行多条语句

如何指定执行器?

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

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

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