0
点赞
收藏
分享

微信扫一扫

介绍Mybatis与使用(什么是Mybatis?)

残北 2022-03-24 阅读 89
java后端

一、什么是Mybatis

在介绍Mybatis之前,抛出一个问题,什么是Mybatis呢这里引用Mybatis官网http://www.mybatis.org/mybatis-3/的一段话来介绍什么是Mybatis。

官网的介绍言简意赅,从三个出发点介绍了什么是Mybatis,首先Mybatis是一个优秀的持久化框架,它支持自定义SQL查询、存储过程,和很好的一个映射。第二点Mybatis减少了大部分JDBC的代码,避免了手动设置参数和结果集的映射。第三点Mybatis用简单的XML配置文件或注解来配置映射关系,将接口和POJO对象映射到数据库记录中。

在使用传统JDBC时,我们往往需要写很多JDBC代码,需要自己写SQL语句以及自己装配参数,然后自己对结果集进行封装处理,而Mybatis帮我们简化了以上功能,只需要一些配置文件(xml)或是注解的方式即可完成对数据库的查询以及结果的映射封装。

二、使用Mybatis

在使用Mybatis时有两种方式,一种是编程式,一种是集成Spring,大部分项目中用到的都是集成Spring来使用,这里也讲一下编程式使用Mybatis,可以帮助我们更好的理解底层原理。

1.编程式

首先先配置Mybtis的配置文件,然后配置对应的mapper.xml与mapper接口,写好POJO实体类,然后将Mybatis配置文件作为流传入SqlSessionFactoryBuilder去build一个SqlSessionFactory,然后调用SqlSessionFactory的openSession方法获取到一个SqlSession,从这个Session中可以

Mybatis配置文件

<?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>
    <settings>
        <setting name="logImpl" value="SLF4J"/>
        <!--<setting name="cacheEnabled" value="true" />-->
    </settings>
    <!--<plugins>-->
        <!--<plugin interceptor="com.mybatis.plugins.TestPlugin">-->
        <!--</plugin>-->
    <!--</plugins>-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="admin"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="xml/TestMapper.xml"/>
        <mapper resource="xml/PostsMapper.xml"/>
    </mappers>
</configuration>

这里只需要注意环境配置(配置数据源)mapper配置文件的路径配置

public class Demo {
    public static SqlSession getSqlSession() throws FileNotFoundException {
        //配置文件
        InputStream configFile = new FileInputStream(
                "D:\IdeaProjects\mybatissrc\main\java\com\mybatis\demo\mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configFile);
        //加载配置文件得到SqlSessionFactory
        return sqlSessionFactory.openSession();
    }

    public static void main(String[] args) throws FileNotFoundException {
        TestMapper testMapper = getSqlSession().getMapper(TestMapper.class);
        Test test = testMapper.selectByPrimaryKey(1);
        System.out.println(test);
    }
}

mapper接口声明各种SQL方法,然后在mapper.xml中写出具体SQL,在编程式使用时只需要从SqlSessionFactory中取出SqlSession,然后取出需要用的Mapper,即可完成对数据库的查询与结果的映射。

2.与Spring集成

不同点在于Spring帮我们省去了创建SqlSessionFactory与SqlSession的过程,装配Mapper让我们直接可以调用mapper类做对应的CRUD,其中是因为Spring帮我们去管理SqlSessionFactory(需要注入DataSource),然后要在Spring中配置dao包(mapper接口路径),来完成对mapper接口代理出各个实现类,我们只需要用@Autowired或是其他注入的注解来注入对应的mapper(底层是把扫描到的mapper接口实现的代理类存放在IOC容器中,我们需要用到的时候再去取),即可使用mapper完成对数据库的CRUD,Spring帮助我们对许多类进行解耦。具体的底层原理会在目录的最后一栏进行分析。这里只是简单带过一下Spring集成方式使用Mybatis。

3.使用Generator生成代码

一般在项目开发时都会使用generator插件去自动生成实体类、dao层(mapper接口)、mapper.xml的简单增删改查方法,这可以替我们省去大量重复工作,如有复杂查询再去mapper添加即可。在项目中一般不用到Example,如果是个人快速项目,建议使用Example可以加速项目的开发,如有需要Example需在generatorConfig.xml中

标签中对应属性设为true即可生成Example文件。

首先需要引入mybatis-generator-core.jar包,然后编写generatorConifg.xml配置文件,在配置文件中指定数据源、生成实体、mapper接口、mapper.xml文件生成位置,然后配置数据库中某个表和文件名即可。

然后在一个类中写一个main方法去读取配置文件进行生成对应mapper、pojo、xml。

List<String> warnings = new ArrayList<String>();
        boolean overwrite = false;
        InputStream is= MybatisGenerator.class.getClassLoader().getResource("mybatis/generatorConfig.xml").openStream();
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(is);
        is.close();
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
 
        System.out.println("生成代码成功,以后执行会覆盖掉mapper,pojo,xml 等文件上做的修改");

4.主要类的作用于SCOPE(生命周期)

SqlSessionFactoryBuilder:method ->用于生成SqlSessionFactory,所以为method级别

SqlSessionFactory:application ->用于生成SqlSession,每次查询都要从factory生成SqlSesion,所以为容器级别

SqlSession:request/method ->用于调用对应查询方法,查询结束之后关闭,所以为method级别(也可以认为是线程级)

Mapper:method ->用于对应某个具体的查询方法,查询一次之后消失,所以为method级别

5.Mapper映射查询方法的xml形式与annotation形式对比

两种形式可结合使用,Mapper.xml形式优点在于它与接口分离,即使编写复杂的SQL语句也可以保持很好的可读性,缺点在于会产生大量文件,即一个mapper接口就要生成对应mapper.xml。

而annotation形式的优点在于在mapper接口方法上即可看到sql语句,不需要再创建xml或者再去配置文件编写对应SQL语句,但缺点在于复杂的SQL语句不易维护,可读性很差。

在项目中很少用到annotation形式,mapper.xml是大部分企业项目用到的形式。

6.TypeHandle

Mybatis在底层JDBC操作时,无论是设置一个参数或是从ResultSet结果集中取出一个值,都会用一个typeHandle将取出的值转换成JAVA类型,Mybatis有各种自带的typeHandle,来完成各种类型的映射,我们也可以自定义typeHandle,将数据库中取出来的值做处理放到结果中展示,也可以将参数做处理存放到数据库中,下面展示一个Demo来实现这种功能。

场景:查询数据库时,数据库中的字段值如果为1将其转化成"是",如果为0将其转换成"否"。

存入数据库时,将“是”转换成1,将“否“转换成0

首先自定义一个TypeHandle,实现BaseTypeHandler抽象类(这里使用了模板方法模式,方便了代码的编写,当然也可以实现TypeHandler接口),泛型为String,注解MapperJdbcType(JdbcType.VARCHAR)表示要对数据库类型VARCHAR和JAVA类型为String的字段做处理。实现setNonNullParameter方法,这里会在填充设置了自定义typeHandle的字段时进入这个方法,在方法里对PreparedStatement做处理即可。getNullableResult方法也是同理,对从结果集ResultSet取出的值做处理即可。

@MappedJdbcTypes(JdbcType.VARCHAR)
public class TestTypeHandle extends BaseTypeHandler<String> {
    public TestTypeHandle() {
    }

    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        switch (parameter){
            case "是":
                ps.setString(i, "1");
                return;
            case "否":
                ps.setString(i, "0");
                return;
            default:
                ps.setString(i, parameter);
        }
    }

    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String name = rs.getString(columnName);
        switch (name){
            case "1":
                return "是";
            case "0":
                return "否";
            default:
                return name;
        }
    }

    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getString(columnIndex);
    }

    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex);
    }
}

然后在mapper.xml中配置指定字段使用typeHandle

  <resultMap id="BaseResultMap" type="com.mybatis.beans.Test">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="nums" jdbcType="INTEGER" property="nums" />
    <result column="name" jdbcType="VARCHAR" property="name" typeHandler="com.gupaoedu.mybatis.typeHandle.TestTypeHandle"/>
  </resultMap>

  <insert id="insert" parameterType="com.mybatis.beans.Test">
    insert into test (id, nums, name
      )
    values (#{id,jdbcType=INTEGER}, #{nums,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR,typeHandler=com.gupaoedu.mybatis.typeHandle.TestTypeHandle}
      )
  </insert>

这里我将insert和select使用到的resultMap中的name字段设置了自定义的typeHandler

表内数据如下:

运行测试代码:

TestMapper testMapper = getSqlSession().getMapper(TestMapper.class);

System.out.println(testMapper.selectByPrimaryKey(3));
System.out.println(testMapper.selectByPrimaryKey(1));
Test test1 = new Test();
test1.setId(3);
test1.setName("否");
test1.setNums(3);
testMapper.insert(test1);

可以看到,从数据库查询的1变为了”是“,0变为了”否“,插入时设置为”否“,数据库中存放的为0。

7.Plugin插件原理与使用

先来介绍如何自定义一个plugin插件,首先实现Interceptor接口,实现三个方法,第一个方法intercept会拦截sql执行之前,在这里可以修改Sql语句或是参数,第二个方法plugin固定写Plugin.wrap(target, this);,原理是把我们自定义的这个插件包装起来,用代理模式实现对SQL的处理,第三个方法setProperties是可以取到一些设置的属性,这个属性在mybatis配置文件配置插件时可以设置取出。然后打上注解,说明是在SQL语句执行过程中哪个点进行拦截调用。以下说明摘抄自Mybatis官网。

@Intercepts({@Signature(type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = ParameterHandler.class,
                method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class TestPlugin implements Interceptor {
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        BoundSql boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
        System.out.println(String.format("plugin output sql = %s , param=%s", boundSql.getSql(),boundSql.getParameterObject()));
        return invocation.proceed();
    }

    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    public void setProperties(Properties properties) {

    }
}

然后在Mybatis配置文件中配置

    <plugins>
        <plugin interceptor="com.mybatis.plugins.CustomPlugin">
        </plugin>
    </plugins>

然后写个测试类随便调用一个mapper的查询方法。

控制台打印可以看出来,在插件中的intercept方法拦截到对应的Sql语句。

分页插件的底层原理也是这样,根据分页信息对查询的Sql语句进行拦截,在查询语句后面加上一些查询条件,修改SQL执行语句达到分页的效果。

8.分页

分页的方式分为两种,一种为逻辑分页,一种为物理分页。

逻辑分页(全部查询出来):Mybatis自带逻辑分页查询,底层原理是在数据库中全部查询之后对ResultSet映射结果集的时候进行分页,这种分页感觉是在骗自己,不合理,不建议使用。

物理分页(分页多少查询多少):推荐一个分页插件https://github.com/pagehelper/Mybatis-PageHelper,上面的插件也提到了物理分页的原理,这里不多赘述。推荐使用物理分页。

举报

相关推荐

0 条评论