MyBatis的学习总结

Mybatis

      • 1、前言
      • 2、下载mybatis
      • 3、在java工程中导入mybatis框架
      • 4、快速上手MyBatis的使用
      • 5、深入了解MyBatis全局配置
      • 6、深入了解XML映射器

1、前言

  • mybatis是一款优秀的持久层框架, 它支持定制化SQL、存储过程、高级映射、逆向工程…
  • mybatis可以使用简单XML或注解用于配置和原始映射,将接口和java的pojo映射成数据库中的记录

为什么要使用MaBatis?
mybatis是一款半自动化的持久层框架

在JDBC中,我们在Java代码中夹杂着大量SQL语句,耦合度高,在我们实际维护的过程中总是需求的sql是有变化的,频繁修改的情况多见

而mybatis框架帮助我们在代码中实现java与sql的分离,一个专注业务,一个专注数据

2、下载mybatis

2.1、找到GitHub的mybatis页面https://github.com/mybatis/mybatis-3
2.2、在Essentials中点击下载最新版
MyBatis的学习总结_第1张图片
2.3、在Assets中下载mybatis的压缩包
MyBatis的学习总结_第2张图片

2.4、解压文件后,mybatis-3.5.7.jar就是我们需要导入java中的包
MyBatis的学习总结_第3张图片

3、在java工程中导入mybatis框架

3.1、创建模块,在lib目录下导入3个包分别是: log4j-1.2.17.jar(这个包在mybatis解压后的lib目录中)、mybatis-3.5.7.jar(mybatis核心包)、mysql-connector-java-5.1.7bin.jar(数据库连接包)

MyBatis的学习总结_第4张图片
3.2、我们需要使用到日志功能在控制台上显示mybatis的信息,这里需要在src的根目录下创建log4j.xml文件,配置信息如下


DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 
 <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
   <param name="Encoding" value="UTF-8" />
   <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m  (%F:%L) \n" />
   layout>
 appender>
 <logger name="java.sql">
   <level value="debug" />
 logger>
 <logger name="org.apache.ibatis">
   <level value="info" />
 logger>
 <root>
   <level value="debug" />
   <appender-ref ref="STDOUT" />
 root>
log4j:configuration>

4、快速上手MyBatis的使用

(以在数据库中添加一条记录为例,帮助理解mybatis的作用)

4.1、在src目录下分为几个目录
dao: 创建对数据库进行持久层的操作的接口、以及对接口实现sql语句的映射xml文件
pojo: 创建Java对于数据库表的映射关系对象
test: 用于测试CRUD功能
MyBatis的学习总结_第5张图片
4.2、我们以book数据库的t_user数据表为例
MyBatis的学习总结_第6张图片
创建数据表的DDL语句

CREATE TABLE `t_user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL,
  `password` varchar(20) NOT NULL,
  `email` varchar(200) NOT NULL,
  `status` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`),
  UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

4.3、创建Java对于数据库关系映射对象User类
(User类中要生成GET/SET、toString, 由于太长了这里就不放在文章中)

public class User {
    private Integer id;
    private String username;
    private String password;
    private String email;
    private String status;

    public User(Integer id, String username, String password, String email, String status) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.email = email;
        this.status = status;
    }

    public User() {
    }
}

4.4、创建对数据库进行持久层的操作UseMapper接口
(以插入数据为例,所以我们只些一条添加方法即可)

public interface UserMapper {
    int addUser(User user);
}

4.5、在conf目录中编写连接池信息jdbc.properties

mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/book?useUnicode=true&characterEncoding=utf8
mysql.username=root
mysql.password=1234

4.6、在conf目录中编写mybatis全局配置文件(重要)
(后面我们会一一分析全局配置文件的标签与属性)


DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    
    <properties resource="conf/jdbc.properties">properties>

    
    <environments default="mysql">
        
        <environment id="mysql">
            
            <transactionManager type="JDBC">transactionManager>
            
            <dataSource type="POOLED">
                <property name="driver" value="${mysql.driver}"/>
                <property name="url" value="${mysql.url}"/>
                <property name="username" value="${mysql.username}"/>
                <property name="password" value="${mysql.password}"/>
            dataSource>
        environment>
    environments>

    
    <mappers>
        <mapper resource="com/gaipian/mybatis/dao/UserMapper.xml"/>
    mappers>
configuration>

4.7、在dao目录中编写UserMapper.xml映射器文件(重要)
(我们这里以插入语句为例,让我们体会mybatis让Java代码与数据分离的效果)


DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.gaipian.mybatis.dao.UserMapper">
    
    <insert id="addUser" useGeneratedKeys="true" keyProperty="id" parameterType="com.gaipian.mybatis.pojo.User">
        
        insert into t_user (`id`,`username`,`password`,`email`,`status`)values(#{id},#{username},#{password},#{email},#{status})
    insert>
mapper>

4.8、我们在test目录中创建一个测试方法
(目的是在数据表中插入一条记录)

public class MyBatisTest {

    @Test
    public void test01() throws FileNotFoundException {
        //MyBatis都是以SqlSessionFactory为核心,这里我们将全局配置文件引入到SqlSessionFactoryBuilder().build()解析,获得SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream("src/conf/mybatis-config.xml"));
        //从SqlSessionFactory中获取SqlSession实例
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            //使用和指定语句的参数和返回值相匹配的接口,底层使用了动态代理的技术获取实例
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //往数据库中添加一条记录
            userMapper.addUser(new User(null,"aab678","345gh","[email protected]","200"));
            //在操作完后并提交给数据库
            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.9、运行后的结果
控制台上打印出了日志信息: 更新了一条记录,并展示预编译、设置的参数内容
在这里插入图片描述
在数据表中添加了一条记录
在这里插入图片描述

5、深入了解MyBatis全局配置

MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息

配置文件的由上至下的标签配置顺序

5.1、属性(properties)

  • 这些属性可以在外部进行配置并动态的进行替换,你可以再SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);传递这些属性
  • 也可以在properties元素的子元素中配置
	
    <properties resource="conf/jdbc.properties">
        <property name="mysql.username" value="abcd"/>
        <property name="mysql.password" value="1236"/>
    properties>

设置好的属性可以再整个配置文件中配置能够动态配置的属性值,如:

    <dataSource type="POOLED">
        <property name="driver" value="${mysql.driver}"/>
        <property name="url" value="${mysql.url}"/>
        <property name="username" value="${mysql.username}"/>
        <property name="password" value="${mysql.password}"/>
    dataSource>

这个例子的driver、url、username、password属性都会由引入的jdbc.properties文件来设置

如果一个属性不止在一个地方进行了配置,那么它将由如下顺序加载:

  • 首先读取properties元素的子元素属性
  • 然后读取properties元素的resource根据类路径引入或url从指定路径读取的属性,并覆盖之前读取过同名的属性值
  • 最后读取作为方法传递的属性值,且覆盖之前读取过同名的属性值

因此properties的优先级为: 方法传参 > resource/url外部引入文件 > properties的子元素属性

5.2、设置(settings)
settings中几个重要的属性配置,如要使用,可以显示的配置属性

设置 描述 默认值
cacheEnabled 全局性地开启或关闭配置文件的任何缓存 true
useGeneratedKey 允许JDBC自动生成主键 false
defaultExecutorType 配置默认的执行器 SIMPLE
jdbcTypeForNull 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型 OTHER
defaultEnumTypeHandler 指定Enum使用默认的TypeHandler TypeHandler

5.3、类型别名(typeAlias)
类型别名可以为Java类型设置一个缩写名字,意在降低冗余的全类名书写

    <typeAliases>
    
        <typeAlias type="com.gaipian.mybatis.pojo.User" alias="user"/>
        
        <package name="com.gaipian.mybatis.pojo"/>
    typeAliases>

每个在com.gaipian.mybatis.pojo包中的JavaBean在没有注解的情况下,会使用当前类名首字母小写后作为它的别名
若类中使用了@Alias(“xxx”)起了别名,则使用这个别名作为当前类,例如:

@Alias("user")
public class User {
}

5.4、类型处理器(typeHandler)
MyBatis在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换为Java类型,类型处理器有很多,这里我们讲下如何自定义类型处理器

你可以重写已有的类型处理器或创建新的类型处理器来处理标准或非标准的类型,只需要实现org.apache.ibatis.type.TypeHandler接口即可或者也可以继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler并且将它配置到全局配置中,如:

public class MyTypeHandler extends BaseTypeHandler<String> {
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
        preparedStatement.setObject(i,s);
    }

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

    @Override
    public String getNullableResult(ResultSet resultSet, int i) throws SQLException {
        return resultSet.getString(i);
    }

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

配置到全局配置中

    <typeHandlers>
        <typeHandler handler="com.gaipian.mybatis.typehandler.MyTypeHandler"/>
    typeHandlers>

使用上述的类型处理器将会覆盖String类型的属性以及VARCHAR类型的参数和结果的类型处理器

5.5、对象工程(objectFactory)
每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现

// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
  public Object create(Class type) {
    return super.create(type);
  }
  public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
    return super.create(type, constructorArgTypes, constructorArgs);
  }
  public void setProperties(Properties properties) {
    super.setProperties(properties);
  }
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }}

<objectFactory type="org.mybatis.example.ExampleObjectFactory">
  <property name="someProperty" value="100"/>
objectFactory>

5.6、插件(plugins)
MyBatis允许映射语句在执行过程中的某一点进行拦截调用,MyBatis允许使用插件拦截来调用的类包括:

  • Executor
  • ParameterHandler
  • ResultSetHandler
  • StatementHandler

我们可以通过查看MyBatis这些类中的源码来查看类中的方法执行细节
通过MyBatis提供的强大机制,使用插件非常简单,只需实现Intercepteor接口,并指定要拦截方法的签名即可

@Intercepts(
        //Signature需要有三个参数: 拦截的类对象、拦截的方法、拦截方法的参数类型
        @Signature(type = Executor.class,method = "update",args = {MappedStatement.class,Object.class})
)
public class MyFirstPlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("MyFirstPlugin...获取到拦截目标对象:"+invocation.getTarget());
        System.out.println("MyFirstPlugin...获取到拦截目标方法:"+invocation.getMethod());
        System.out.println("MyFirstPlugin...获取到拦截目标方法的参数:"+ Arrays.toString(invocation.getArgs()));
        //invocation.proceed()执行拦截的方法,能够在方法前后添加许多操作
        Object proceed = invocation.proceed();
        return proceed;
    }

    @Override
    public Object plugin(Object target) {
        //通过Plugin类的静态方法wrap获取动态代理对象
        Object wrap = Plugin.wrap(target, this);
        return wrap;
    }

    @Override
    public void setProperties(Properties properties) {
        System.out.println("获取的属性: "+properties);
    }
}

然后在全局配置中注册Plugin

    <plugins>
        <plugin interceptor="com.gaipian.mybatis.interceptor.MyFirstPlugin">
            
            <property name="key123" value="val123"/>
        plugin>
    plugins>

输出到控制台的结果
MyBatis的学习总结_第7张图片
上述插件会将拦截在Executor中的update方法调用,这里的Executor负责执行底层映射语句的对象


5.7、环境变量(Environments)
MyBatis可以配置成适应多种环境,这种机制有助于SQL映射应用与不同的数据库之中,现实情况下有多种理由这么做,如: 开发生成环境要有不同的配置,或想在不同的数据库厂商之间切换配置
不过要记住: 尽可能配置多个不同的环境, 但每个SqlSessuionFactory实例只能选择一种环境

environment元素定义了如何配置环境

        
        <environment id="mysql">
            
            <transactionManager type="JDBC">transactionManager>
            
            <dataSource type="POOLED">
                <property name="driver" value="${mysql.driver}"/>
                <property name="url" value="${mysql.url}"/>
                <property name="username" value="${mysql.username}"/>
                <property name="password" value="${mysql.password}"/>
            dataSource>
        environment>

注意一些关键点:

  • 默认使用的环境ID (例如:default=mysql)
  • 每个 environment 元素定义的环境 ID
  • 事务管理器的配置
  • 数据源的配置

5.7.1、事物管理(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type=“[JDBC|MANAGED]”):

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。而是让容器来管理事物的整个生命周期

5.7.2、数据源(dataSource)
dataSource使用标准的JDBC数据源接口来配置JDBC连接配置信息

有三种内建的数据源类型(type=“[POOLED|UNPOOLED|JNDI]”)

  • UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形
  • POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
  • JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。

这里面常用的配置数据源的属性:
1、driver – 这是 JDBC 驱动的 Java 类全限定名
2、url – 这是数据库的 JDBC URL 地址。
3、username – 登录数据库的用户名。
4、password – 登录数据库的密码。
5、defaultTransactionIsolationLevel – 默认的连接事务隔离级别。

记得在MyBatis中每个希望setter方法增加对应的属性以下是可以连接至MySql数据库的例子:

<dataSource type="POOLED">
    <property name="driver" value="${mysql.driver}"/>
    <property name="url" value="${mysql.url}"/>
    <property name="username" value="${mysql.username}"/>
    <property name="password" value="${mysql.password}"/>
dataSource>

5.8、数据库厂商标识(databaseIdProvider)

MyBatis可以根据不同数据库厂商来选择不同的语句,这种多厂商功能的支持是基于映射语句中的databaseId属性,MyBatis会加载带有databaseId属性与不带databaseId属性的语句,而后者会被丢弃,为了支持MyBatis的多厂商功能,只需要在mybatis-config.xml中添加databaseIdProcider元素即可

databaseIdProvider 对应的 DB_VENDOR 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName() 返回的字符串,由于通常情况下这些字符串都非常长,而且相同产品的不同版本会返回不同的值,你可能想通过设置属性别名来使其变短:

<databaseIdProvider type="DB_VENDOR">
    <property name="MySQL" value="mysql"/>
    <property name="Sql Server" value="sqlserver"/>
    <property name="DB2" value="db2"/>
    <property name="Oracle" value="oracle"/>
databaseIdProvider>

在提供了属性别名时,databaseIdProvider 的 DB_VENDOR 实现会将 databaseId 设置为数据库产品名与属性中的名称第一个相匹配的值,如果没有匹配的属性,将会设置为 “null”。 在这个例子中,如果 getDatabaseProductName() 返回“Oracle (DataDirect)”,databaseId 将被设置为“oracle”。

5.9、映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。
在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。
你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:


<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
mappers>

<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
mappers>

<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
mappers>

<mappers>
  <package name="org.mybatis.builder"/>
mappers>

6、深入了解XML映射器

MyBatis真正强大的在于它的语句映射,这是它的魔力所在

SQL映射器文件只有很少的几个顶级元素(按照被定义的顺序列出):

  • cacha - 该命名空间的缓存配置
  • cacha-ref - 引用其他命名空间的缓存配置
  • resultMap - 描述如何从结果集中加载对象,是最复杂也最强大的元素
  • sql - 可被其他语句引用可重用的语句块
  • insert - 映射插入语句
  • delete - 映射删除语句
  • update - 映射更新语句
  • select - 映射查询语句

6.1、select (查询)
查询语句是MyBatis最常用的语句之一,光是能往数据库存储元素是不行的,还得把数据库的元素取出,多数应用也都是查询比更新操作更频繁,一个简单的select查询语句,例如:

    <select id="queryUserById" parameterType="Integer" resultType="com.gaipian.mybatis.pojo.User">
        select `id`,`username`,`password`,`email`,`status` from t_user where id=#{id}
    select>

这个语句名为queryUserById是获取User对象的数据访问接口的queryUserById()方法并接收一个int或Integer类型的参数,结果获取后返回一个HashMap类型的对象键为列名、值便是结果行对应的值

select元素允许你配置许多属性影响语句的行为:

<select id="queryUserById"
  resultType="com.gaipian.mybatis.pojo.User"
  parameterType="Integer"
  databaseId="mysql"
  fetchSize="256"
  flushCache="true"
  useCache="true"
  resultSetType="FORWARD_ONLY"
  timeout="10" >
select>

select语句的几个重要参数

属性 描述
id 在命名空间中的唯一标识,可被用来引用这条语句
parameterType 将会传入这条语句的参数的类全限定名或别名
resultType 期待这条语句中返回的结果的类全限定名或别名
resultMap 对外部resultMap的命名引用,结果映射是MyBaits最强大的特性,resultType与resultMap在一条语句中在二选一
flushCache 设置为true后,只要语句被调用就会清空本地缓存与二级缓存
useCache 设置为true后,只要语句执行就会被缓存进二级缓存中

6.2、insert、update、delete(插入、更新、删除)
数据变更语句insert、update、delet的实现非常接近:

<insert
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  keyProperty=""
  keyColumn=""
  useGeneratedKeys=""
  timeout="20">

<update
  id="updateAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

<delete
  id="deleteAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

insert、update、delete的几个重要的属性列表:

属性 描述
id 在命名空间中的唯一标识,可被用来引用这条语句
useGeneratedKey (仅限insert与update使用)这会令MyBatis调用JDBC的getGeneratedKey来取出数据库内部生成的主键
keyProperty (仅限insert与update使用)指定能够唯一识别对象的属性
keyColumn (仅限insert与update使用)设置在表中生成主键的列名,如果主键列不是在表中的第一列,是必须设置的

下面是insert、update、delete语句的示例:

    
    <insert id="addUser" parameterType="com.gaipian.mybatis.pojo.User">
        insert into t_user (`id`,`username`,`password`,`email`,`status`)values(#{id},#{username},#{password},#{email},#{status})
    insert>
    <update id="updateUser" parameterType="user">
        update t_user set `username`=#{username},`password`=#{password},`email`=#{email},`status`=#{status} where id=#{id}
    update>
    <delete id="deleteUserById" parameterType="Integer">
        delete from t_user where id=#{id}
    delete>

如果你的数据库支持自动生成主键(如: MySQL、SQL Server), 那么你可以设置useGeneratedKey为true,在把keyProperty设置为目标属性即可

如上面的User表的id已设置了自动生成主键, 例如将insert语句修改为:

<insert id="addUser" useGeneratedKeys="true" keyProperty="id" parameterType="com.gaipian.mybatis.pojo.User">
    insert into t_user (`id`,`username`,`password`,`email`,`status`)values(#{id},#{username},#{password},#{email},#{status})
insert>
    @Test
    public void insertTest() throws FileNotFoundException {
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream("src/conf/mybatis-config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User user = new User(null, "cqq124", "cqq124", "[email protected]", "200");
            userMapper.addUser(user);
            //映射器中的插入语句的属性列表设置了userGeneratedKey="true"与keyProperty="id",插入的语句会返回一个主键值给id
            System.out.println(user.getId()); //返回回来的主键值: 47
            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

6.3、sql
这个语句可以用来定义重用的sql语句片段,以便在其他语句中使用,参数可以静态地确定下来,并在include元素中定义不同的参数

<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password sql>
<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns"><property name="alias" value="t1"/>include>,
    <include refid="userColumns"><property name="alias" value="t2"/>include>
  from some_table t1
    cross join some_table t2
select>

6.4、参数
之前MyBatis的SQL语句都使用了简单的参数,对于大多数简单的场景,你都不需用使用复杂的参数,例如:

    <select id="queryUserById" parameterType="Integer" resultType="com.gaipian.mybatis.pojo.User">
        select `id`,`username`,`password`,`email`,`status` from t_user where id=#{id}
    select>

上面示例展示了一个非常简单的命名参数映射,鉴于参数类型会(parameterType)被自动设置int,这里可以省略设置,然而对于复杂的参数行为就不同了,例如:

    <insert id="addUser" useGeneratedKeys="true" keyProperty="id" parameterType="com.gaipian.mybatis.pojo.User">
        insert into t_user (`id`,`username`,`password`,`email`,`status`)
        values(#{id},#{username},#{password},#{email},#{status})
    insert>

对于传递参数来说,可以干的远远不止这些,参数也可以指定Java的类型与JDBC的类型

#{username,javaType=String,jdbcType=VARCHAR}

和MyBatis的其他部分一样,可以根据参数对象类型确定javaType,除非类型是HashMap,你需要显示的指定javaType来确保使用正确的类型处理器

如果一个列允许使用null值,并且会使用值为null的参数,就必须要指定JDBC类型(jdbcType)。

要更进一步的指定类型处理器的方式可以指定自定义的类型处理器或别名:

#{username,javaType=String,jdbcType=VARCHAR,typeHandler=MyStrTypeHandler}

对于数值类型,还可以设置 numericScale 指定小数点后保留的位数。

#{height,javaType=double,jdbcType=INTEGER,numericScale=2}

尽管上面这些选项很强大,但大多时候,你只须简单指定属性名,顶多要为可能为空的列指定 jdbcType,其他的事情交给 MyBatis 自己去推断就行了。

6.5、两种不同的参数语法
默认情况下,使用#{xxx}参数时,MyBatis会创建PreparedStatement参数占位符?, 并且参数占位符安全的设置参数,在JDBC的SQL语句填充参数时候我们发现只有?也就是参数才能填充,但有时就是想在SQL语句中直接填充一个不转义的字符串,这时候可以用${xx}:

select `id`,`username`,`password`,`email`,`status` from t_user where ${column}=#{value}
User queryUserForColumn(@Param("column") String column,@Param("value") Object value);

其中${column}会被直接替换,而#{value}会使用?预处理,然后给参数处理器填充参数

用这种方式接受用户的输入,并用作语句参数是不安全的,会导致潜在的 SQL 注入攻击。因此,要么不允许用户输入这些字段,要么自行转义并检验这些参数。

6.6、结果映射 (resultMap)
resultMap元素是MyBatis中最重要最强大的元素,它可以让你从90%的JDBCResultSets数据提取代码中解放出来,resultMap的设计思想是对简单的语句做到零配置,对复杂的语句只需要描述语句之间的关系即可

简单来说,有时候数据库的列名与JavaBean的属性名不对应,查询时会出现报错,现在你可以在resultMap元素中配置JavaBean与数据库的名称关系与其他一些参数

在映射器中给一个JavaBean使用resultMap

<resultMap id="userResult" type="user">
  <id property="id" column="id" javaType="Integer" jdbcType="INTEGER"/>
  <result property="username" column="username" javaType="String" jdbcType="VARCHAR"/>
  <result property="password" column="password" javaType="String" jdbcType="VARCHAR"/>
  <result property="email" column="email" javaType="String" jdbcType="VARCHAR"/>
  <result property="status" column="status" javaType="String" jdbcType="VARCHAR"/>
resultMap>

然后在引用resultMap的语句中设置

<select id="queryUserById" parameterType="Integer" resultMap="userResult">
  select `id`,`username`,`password`,`email`,`status` from t_user where id=#{id}
select>

resultMap元素有很多子元素,下面是resultMap的子元素描述

  • constructor - 用于在实例化时,注入结果到构造器中
    • idArg - ID参数,标记出主键有助于提升性能
    • arg - 将被注入到构造方法的普通结果
  • id - ID参数,标记出主键有助于提升性能
  • result - 注入到字段或属性的普通结果
  • association - 一个复杂类型的结果关联,如:自定义类型
  • collections - 一个复杂的类型集合,结果可以是resultMap或者其他结果映射的引用
  • discriminator – 使用结果值来决定使用哪个 resultMap
    • case – 基于某些值的结果映射

ResultMap的属性列表

属性 描述
id 当前命名空间的唯一标识,用于标识结果映射
type 要映射的JavaBean对象的全类名
autoMapping 自动映射,配置覆盖全局配置中autoMappingBehavior设置

在resultMap元素中子元素id与result有什么不同?

<resultMap id="userResult" type="user">
  <id property="id" column="id" javaType="Integer" jdbcType="INTEGER"/>
  <result property="username" column="username" javaType="String" jdbcType="VARCHAR"/>
resultMap>

id和result都是映射单列值到一个属性或字段的简单数据类型。
唯一不同的是,id代表resultMap的主键,id是作为唯一标识的,当和其他对象实例对比的时候,这个id很有用,尤其是应用到缓存和内嵌的结果映射。

id和result元素的属性列表

属性 描述
property 映射到结果的字段或属性
column 数据库的列名
javaType 属性的类型全类名或别名
jdbcType JDBC类型,对照Java对象类型与JDBC类型的转换
typeHandler 类型处理器

下面是一部分的Java类型与JDBC类型对照
MyBatis的学习总结_第8张图片

6.7、构造方法 (constructor)
MyBatis的resultMap支持通过构造方法完成参数注入,所以resultMmap提供了constructor元素供你完成构造方法注入属性

<resultMap id="userConst" type="user">
  <constructor>
    <idArg  name="id" column="id" javaType="Integer" jdbcType="INTEGER"/>
    <arg name="username" column="username" javaType="String" jdbcType="VARCHAR"/>
    <arg name="password" column="password" javaType="String" jdbcType="VARCHAR"/>
    <arg name="email" column="email" javaType="String" jdbcType="VARCHAR"/>
    <arg name="status" column="status" javaType="String" jdbcType="VARCHAR"/>
  constructor>
resultMap>

constructor的属性列表

属性 描述
name 构造方法的形参名
column 数据库的列名
javaType 属性的类型全类名或别名
jdbcType JDBC类型,对照Java对象类型与JDBC类型的转换
typeHandler 类型处理器

6.8、关联 (association)

<association property="author" column="blog_author_id" javaType="Author">
  <id property="id" column="author_id"/>
  <result property="username" column="author_username"/>
association>

关联(association)元素处理“有一个”类型的关系。 比如,在我们的示例中,一个博客有一个用户。关联结果映射和其它类型的映射工作方式差不多。 你需要指定目标属性名以及属性的javaType(很多时候 MyBatis 可以自己推断出来),在必要的情况下你还可以设置 JDBC 类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。

association的属性列表

属性 描述
property 映射到结果列的列名或属性名
javaType 属性的类型全类名或别名
jdbcType JDBC类型,对照Java对象类型与JDBC类型的转换
typeHandler 类型处理器

关联嵌套select查询

属性 描述
column 数据库中的列名
select 用于加载复杂映射的select语句的id
示例:
<resultMap id="blogResult" type="Blog">
  <association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
select>

<select id="selectAuthor" resultType="Author">
  SELECT * FROM AUTHOR WHERE ID = #{id}
select>

关联的嵌套结果映射
创建一个JavaBean对象名为Order,属性中有个自定义类型为User

public class Order {
    private Integer orderId;
    private Date createTime;
    private BigDecimal price;
    private Integer status;
    private User user;
}

在数据库中创建一个Order表,并且设置user_id字段的外键为User表中的id

CREATE TABLE `t_order` (
  `order_id` varchar(50) NOT NULL,
  `create_time` datetime DEFAULT NULL,
  `price` decimal(11,2) DEFAULT NULL,
  `status` int DEFAULT NULL,
  `user_id` int DEFAULT NULL,
  PRIMARY KEY (`order_id`),
  KEY `user_id` (`user_id`),
  CONSTRAINT `t_order_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`)
);

如果要通过user_id字段查询到Order与User,你可以这样设置:

<resultMap id="orderResult" type="com.gaipian.mybatis.pojo.Order">
  <id property="orderId" column="order_id">id>
  <result property="createTime" column="create_time">result>
  <result property="price" column="price">result>
  <result property="status" column="status">result>
  <association property="user" javaType="user" >
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="password" column="password"/>
    <result property="email" column="email"/>
    <result property="status" column="status"/>
  association>
resultMap>

关联的嵌套结果映射中可用的属性列表

属性 描述
resultMap 引用外部resultMap
columnPrefix 当连接多个表时,你不得不起别名来区分不同表相同名称的字符,而columnPrefix能为你当前的association设置前缀

你可能感兴趣的:(mybatis,jdbc,java,java,后端,mybatis)