Mybatis框架知识整理

Mybatis框架知识整理,第1张

Mybatis框架知识整理 Mybatis简介

学习框架前,先梳理原始JDBC如何 *** 作数据库

原始JDBC *** 作 查询数据

引入依赖

    
        
            junit
            junit
            4.13.2
            test
        
        
            mysql
            mysql-connector-java
            8.0.26
        
    

测试查询数据
测试用表

数据库表映射类

package com.jtyhnet.domain;

public class Account {

    private int id;
    private String name;
    private float money;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getMoney() {
        return money;
    }

    public void setMoney(float money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "account{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", money=" + money +
                '}';
    }
}

测试代码

import com.jtyhnet.domain.Account;
import org.junit.Test;

import java.sql.*;

public class JDBCTest1 {

    @Test
    public void test1() throws ClassNotFoundException, SQLException {
        //注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //获得连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "root");
        //获得statement
        PreparedStatement preparedStatement = connection.prepareStatement("select * from account");
        //执行查询
        ResultSet resultSet = preparedStatement.executeQuery();
        //遍历结果集
        while (resultSet.next()){
            //封装实体
            Account account = new Account();
            account.setId(resultSet.getInt("id"));
            account.setName(resultSet.getString("name"));
            account.setMoney(resultSet.getFloat("money"));
            //实体封装完成
            System.out.println(account);
        }

        //释放资源
        resultSet.close();
        preparedStatement.close();
        connection.close();


    }

}

插入数据
    @Test
    public void test2() throws ClassNotFoundException, SQLException {
        Account account = new Account();
        account.setName("wangwu");
        account.setMoney(6000);

        //注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //获得连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "root");
        //获得statement
        PreparedStatement preparedStatement = connection.prepareStatement("insert into account(id,name,money) values (?,?,?)");
        //设置占位符参数
        preparedStatement.setInt(1,account.getId());
        preparedStatement.setString(2,account.getName());
        preparedStatement.setFloat(3,account.getMoney());
        //执行更新 *** 作
        preparedStatement.executeUpdate();
        //释放资源
        preparedStatement.close();
        connection.close();
    }

原始JDBC *** 作分析

原始jdbc开发存在的问题如下:
① 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能
② sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变java代码。
③ 查询 *** 作时,需要手动将结果集中的数据手动封装到实体中。插入 *** 作时,需要手动将实体的数据设置到sql语句的占位符位置
应对上述问题给出的解决方案:
① 使用数据库连接池初始化连接资源
② 将sql语句抽取到xml配置文件中
③ 使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射

Mybatis简介
  • mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
  • mybatis通过xml或注解的方式将要执行的各种 statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。
  • 最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api 底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化 *** 作。
快速入门

MyBatis开发步骤:
① 添加MyBatis的坐标
② 创建user数据表
③ 编写User实体类
④ 编写映射文件UserMapper.xml
⑤ 编写核心文件SqlMapConfig.xml
⑥ 编写测试类

依赖导入

    
        
            junit
            junit
            4.13.2
            test
        
        
            mysql
            mysql-connector-java
            8.0.26
        
        
            org.mybatis
            mybatis
            3.4.5
        
        
            log4j
            log4j
            1.2.12
        
    

为查看详细日志,resources下新建log4j.properties配置文件

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=debug, stdout

创建测试用表

创建实体类

package com.jtyhnet.domain;

public class User {

    private int id;
    private String username;
    private String password;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + ''' +
                ", password='" + password + ''' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

编写映射文件UserMapper.xml




    
        select * from USER
    
      
    
        insert into user values (#{id},#{username},#{password})
    
    
    
        update user set username=#{username},password=#{password} where id = #{id}
    
    
    
        delete from user where id = #{id}
    
    
    

//查
List userList = sqlSession.selectList("userMapper.findAll");

//增
int insert = sqlSession.insert("userMapper.add",user);
sqlSession.commit();

//删
int update = sqlSession.update("userMapper.update", user);
sqlSession.commit();

//改
int delete = sqlSession.delete("userMapper.delete", 3);
sqlSession.commit();
MyBatis核心配置文件概述

核心配置文件层级关系

MyBatis常用配置解析 environments标签

数据库环境的配置,支持多环境配置

其中,事务管理器(transactionManager)类型有两种:

  • JDBC:这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
  • MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection 属性设置为 false 来阻止它默认的关闭行为。

其中,数据源(dataSource)类型有三种:

  • UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
  • POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。
  • JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置
    一个 JNDI 上下文的引用。
mapper标签

该标签的作用是加载映射的,加载方式有如下几种:

  • 使用相对于类路径的资源引用,例如:
  • 使用完全限定资源定位符(URL),例如:
  • 使用映射器接口实现类的完全限定类名,例如:
  • 将包内的映射器接口实现全部注册为映射器,例如:
Properties标签

实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件

typeAliases标签

类型别名是为Java 类型设置一个短的名字。原来的类型名称配置如下

    
        select * from USER
    
    
    
        insert into user values (#{id},#{username},#{password})
    
    
    
        update user set username=#{username},password=#{password} where id = #{id}
    
    
    
        delete from user where id = #{id}
    
    
    

执行查询测试

上面我们是自定义的别名,mybatis框架已经为我们设置好的一些常用的类型的别名

别名数据类型stringStringlongLongintIntegerdoubleDoublebooleanBoolean… …… … 知识小结

核心配置文件常用配置:
1、properties标签:该标签可以加载外部的properties文件


2、typeAliases标签:设置类型别名


	

3、mappers标签:加载映射配置


	

4、environments标签:数据源环境配置标签


    
        
        
            
            
            
            
        
    

MyBatis相应API SqlSession工厂构建器SqlSessionFactoryBuilder

常用API:SqlSessionFactory build(InputStream inputStream)
通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象

//加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//获得SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

其中, Resources 工具类,这个类在 org.apache.ibatis.io 包中。Resources 类帮助你从类路径下、文件系统或一个 web URL 中加载资源文件。

SqlSession工厂对象SqlSessionFactory

SqlSessionFactory 有多个个方法创建 SqlSession 实例。常用的有如下两个:

方法解释openSession()会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提交该事务,更新 *** 作数据才会持久化到数据库中openSession(boolean autoCommit)参数为是否自动提交,如果设置为true,那么不需要手动提交事务 SqlSession会话对象

SqlSession 实例在 MyBatis 中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法。
执行语句的方法主要有:

 T selectOne(String statement, Object parameter) 
 List selectList(String statement, Object parameter) 
int insert(String statement, Object parameter) 
int update(String statement, Object parameter) 
int delete(String statement, Object parameter)

*** 作事务的方法主要有:

void commit()
void rollback()
MyBatis的Dao层实现方式 传统开发方式
package com.jtyhnet.dao;

import com.jtyhnet.domain.User;

import java.util.List;

public interface UserDao {

    List findAll() throws Exception;
}

package com.jtyhnet.dao.impl;

import com.jtyhnet.dao.UserDao;
import com.jtyhnet.domain.User;
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.InputStream;
import java.util.List;

public class UserDaoImpl implements UserDao {
    @Override
    public List findAll() throws Exception {
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List userList = sqlSession.selectList("userMapper.findAll");
        return userList;
    }
}

测试

    @Test
    public void test5() throws Exception {
        UserDao userDao = new UserDaoImpl();
        List userList = userDao.findAll();
        System.out.println(userList);
    }

代理开发方式

采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。
Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper 接口开发需要遵循以下规范:
1、 Mapper.xml文件中的namespace与mapper接口的全限定名相同
2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

接口类

package com.jtyhnet.dao;

import com.jtyhnet.domain.User;

import java.io.IOException;
import java.util.List;

public interface UserDao {

    List findAll() throws IOException;
    User findById(int id);
}





    
        select * from user where id = #{id}
    

    @Test
    public void test21() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserDao userDaoMapper = sqlSession.getMapper(UserDao.class);
        List userList = userDaoMapper.findAll();
        System.out.println(userList);
        System.out.println("------");
        User user1 = userDaoMapper.findById(1);
        System.out.println(user1);

    }



传统方式实现接口
代理方式在xml配置文件中对应接口类配置映射

MyBatis映射文件深入 动态sql语句

Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。

动态 SQL 之

我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。

接口类

package com.jtyhnet.dao;

import com.jtyhnet.domain.User;

import java.io.IOException;
import java.util.List;

public interface UserDao {

    List findAll() throws IOException;
    User findById(int id);
    User findByCondition(User user);
}

mapper配置

    

    
    
    

    

MyBatis核心配置文件深入 typeHandlers标签

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器(截取部分)。


你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类org.apache.ibatis.type.baseTypeHandler, 然后可以选择性地将它映射到一个JDBC类型。例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。

开发步骤:
① 定义转换类继承类baseTypeHandler
② 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时 mysql的字符串类型转换成 java的Type类型的方法
③ 在MyBatis核心配置文件中进行注册
④ 测试转换是否正确

创建测试用表

CREATE TABLE userinfo (
    id INT NOT NULL auto_increment,	 
    username VARCHAR ( 50 ) ,	
    password VARCHAR ( 50 )  ,		
    birthday VARCHAR ( 50 )  ,		 
    PRIMARY KEY ( id ) 				 
);
insert  into userinfo values (null,'zhangsan','123','1640784746140');

创建实体类

package com.jtyhnet.domain;

import java.util.Date;

public class Userinfo {

    private int id;
    private String username;
    private String password;
    private Date birthday;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Userinfo{" +
                "id=" + id +
                ", username='" + username + ''' +
                ", password='" + password + ''' +
                ", birthday=" + birthday +
                '}';
    }
}

插入数据接口方法

package com.jtyhnet.dao;

import com.jtyhnet.domain.Userinfo;

public interface UserinfoDao {
    void add(Userinfo userinfo);
}

定义转换类

package com.jtyhnet.typeHandler;

import org.apache.ibatis.type.baseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

public class MyDateTypeHandler extends baseTypeHandler {
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
        preparedStatement.setString(i,date.getTime()+"");
    }

    @Override
    public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
        return new Date(resultSet.getLong(s));
    }

    @Override
    public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
        return new Date(resultSet.getLong(i));
    }

    @Override
    public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        return callableStatement.getDate(i);
    }
}

mybatis核心配置类
添加userinfo别名,注册自定义转换类





    

    
        
        
    

    
        
    

    
        
            
            
                
                
                
                
            
        
    
    
    
        
        
    

mapper类




    
        insert into userinfo values(#{id},#{username},#{password},#{birthday});
    

测试

    @Test
    public void test31() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserinfoDao userinfoDaoMapper = sqlSession.getMapper(UserinfoDao.class);

        Userinfo userinfo = new Userinfo();
        userinfo.setUsername("lisi");
        userinfo.setPassword("1234");
        userinfo.setBirthday(new Date());
        userinfoDaoMapper.add(userinfo);

        sqlSession.commit();
        sqlSession.close();
    }


查询数据库确认插入成功,date类型被转为varchar

测试查询

    @Test
    public void test32() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserinfoDao userinfoDaoMapper = sqlSession.getMapper(UserinfoDao.class);

        Userinfo userinfo = userinfoDaoMapper.findById(1);

        System.out.println(userinfo);
        sqlSession.close();
    }


可以发现查询结果已经由varchar转为Date类型

关于自定义转换类
package com.jtyhnet.typeHandler;

import org.apache.ibatis.type.baseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

public class MyDateTypeHandler extends baseTypeHandler {
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
        preparedStatement.setString(i,date.getTime()+"");
    }

    @Override
    public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
        return new Date(resultSet.getLong(s));
    }

    @Override
    public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
        return new Date(resultSet.getLong(i));
    }

    @Override
    public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        return callableStatement.getDate(i);
    }
}

  1. 自定义转换类继承baseTypeHandler,其中T是javaType,上述案例中,birthday字段在java中为Date类型,在数据库中为varchar类型,因此继承baseTypeHandler
  2. 继承后需复写4个方法
    setNonNullParameter,用于preparedStatement执行sql时,对参数进行设置,i为参数角标,即第几个参数,上述案例中,java端设置的birthday为Date类型,在执行数据库insert *** 作时,需转为String类型才能插入到数据库Varchar类型字段中,因此,preparedStatement.setString(i,date.getTime()+"");
    getNullableResult(ResultSet resultSet, String s),getNullableResult(ResultSet resultSet, int i),用于将从数据库查询的结果封装为javaBean,即将查询出的String类型转为Date类型,其中s为字段名,i为字段角标。
    getNullableResult(CallableStatement callableStatement, int i)为存储过程使用,i为字段角标。
plugins标签

MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂 *** 作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤:
① 导入通用PageHelper的坐标
② 在mybatis核心配置文件中配置PageHelper插件
③ 测试分页数据获取

        
            com.github.pagehelper
            pagehelper
            3.7.5
        
        
            com.github.jsqlparser
            jsqlparser
            0.9.1
        

核心配置文件中配置分页插件

    
        
            
            
        
    

新建查询所有接口方法

    List findAll();

配置mapper

    
        select *,o.id oid from userinfo u left join orderlist o on o.uid=u.id
    

由于一个用户对应多个订单,此处订单List使用collection标签定义
测试

    @Test
    public void test12() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        OrderUserinfoMapper orderUserinfoMapper = sqlSession.getMapper(OrderUserinfoMapper.class);
        List orderUserinfoList = orderUserinfoMapper.findAll();
        for (OrderUserinfo orderUserinfo : orderUserinfoList) {
            System.out.println(orderUserinfo.getUsername());
            for (Order order : orderUserinfo.getOrderList()) {
                System.out.println(order);
            }
            System.out.println("--------------------------");
        }
        sqlSession.close();
    }

多对多查询

案例:
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色

用户表增加属性角色列表

package com.jtyhnet.domain;


import java.util.List;

public class OrderUserinfo {

    private int id;
    private String username;
    private String password;

    //当前用户下的所有订单
    private List orderList;

    //当前用户所具有的角色
    private List roleList;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public List getOrderList() {
        return orderList;
    }

    public void setOrderList(List orderList) {
        this.orderList = orderList;
    }

    public List getRoleList() {
        return roleList;
    }

    public void setRoleList(List roleList) {
        this.roleList = roleList;
    }

    @Override
    public String toString() {
        return "OrderUserinfo{" +
                "id=" + id +
                ", username='" + username + ''' +
                ", password='" + password + ''' +
                ", orderList=" + orderList +
                ", roleList=" + roleList +
                '}';
    }
}

接口类增加方法

    List findAllUserAndRole();

设置mapper