深入浅出Mybatis技术原理与实战

本文深入探讨了Mybatis技术原理,从JDBC、Hibernate对比引入,详细讲解了Mybatis的配置、映射器、动态SQL、缓存以及与Spring的整合。重点阐述了Mybatis的自动映射、主键回填、动态SQL元素如if、choose、where、foreach等,并介绍了存储过程、分页、预编译和缓存机制,展示了Mybatis在实际开发中的应用与优势。
摘要由CSDN通过智能技术生成


Mybatis简介

JDBC定义了连接数据库的接口规范,每个数据库厂商都会提供具体的实现,JDBC是一种典型的桥接模式。

传统的JDBC编程

  • 获取数据库连接;
  • 操作Connection,打开Statement对象;
  • 通过Statement对象执行SQL,返回结果到ResultSet对象;
  • 关闭数据库资源
public class javaTest {
   
    public static void main(String[] args) throws ClassNotFoundException, SQLException  {
   
        String URL="jdbc:mysql://127.0.0.1:3306/imooc?useUnicode=true&characterEncoding=utf-8";
        String USER="root";
        String PASSWORD="tiger";
        //1.加载驱动程序
        Class.forName("com.mysql.jdbc.Driver");
        //2.获得数据库链接
        Connection conn=DriverManager.getConnection(URL, USER, PASSWORD);
        //3.通过数据库的连接操作数据库,实现增删改查(使用Statement类)
        Statement st=conn.createStatement();
        ResultSet rs=st.executeQuery("select * from user");
        //4.处理数据库的返回结果(使用ResultSet类)
        while(rs.next()){
   
            System.out.println(rs.getString("user_name")+" "
                          +rs.getString("user_password"));
        }

        //关闭资源
        rs.close();
        st.close();
        conn.close();
    }
}

Hibernate与Mybatis

Hibernate建立在POJO和数据库表模型的直接映射关系上。通过POJO我们可以直接操作数据库的数据。相对而言,Hibernate对JDBC的封装程度比较高,我们不需要编写SQL,直接通过HQL去操作POJO进而操作数据库的数据。

Mybatis是半自动映射的orm框架,它需要我们提供POJO,SQL和映射关系,而全表映射的Hibernate只需要提供POJO和映射关系。

Hibernate编程简单,需要我们提供映射的规则,完全可以通过IDE实现,同时无需编写SQL,开发效率优于Mybatis。此外,它提供缓存、级联、日志等强大的功能,

Hibernate与Mybatis区别:

  1. Hibernate是全自动,而Mybatis是半自动。
    Hibernate是全表映射,可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。而Mybatis仅有基本的字段映射,对象数据以及对象实际关系仍然需要通过手写sql来实现和管理。

  2. Hibernate数据库移植性较好。
    Hibernate通过它强大的映射结构和hql语言,大大降低了对象与数据库的耦合性,而Mybatis由于需要手写sql,因此与数据库的耦合性直接取决于程序员写sql的方法,如果sql不具通用性而用了很多某数据库特性的sql语句的话,移植性也会随之降低很多,成本很高。

  3. Hibernate拥有完整的日志系统,Mybatis则欠缺一些。
    Hibernate日志系统非常健全,涉及广泛,包括:sql记录、关系异常、优化警告、缓存提示、脏数据警告等;而Mybatis则除了基本记录功能外,功能薄弱很多。

  4. sql直接优化上,Mybatis要比Hibernate方便很多。
    由于Mybatis的sql都是写在xml里,因此优化sql比Hibernate方便很多,解除了sql与代码的耦合。而Hibernate的sql很多都是自动生成的,无法直接维护sql;写sql的灵活度上Hibernate不及Mybatis。

  5. Mybatis提供xml标签,支持编写动态sql。

Mybatis入门

SqlSessionFactory

使用xml构建SqlSessionFactory,配置信息在mybatis-config.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">
<configuration>
    <!--定义别名-->
    <typeAliases>
        <typeAlias alias="role" type="com.tyson.pojo.Role"/>
    </typeAliases>
    <!--默认使用development数据库构建环境-->
    <environments default="development">
        <environment id="development">
            <!--采用JDBC事务管理-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据库连接信息-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatisdemo?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--定义映射器-->
    <mappers>
        <mapper resource="RoleMapper.xml"/>
    </mappers>
</configuration>
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 SqlSessionFactoryUtil   {
   
    private static SqlSessionFactory sqlSessionFactory = null;
    //类线程锁
    private static final Class CLASS_LOCK = SqlSessionFactoryUtil.class;

    /**
     * 私有化构造器
     */
    private SqlSessionFactoryUtil() {
   }

    public static SqlSessionFactory initSqlSessionFactory() {
   
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
   
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException ex) {
   
            ex.printStackTrace();
        }
        synchronized (CLASS_LOCK) {
   
            if(sqlSessionFactory == null) {
   
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            }
        }
        return sqlSessionFactory;
    }

    public static SqlSession openSqlSession() {
   
        if(sqlSessionFactory == null) {
   
            initSqlSessionFactory();
        }

        return sqlSessionFactory.openSession();
    }
}

SqlSession

SqlSession是接口类,扮演门面的作用,真正处理数据的是Executor接口。在Mybatis中SqlSession的实现类有DefaultSqlSession和SqlSessionManager。

映射器Mapper

映射器的实现方式有两种:通过xml方式实现,在mybatis-config.xml中定义Mapper;通过代码方式实现,在Configuration里面注册Mapper接口。建议使用xml配置方式,这种方式比较灵活,尤其当SQL语句很复杂时。

xml文件配置方式实现Mapper

public interface RoleMapper {
   
    public Role getRole(@Param("id") Long id);
}

映射xml文件RoleMapper.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">
<mapper namespace="com.tyson.mapper.RoleMapper">
    <!--SQL列的别名与pojo的属性一样,则SQL查询的结果会自动映射到pojo-->
    <select id="getRole" parameterType="long" resultType="com.tyson.pojo.Role">
        SELECT id, role_name as roleName, note FROM role WHERE id = #{id}
    </select>
</mapper>

测试类

import com.tyson.mapper.RoleMapper;
import com.tyson.pojo.Role;
import com.tyson.util.SqlSessionFactoryUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

@Slf4j
public class RoleTest {
   
    @Test
    public void findRoleTest() {
   
        SqlSession sqlSession = null;
        try {
   
            sqlSession = SqlSessionFactoryUtil.openSqlSession();
            RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
            Role role = roleMapper.getRole(1L);
            if(role != null) {
   
                 log.info(role.toString());
            }
        } catch (Exception e) {
   
            e.printStackTrace();
        } finally {
   
            if (sqlSession != null) {
   
                sqlSession.close();
            }
        }
    }
}

Java方式实现Mapper

import com.tyson.pojo.Role;
import org.apache.ibatis.annotations.Select;

public interface RoleMapper1 {
   
    @Select(value="SELECT id, role_name as roleName, note from role where id = #{id}")
    public Role getRole(@Param("id") Long id);
}

在Mybatis全局配置文件注册Mapper,有三种方式。

    <!--定义映射器-->
    <!--resource:引入类路径下的资源
        URL:引入网络或磁盘的资源
        class:引用注册接口
        1.有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
        2.没有sql映射文件,所有的sql都是基于注解写在接口上。
    -->
	<mappers>
        <!--方式一:通过映射文件注册 Mapper-->
        <!--<mapper resource="RoleMapper.xml"/>-->

        <!-- 方式二:通过mapper元素的class属性可以指定一个Mapper接口进行注册 -->
        <!-- 基于映射文件的接口。映射文件名必须和接口同名,并且放在与接口同一目录下-->
        <!--<mapper class="com.tyson.mapper.RoleMapper"/>-->
        <!-- 基于注解的接口。没有sql映射文件,所有的sql都是基于注解写在接口上-->
        <!--<mapper class="com.tyson.mapper.RoleMapper1"/>-->

        <!-- 方式三:通过package元素将指定包下面的所有Mapper接口进行注册
        批量注册 :基于映射文件的接口与映射文件必须在同一个包下
        name:包的全类名 -->
        <package name="com.tyson.mapper"/>
    </mappers>

Mybatis组件的生命周期

  1. SqlSessionFactoryBuilder

作用是生成SqlSessionFactory,构建完毕则作用完结,生命周期只存在于方法的局部。

  1. SqlSessionFactory

创建SqlSession,每次访问数据库都需要通过SqlSessionFactory创建SqlSession。故SqlSessionFactory应存在于Mybatis应用的整个生命周期。

  1. SqlSession

会话,相当于JDBC的Connection对象,生命周期为请求数据库处理事务的过程。

  1. Mapper

作用是发送SQL,返回结果或执行SLQ修改数据库数据,它的生命周期在一个SqlSession事务方法之内。其最大的作用范围和SqlSession相同。

Mybatis组件的生命周期

配置

Mybatis全局配置文件mybatis-config.xml的层次结构顺序不能颠倒,否则在解析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">
<configuration>
    <properties/><!--属性-->
    <settings><!--设置-->
        <setting name="" value=""/>
    </settings>
    <typeAliases></typeAliases><!--别名-->
    <typeHandlers></typeHandlers><!--类型处理器-->
    <objectFactory></objectFactory><!--类型工厂-->
    <plugins>
        <plugin interceptor=""></plugin>
    </plugins>
     <!--默认使用development数据库构建环境-->
    <environments default="development">
        <environment id="development">
            <!--采用JDBC事务管理-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据库连接信息-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <databaseIdProvider></databaseIdProvider>
    <!--定义映射器-->
    <mapper></mapper>
</configuration>

properties

<properties resource="db.properties"/>
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatisdemo?serverTimezone=UTC
username=root
password=ad1234

typeAliases

    <!--定义别名-->
    <typeAliases>
        <!--<typeAlias alias="role" type="com.tyson.pojo.Role"/>-->
        <!--自动扫描,默认别名为首字母小写的类名-->
        <package name="com.tyson.pojo"/>
    </typeAliases>

typeHandler

参考自:关于mybatis中typeHandler的两个案例

类型处理器,作用是将参数从javaType转化为jdbcType,或者从数据库取出结果时把jdbcType转化成javaType。typeHandler和别名一样,分为系统定义和用户自定义。Mybatis系统定义的typeHandler就可以实现大部分的功能。

public TypeHandlerRegistry() {
   
        this.register((Class)Boolean.class, (TypeHandler)(new BooleanTypeHandler()));
        this.register((Class)Boolean.TYPE, (TypeHandler)(new BooleanTypeHandler()));
        this.register((JdbcType)JdbcType.BOOLEAN, (TypeHandler)(new BooleanTypeHandler()));
        this.register((JdbcType)JdbcType.BIT, (TypeHandler)(new BooleanTypeHandler()));
        this.register((Class)Byte.class, (TypeHandler)(new ByteTypeHandler()));
        this.register((Class)Byte.TYPE, (TypeHandler)(new ByteTypeHandler()));
		......
}
自定义typeHandler

假如需要将日期以字符串格式(转化成毫秒数)写进数据库,此时可以通过自定义typeHandler来实现此功能。

role表

CREATE TABLE `role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(20) DEFAULT NULL,
  `note` varchar(20) DEFAULT NULL,
  `reg_time` varchar(64) DEFAULT NULL,
  `users` varchar(64) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1

Role实体类

import java.util.Date;
import java.util.List;

public class Role {
   
    private Long id;
    private String roleName;
    private String note;
    private Date regTime;

    //setter和getter
    
    @Override
    public String toString() {
   
        return "id: " + id + ", roleName: " + roleName + ", note: " + note + ", regTime: " + regTime; //+ ", users: " + users.get(0);
    }
}
  1. 首先定义TypeHandler实现类(或继承BaseTypeHandler,BaseTypeHandler是TypeHandler的实现类)。在setNonNullParameter方法中,我们重新定义要写往数据库的数据。 在另外三个方法中我们将从数据库读出的数据进行类型转换。
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

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

@Slf4j
@MappedJdbcTypes({
   JdbcType.VARCHAR})
@MappedTypes({
   Date.class})
public class MyDateTypeHandler extends BaseTypeHandler<Date> {
   

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
   
        log.info("预编译语句设置参数: " + date.toString());
        preparedStatement.setString(i, String.valueOf(date.getTime()));
    }

    @Override
    public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
   
        log.info("由列名 " + s + " 获取字符串:" + resultSet.getLong(s));
        return new Date(resultSet.getLong(s));
    }

    @Override
    public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
   
        log.info("由下标 " + i + " 获取字符串:" + resultSet.getLong(i));
        return new Date(resultSet.getLong(i));
    }

    @Override
    public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
   
        log.info("通过callbleStatement下标获取字符串");
        return callableStatement.getDate(i);
    }
}
  1. 在mybatis-config.xml中国注册自定义的typeHandler。单独配置或者扫描包的形式。注册之后数据的读写会被这个类过滤。
    <!--类型处理器-->
    <typeHandlers>
    <!--<typeHandler jdbcType="VARCHAR" javaType="java.util.Date" handler="com.tyson.typeHandler.MyDateTypeHandler"/>-->
        <!--扫描包-->
        <package name="com.tyson.typeHandler"/>
    </typeHandlers>
  1. 在RoleMapper.xml编写SQL语句,resultMap中的转换字段需要指定相应的jdbcType和javaType,启动我们自定义的typeHandler,本例中应设置reg_time字段jdbcType为VARCHAR,对应的regTime属性javaType设置为java.util.Date,与MyDateTypeHandler处理类型匹配,所以对reg_time字段的读取会先经过MyDateTypeHandler的处理。插入的时候不会启用我们自定义的typeHandler,需要在insert标签配置typeHandler(或指定jdbcType和javaType)。
<?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.tyson.mapper.RoleMapper">
    <resultMap id="roleMap" type="role">
        <id column="id" property="id" javaType="long" jdbcType="BIGINT"/>
        <result column="role_name" property="roleName" javaType="string" jdbcType="VARCHAR"/>
        <result column="note" property="note" javaType="string" jdbcType="VARCHAR"/>
        <result column="reg_time" jdbcType="VARCHAR" property="regTime" javaType="java.util.Date"/>
    </resultMap>
    
    <!--使用resultMap进行结果映射, 用typeHandler对reg_time字段进行转化-->
    <select id="getRole" parameterType="long" resultMap="roleMap">
        SELECT id, role_name, note, reg_time FROM role WHERE id = #{id}
    </select>
    
    <!--取regTime值也可以只配置typeHandler,即#{regTime,typeHandler=com.tyson.typeHandler.MyDateTypeHandler}-->
    <insert id="insertRole" parameterType="role">
        INSERT into role(id, role_name, note, reg_time)
        VALUES(#{id}, #{roleName}, #{note}, #{regTime,javaType=Date, jdbcType=VARCHAR})
    </insert>
</mapper>
枚举类型typeHandler

Mybatis内部提供了两个转化枚举类型的typeHandler:org.apache.ibatis.type.EnumTypeHandler和org.apache.apache.ibatis.type.EnumOrdinalTypeHandler。EnumTypeHandler使用枚举字符串名称作为参数传递,EnumOrdinalTypeHandler使用整数下标作为参数传递。

下面通过EnumOrdinalTypeHandler实现性别枚举。

public enum  Sex {
   
    MALE(1, "男"),  FEMALE(2, "女");

    private int id;
    private String name;

    private Sex(int id, String name) {
   
        this.id = id;
        this.name = name;
    }

    public static Sex getSex(int id) {
   
        if(id == 1) {
   
            return MALE;
        } else if(id == 2) {
   
            return FEMALE;
        } else {
   
            return null;
        }
    }

    //setter和getter
}

StudentMapper.java

public interface StudentMapper {
   
    public Student findStudent(int id);
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis是一个流行的Java持久化框架,它提供了一种简单且灵活的方式来访问数据库。MyBatis的配置文件对整个框架的使用产生深远的影响,因此我们需要认真学习它。[1]配置文件中包含了数据库连接信息、映射器配置、SQL语句等重要内容,通过配置文件可以实现对数据库的增删改查操作。 除了配置文件,MyBatis最强大的工具之一是映射器。映射器是用于定义SQL语句和Java方法之间映射关系的工具,我们在使用MyBatis时会经常使用到它。[1]映射器可以将数据库表的字段映射到Java对象的属性上,使得我们可以方便地进行对象与数据库之间的转换。 在实际工作中,我们经常会遇到一些特殊的场景,需要灵活运用MyBatis来解决问题。比如处理数据库的BLOB字段的读写、批量更新、调用存储过程、分页、使用参数作为列名、分表等等。[2]这些场景都是通过实战总结出来的,具有较强的实用价值,可以帮助我们更好地应对实际开发中的需求。 此外,MyBatis和Spring框架的结合也是非常常见的。Spring框架是Java世界最流行的IOC和AOP框架之一,而MyBatis和Spring的结合可以构建高性能的大型网站。[3]通过使用Spring MVC和MyBatis,我们可以充分发挥它们的优势,实现灵活可配置的SQL操作,从而构建高性能的Java互联网应用。 总结起来,深入浅出地学习MyBatis技术原理实战经验,可以帮助我们更好地理解和应用这个持久化框架,提高开发效率和代码质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值