MyBatis

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。

MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

1、核心配置文件

resources/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>
	<!--环境配置-->
    <environments default="yan">
    	<!--某个环境配置-->
        <environment id="yan">
        	<!--事务管理器,jdbc/manager -->
            <transactionManager type="JDBC"/>
            <!--数据源配置,unpooled/pooled/jndi-->
            <dataSource type="POOLED">
            	<!-- 创建连接的基本配置 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///test?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
                <!-- 还有连接池的其它配置参数-->
            </dataSource>
        </environment>
    </environments>
    <!-- 注册映射元文件-->
    <mappers>
        <mapper resource="com/yan/dao/UserMapper.xml"/>
    </mappers>
</configuration>

2、创建表

  • 分析需求
  • 定义模式
  • 规范化
drop database test;

create database test default character set utf8;

use test;

create table t_users(
    id bigint primary key auto_increment,
    username varchar(20) not null unique,
    password varchar(20) not null,
    birth datetime default now(),
    sex boolean default 1,
    salary numeric(8,2)
)engine=innodb default charset utf8;

3、根据表结构定义对应的实体类

@Data
public class UserBean implements Serializable {
    private Long id;
    private String username;
    private String password;
    private Date birth;
    private Boolean sex;
    private Double salary;
}

4、定义映射元文件

一般名称方式为【表名/实体类名-mapper.xml】

resources/com/yan/mapper/UserMapper.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.yan.dao.UserMapper">

</mapper>

5、为了保留测试代码,所以引入 junit—回归测试\单元测试

添加依赖

<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
 </dependency>

测试类

test/java目录下

public class UserMapperTest {
    private SqlSessionFactory factory;
    @Before  //声明bb方法是在每个test方法执行之前执行
    public void bb()throws Exception{
        Reader r= Resources.getResourceAsReader("mybatis-config.xml");
        factory=new SqlSessionFactoryBuilder().build(r);
    }
    @Test  //测试方法
    public void test1(){}
}

插入数据

MyBatis不会生成sql语句,所以任何操作都需要在映射元文件种定义相关的sql语句

<?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.yan.dao.UserMapper">
    <insert id="save" parameterType="com.yan.entity.UserBean">
        insert into t_users(username,password) values(#{username},#{password})
    </insert>
</mapper>

然后在接口中定义对应的方法声明

public interface UserMapper {
    int save(UserBean user);
}

单元测试

public class UserMapperTest {
    private SqlSessionFactory factory;
    @Before
    public void bb()throws Exception{
        //读取mybatis-config.xml的字符流
        Reader r= Resources.getResourceAsReader("mybatis-config.xml");
        //创建SQLSessionFactory对象
        factory=new SqlSessionFactoryBuilder().build(r);
    }
    @Test
    public void testSave(){
        //创建SqlSession对象。SqlSession充当实体管理器功能,提供最基本的CRUD操作方法
        SqlSession session=factory.openSession();
        //获取Mapper接口的代理对象
        UserMapper userMapper=session.getMapper(UserMapper.class);

        UserBean user=new UserBean();
        user.setUsername("猴子");
        user.setPassword("123456");
		//调用save方法
        int len=userMapper.save(user);
        session.commit();  //提交事务
        session.close();   //关闭session会话
    }
}

插入完成后要求返回自增长的id值

针对mysql的auto_increment,在配置上添加设置useGeneratedKeys=“true”

<insert id="save" parameterType="com.yan.entity.UserBean" useGeneratedKeys="true" keyProperty="id">
        insert into t_users(username,password) values(#{username},#{password})
</insert>

修改单元测试

public class UserMapperTest {
    private SqlSessionFactory factory;
    @Before
    public void bb()throws Exception{
        Reader r= Resources.getResourceAsReader("mybatis-config.xml");
        factory=new SqlSessionFactoryBuilder().build(r);
    }
    @Test
    public void testSave(){
        SqlSession session=factory.openSession();
        UserMapper userMapper=session.getMapper(UserMapper.class);

        UserBean user=new UserBean();
        user.setUsername("猴子");
        user.setPassword("123456");

        int len=userMapper.save(user);
        session.commit();
        session.close();
        Assert.assertEquals(1,len);
        if(len>0){
            System.out.println(user.getId());
        }
    }
}

删除数据

  • 逻辑删除和物理删除

修改映射元文件,添加delete操作配置

<delete id="remove" parameterType="long">
        delete from t_users where id=#{id}  //针对简单类型,#{}用于指代这个参数,而不是参数中的某个属性值,这个名称可以随意写
</delete>

修改接口,新增方法

public interface UserMapper {
    int save(UserBean user);
    int remove(Long id);  //对应映射元文件种的<delete>
}

单元测试

@Test
public void testRemove(){
        SqlSession session=factory.openSession();
        UserMapper userMapper=session.getMapper(UserMapper.class);
        int len=userMapper.remove(1L);
        if(len>0)
            System.out.println("删除成功!");
        Assert.assertEquals(1,len);
        session.commit();
        session.close();
}

按照id加载数据

修改映射元文件,添加select操作配置

<select id="load" parameterType="long" resultType="com.yan.entity.UserBean">参数是long类型,而返回值为UserBean类型
        select * from t_users where id=#{id}
    </select>

修改接口,新增方法

public interface UserMapper {
    int save(UserBean user);
    int remove(Long id);
    UserBean load(Long id);  //对应的就是<select id="load">
}

单元测试

@Test
    public void testLoad(){
        SqlSession session=factory.openSession();
        UserMapper userMapper=session.getMapper(UserMapper.class);
        UserBean user=userMapper.load(3L);
        System.out.println(user);
        session.commit();
        session.close();
    }

修改操作

按照id修改数据

修改映射元文件,添加update操作配置

<update id="modify" parameterType="com.yan.entity.UserBean">
        update t_users set username=#{username},password=#{password} where id=#{id}
    </update>

修改接口,新增方法

public interface UserMapper {
    int save(UserBean user);
    int remove(Long id);
    UserBean load(Long id);
    int modify(UserBean user);  //对应<update id="modify">对应名称
}

单元测试

@Test
    public void testModify() {
        SqlSession session = factory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        UserBean user = userMapper.load(3L);
        user.setUsername("小吕");
        user.setPassword("666666");
        int len=userMapper.modify(user);
        if(len>0) System.out.println("修改成功!");
        session.commit();
        session.close();
    }

查询所有数据

修改映射元文件,添加select操作配置

<select id="selectAll" resultType="com.yan.entity.UserBean"> 注意虽然返回的是一个集合,但是不能写集合类型,应该定义集合元素的类型
        select * from t_users
    </select>

修改接口,新增方法

public interface UserMapper {
    int save(UserBean user);
    int remove(Long id);
    UserBean load(Long id);
    int modify(UserBean user);
    List<UserBean> selectAll();  //对应的是<select id="selectAll">
}

单元测试

@Test
    public void testSelectAll() {
        SqlSession session = factory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        List<UserBean> userList=userMapper.selectAll();
        userList.forEach(System.out::println);
        session.close();
    }

查询总行数

修改映射元文件,添加select操作配置

<select id="selectCount" resultType="long">  返回结果值为long类型,没有参数
        select count(*) from t_users
    </select>

修改接口,新增方法

public interface UserMapper {
   ... ...
    long selectCount();//对应<select id="selectCount">
}j

单元测试

@Test
    public void testSelectCount() {
        SqlSession session = factory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        long len=userMapper.selectCount();
        System.out.println(len);
        Assert.assertEquals(2,len);
        session.close();
    }

不使用接口的调用方式

  • 属于比较老的调用方法
int len=session.insert("com.yan.dao.UserMapper.save",user);   insert表示执行<insert>操作,对应的id为[namespace+id]
    
int len=session.delete("com.yan.dao.UserMapper.remove",1L);

UserBean user=session.selectOne("com.yan.dao.UserMapper.load",3L);

int len=session.update("com.yan.dao.UserMapper.modify",user);

List<UserBean> userList=session.selectList("com.yan.dao.UserMapper.selectAll");

long len=session.selectOne("com.yan.dao.UserMapper.selectCount");

最大的问题是:不能进行静态检查

全局核心配置

  • 文件名称建议使用mybatis-config.xml,但是不是必须,可以改名称,甚至修改位置
Reader r= Resources.getResourceAsReader("mybatis-config.xml");  //对应位置为resources根目录下

Reader r= Resources.getResourceAsReader("config/mybatis-config.xml");//对应位置为resources/config目录下

全局配置文件的格式

configuration(配置)
	properties(属性)  --*
	settings(全局设置)  
	typeAliases(类型别名)
	typeHandlers(类型处理器)
	objectFactory(对象工厂)
	plugins(插件)
	environments(环境配置)   --*
		environment(环境变量)
			transactionManager(事务管理器)
			dataSource(数据源)
	databaseIdProvider(数据库厂商标识)
	mappers(映射器)  --*

properties属性配置

将应用中的一些常量定义在一个独立的properties文件

resources/database.properties 存储数据库链接相关的配置常量

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///test?serverTimezone=UTC
username=root
password=654321

在核心配置文件引用

<properties resource="database.properties"/> 引入并解析database.properties文件

<?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 resource="database.properties"/>
    <environments default="yan">
        <environment id="yan">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>  使用的是OGNL表达式语言,用于获取properties中定义的key为driver的配置值
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/yan/dao/UserMapper.xml"/>
    </mappers>
</configuration>

另外定义属性的方法

<properties resource="database.properties">
        <property name="password" value="123456"/>  如果database.properties中有password则这个配置无效,否则在其他位置可以通过${password}引用这个配置值
</properties>

编程配置

public SqlSessionFactory build(Reader reader, Properties properties)

setting全局配置

setting会改变 MyBatis 的运行时行为,例如是否使用二级缓存。

引入日志查看所执行的sql语句

logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。可取值为SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING,

1、添加依赖

<dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
</dependency>

2、针对log4j进行配置

resources/log4j.properties 名称和位置固定

log4j.rootLogger=DEBUG,console # debug就是需要输出的日志等级,只有大于等于debug的日志信息才进行输出
# 控制台(console)
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%m%n

3、全局配置

mybatis-config.xml

<settings>
        <setting name="logImpl" value="LOG4J"/>
</settings>

4、执行操作,则在控制台上查看输出

==>  Preparing: select * from t_users
==> Parameters: 
<==      Total: 4

Log4j

Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIXSyslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

日志级别详解

只有大于等于配置等级的日志信息才进行输出

日志级别

描述

OFF 关闭

最高级别,不输出日志

FATAL 致命

输出非常严重的可能会导致应用程序终止的错误

ERROR 错误

输出错误,但应用还能继续运行

WARN 警告

输出可能潜在的危险状况

INFO 信息

输出应用运行过程的详细信息

DEBUG 调试

输出更细致的对调试应用有用的信息

TRACE 跟踪

输出更细致的程序运行轨迹

ALL 所有

输出所有级别信息

编程使用

private static final Logger logger=Logger.getLogger(UserMapperTest.class);  获取日志记录器对象


在方法中输出日志信息
logger.trace("输出内容");  其中方法名称对应的就是日志等级
logger.warn("输出内容");

输出格式定义

%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL  
%r 输出自应用启动到输出该log信息耗费的毫秒数  
%c 输出所属的类目,通常就是所在类的全名  
%t 输出产生该日志事件的线程名  
%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”  
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921  
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。

类型别名typeAliases

解决在映射元文件种多次编写类全名的问题

<select id="load" parameterType="long" resultType="com.yan.entity.UserBean">
        select * from t_users where id=#{id}
    </select>

定义别名后在映射元文件种则可以使用别名替代全名

mybatis-config.xml

<typeAliases>
        <typeAlias type="com.yan.entity.UserBean" alias="User"/>  配置别名User用于替代类全名com.yan.entity.UserBean
</typeAliases>

可以修改映射元文件

<select id="load" parameterType="long" resultType="User">
        select * from t_users where id=#{id}
</select>

实际上系统提供了一组别名

别名

映射的类型

_byte

byte

_long

long

_short

short

_int

int

_integer

int

_double

double

_float

float

_boolean

boolean

string

String

byte

Byte

long

Long

short

Short

int

Integer

integer

Integer

double

Double

float

Float

boolean

Boolean

date

Date

decimal

BigDecimal

bigdecimal

BigDecimal

object

Object

map

Map

hashmap

HashMap

list

List

arraylist

ArrayList

collection

Collection

iterator

Iterator

类型处理器typeHandlers

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

#{birth,jdbcType=DATE}

##对象工厂objectFactory

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

##插件plugins

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

##环境配置environments

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。

<environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="jdbc:mysql://192.168.3.102:3306/test?serverTimezone=UTC"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
        <environment id="prod">
            <transactionManager type="JDBC"/>
            <dataSource type="JNDI">
                <property name="data_source " value="java:/aaa"/>
            </dataSource>
        </environment>
    </environments>

事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

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

数据源(dataSource)

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

  • 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):

UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:

  • driver – 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
  • url – 这是数据库的 JDBC URL 地址。
  • username – 登录数据库的用户名。
  • password – 登录数据库的密码。
  • defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
  • defaultNetworkTimeout – 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看 java.sql.Connection#setNetworkTimeout() 的 API 文档以获取更多信息。

作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:

  • driver.encoding=UTF8

这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8encoding 属性给数据库驱动。

POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:

  • poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10
  • poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
  • poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
  • poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。
  • poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnectionspoolMaximumLocalBadConnectionTolerance 之和。 默认值:3(新增于 3.4.5)
  • poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
  • poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。
  • poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。

JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:

  • initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
  • data_source – 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。

和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:

  • env.encoding=UTF8

数据库厂商标识databaseIdProvider

MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。

映射器mappers

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

映射元文件

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

select

参数类型为long,结果类型为Map

<select id="selectMap" parameterType="long" resultType="map">  这里的long和map都是系统预定义的别名
        select * from t_users where id=#{id}
</select>

修改接口

public interface UserMapper {
    Map<String,Object> selectMap(Long id);
}

调用

@Test
    public void testSelectMap() {
        SqlSession session = factory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        Map<String, Object> res = userMapper.selectMap(1L);
        System.out.println(res);
        session.close();
    }

id

在命名空间中唯一的标识符,可以被用来引用这条语句。对应Mapper接口中的方法名称

parameterType

将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。

resultType

期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合中元素的类型UserBean,而不是集合本身的类型List。 resultType 和 resultMap 之间只能同时使用一个。一般用于列名称和对象属性名称一致时

resultMap

对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。

表定义

create table t_users(
    id bigint primary key auto_increment,
    username varchar(20) not null unique,
    password varchar(20) not null,
    birth datetime default now(),
    sex boolean default 1,
    salary numeric(8,2)
)engine=innodb default charset utf8;

实体 类定义

@Data
public class UserBean implements Serializable {
    private Long id;
    private String username;
    private String password;
    private Date birth;
    private Boolean sex;
    private Double salary;
}

引入列名称和属性名称一一对应【名称对应,没有顺序的概念】,所以使用resultType指定返回类型。如果有不对应的内容则返回为null

<select id="load" parameterType="long" resultType="com.yan.entity.UserBean">
        select * from t_users where id=#{id}
    </select>

假设列名和属性名称不一致,则需要使用resultMap定义对应关系

create table t_users(
    id bigint primary key auto_increment,
    username varchar(20) not null unique,
    password varchar(20) not null,
    birth datetime default now(),
    sex boolean default 1,
    salary numeric(8,2)
)engine=innodb default charset utf8;

实体类定义

@Data
public class UserBean implements Serializable {
    private Long id1;
    private String username2;
    private String password3;
    private Date birth4;
    private Boolean sex5;
    private Double salary6;
}

由于表中的列名称和实体类中的属性名称不一致,所以需要通过resultMap声明对应关系

<resultMap id="userMapper" type="com.yan.entity.UserBean">
        <id column="id" property="id1" javaType="long"/>
        <result column="username" property="username2" javaType="java.lang.String"/>
        <result column="password" property="password3"/>
        <result column="birth" property="birth4" jdbcType="DATE"/>
        <result column="sex" property="sex5"/>
        <result column="salary" property="salary6"/>
</resultMap>

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

resultMap的子标签

  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
  • result – 注入到字段或 JavaBean 属性的普通结果

子标签的属性

property

映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。

column

数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。

javaType

一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。

jdbcType

JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。

resultMap的属性

id

当前命名空间中的一个唯一标识,用于标识一个结果映射。

type

类的完全限定名, 或者一个类型别名,用于指定返回值的类型。

autoMapping

如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。

引入resultMap

<select id="selectAll" resultMap="userMapper">  其中userMapper就是<resultMap>的id
        select * from t_users
</select>

insert

<insert id="save" parameterType="com.yan.entity.UserBean" useGeneratedKeys="true" keyProperty="id">
        insert into t_users(username,password) values(#{username},#{password})
    </insert>

在oracle中没有auto_increment,子增长需要依赖sequence序列对象

<insert id="insertUser" parameerType="com.yan.entity.UserBean">
  <selectKey keyProperty="id" resultType="long" order="BEFORE"> 对应的属性名为id,对应的类型为long,执行时机为insert语句之前
    select seq1.nextval from dual  oracle中的sql语句,用于执行查询一个自增长的id值
  </selectKey>
  insert into t_users(id, username, password) values (#{id}, #{username}, #{password})
</insert>

id

在命名空间中唯一的标识符,可以被用来引用这条语句。

parameterType

将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。

statementType

可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。

useGeneratedKeys

(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。

keyProperty

(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。

keyColumn

(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。

selectKey 元素的属性

keyProperty

selectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。

keyColumn

返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。

resultType

结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。

order

可以设置为 BEFOREAFTER。如果设置为 BEFORE,那么它首先会生成主键,设置 keyProperty 再执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。

update 和 delete

<delete id="remove" parameterType="long">
        delete from t_users where id=#{id}
    </delete>
    
<update id="modify" parameterType="com.yan.entity.UserBean">
        update t_users set username=#{username},password=#{password} where id=#{id}
    </update>

##sql

这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值

<sql id="cols">
        id,username,password,birth,sex,salary
    </sql>
    
<select id="load" parameterType="long" resultType="com.yan.entity.UserBean">
        select <include refid="cols"></include> from t_users where id=#{id}
    </select>

在日志系统中查看执行的sql语句为

com.yan.dao.UserMapper.load ==>  Preparing: select id,username,password,birth,sex,salary from t_users where id=?
com.yan.dao.UserMapper.load ==> Parameters: 3(Long)
com.yan.dao.UserMapper.load <==    Columns: id, username, password, birth, sex, salary
com.yan.dao.UserMapper.load <==        Row: 3, 小吕2, 266666, 2021-04-19 10:12:28, 1, null
com.yan.dao.UserMapper.load <==      Total: 1