MyBatis

MyBatis

Featured image

第1章 MyBatis是什么

1.1 MyBatis概念

1.2 MyBatis工作原理

image

  1. 读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
  2. 加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
  3. 构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
  4. 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
  5. Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
  6. MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
  7. 输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
  8. 输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。

第2章 MyBatis的核心组件

SqlSessionFactoryBuilder(构造器):它会根据配置或者代码来生成 SqlSessionFactory,采用的是分步构建的 Builder 模式。 SqlSessionFactory(工厂接口):依靠它来生成 SqlSession,使用的是工厂模式。 SqlSession(会话):一个既可以发送 SQL 执行返回结果,也可以获取 Mapper 的接口。在现有的技术中,一般我们会让其在业务逻辑代码中“消失”,而使用的是 MyBatis 提供的 SQL Mapper 接口编程技术,它能提高代码的可读性和可维护性。 SQL Mapper(映射器):MyBatis 新设计存在的组件,它由一个 Java 接口和 XML 文件(或注解)构成,需要给出对应的 SQL 和映射规则。它负责发送 SQL 去执行,并返回结果。

image

2.1 SqlSessionFactoryBuilder(构造器)/SqlSessionFactory(工厂接口)

2.1.1 使用 XML 构建 SqlSessionFactory(推荐)

<?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><!--别名-->
        <typeAliases alias="user" type="com.mybatis.po.User"/>
    </typeAliases>
    <!-- 数据库环境 -->
    <environments default="development">
        <environment id="development">
            <!-- 使用JDBC的事务管理 -->
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <!-- MySQL数据库驱动 -->
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <!-- 连接数据库的URL -->
                <property name="url"
                    value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>
    <!-- 将mapper文件加入到配置文件中 -->
    <mappers>
        <mapper resource="com/mybatis/mapper/UserMapper.xml" />
    </mappers>
</configuration>
  1. 元素定义了一个别名 user,它代表着 com.mybatis.po.User 这个类。这样定义后,在 MyBatis 上下文中就可以使用别名去代替全限定名了。
  2. 元素的定义,这里描述的是数据库。它里面的 元素是配置事务管理器,这里采用的是 MyBatis 的 JDBC 管理器方式。
  3. 元素配置数据库,其中属性 type="POOLED" 代表采用 MyBatis 内部提供的连接池方式,最后定义一些关于 JDBC 的属性信息。
  4. 元素代表引入的那些映射器,在谈到映射器时会详细讨论它。
SqlSessionFactory factory = null;
String resource = "mybatis-config.xml";
InputStream is;
try {
    InputStream is = Resources.getResourceAsStream(resource);
    factory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
    e.printStackTrace();
}

首先读取 mybatis-config.xml,然后通过 SqlSessionFactoryBuilder 的 Builder 方法去创建 SqlSessionFactory。

2.1.2 使用代码创建 SqlSessionFactory(不推荐)

// 数据库连接池信息
PooledDataSource dataSource = new PooledDataSource();
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword ("root");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
dataSource.setDefeultAutoCommit(false);
// 采用 MyBatis 的 JDBC 事务方式
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment ("development", transactionFactory, dataSource);
// 创建 Configuration 对象
Configuration configuration = new Configuration(environment);
// 注册一个 MyBatis 上下文别名
configuration.getTypeAliasRegistry().registerAlias("role", Role.class);
// 加入一个映射器
configuration.addMapper(RoleMapper.class);
//使用 SqlSessionFactoryBuilder 构建 SqlSessionFactory
SqlSessionFactory SqlSessionFactory =
new SqlSessionFactoryBuilder().build(configuration);
return SqlSessionFactory;

2.2 SqlSession(会话)

  1. 获取 Mapper 接口。
  2. 发送 SQL 给数据库。
  3. 控制数据库事务。
SqlSession sqlSession = SqlSessionFactory.openSession();
//定义 SqlSession
SqlSession sqlSession = null;
try {
    // 打开 SqlSession 会话
    sqlSession = SqlSessionFactory.openSession();
    // some code...
    sqlSession.commit();    // 提交事务
} catch (IOException e) {
    sqlSession.rollback();  // 回滚事务
}finally{
    // 在 finally 语句中确保资源被顺利关闭
    if(sqlSession != null){
        sqlSession.close();
    }
}

使用 commit 方法提交事务,或者使用 rollback 方法回滚事务。因为它代表着一个数据库的连接资源,使用后要及时关闭它,如果不关闭,那么数据库的连接资源就会很快被耗费光,整个系统就会陷入瘫痪状态,所以用 finally 语句保证其顺利关闭。

2.3 Mapper(映射器)

  1. 描述映射规则。
  2. 提供 SQL 语句,并可以配置 SQL 参数类型、返回类型、缓存刷新等信息。
  3. 配置缓存。
  4. 提供动态 SQL。
CREATE TABLE `role` (
    `id` bigint(20) NOT NULL,
    `role_name` varchar(20) DEFAULT NULL,
    `note` varchar(20) DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
package com.mybatis.pojo;

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

    public Long getId() {
        return id;
    }

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

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }
}

2.3.1 用 XML 实现映射器(推荐)

package com.mybatis.mapper;
import com.mybatis.pojo.Role;

public interface RoleMapper {
    public Role getRole(Long id);
}
<mapper resource="com/mybatis/mapper/RoleMapper.xml" />

它的作用就是引入一个 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.mybatis.mapper.RoleMapper">
    <select id="getRole" parameterType="long" resultType="role">
        SELECT id,role_name as roleName,note FROM role WHERE id =#{id}
    </select>
</mapper>
  1. 元素中的属性 namespace 所对应的是一个接口的全限定名,于是 MyBatis 上下文就可以通过它找到对应的接口。

这里采用的是一种被称为自动映射的功能,MyBatis 在默认情况下提供自动映射,只要 SQL 返回的列名能和 POJO 对应起来即可。
这里 SQL 返回的列名 id 和 note 是可以和之前定义的 POJO 的属性对应起来的,而表里的列 role_name 通过 SQL 别名的改写,使其成为 roleName,也是和 POJO 对应起来的,所以此时 MyBatis 就可以把 SQL 查询的结果通过自动映射的功能映射成为一个 POJO。

2.3.2 注解实现映射器(不推荐)

package com.mybatis.mapper;
import org.apache.ibatis.annotations.Select;
import com.mybatis.pojo.Role;
public interface RoleMapper2 {
    @Select("select id,role_name as roleName,note from t_role where id=#{id}")
    public Role getRole(Long id);
}

2.4 MyBatis执行SQL的两种方式

2.4.1 SqlSession 发送 SQL(不推荐)

Role role = (Role)sqlSession.select("com.mybatis.mapper.RoleMapper.getRole",1L);

/**
 * String 对象是由一个命名空间加上 SQL id 组合而成的,
 * 它完全定位了一条 SQL,
 * 这样 MyBatis 就会找到对应的 SQL。
 * 如果在 MyBatis 中只有一个 id 为 getRole 的 SQL,
 * 那么也可以简写为:
 */
Role role = (Role)sqlSession.selectOne("getRole",1L);

selectOne 方法表示使用查询并且只返回一个对象,而参数则是一个 String 对象和一个 Object 对象。这里是一个 long 参数,long 参数是它的主键。

2.4.2 用 Mapper 接口发送 SQL(推荐)

RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRole(1L);

通过 SqlSession 的 getMapper 方法来获取一个 Mapper 接口,就可以调用它的方法了。因为 XML 文件或者接口注解定义的 SQL 都可以通过“类的全限定名+方法名”查找,所以 MyBatis 会启用对应的 SQL 进行运行,并返回结果。

2.4.3 对比两种发送 SQL 方式

  1. 使用 Mapper 接口编程可以消除 SqlSession 带来的功能性代码,提高可读性,而 SqlSession 发送 SQL,需要一个 SQL id 去匹配 SQL,比较晦涩难懂。使用 Mapper 接口,类似 roleMapper.getRole(1L)则是完全面向对象的语言,更能体现业务的逻辑。
  2. 使用 Mapper.getRole(1L)方式,IDE 会提示错误和校验,而使用 sqlSession.selectOne(“getRole”,1L)语法,只有在运行中才能知道是否会产生错误。
  3. 目前使用 Mapper 接口编程已成为主流,尤其在 Spring 中运用 MyBatis 时,Mapper 接口的使用就更为简单。

2.5 核心组件的生命周期

2.5.1 SqlSessionFactoryBuilder

image

2.5.2 SqlSessionFactory

2.5.3 SqlSession

2.5.4 Mapper


第3章 第一个MyBatis程序

CREATE DATABASE mybatis;
USE mybatis;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `uid` tinyint(2) NOT NULL,
  `uname` varchar(20) DEFAULT NULL,
  `usex` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3.1 具体步骤

1)创建 Web 应用,并添加相关 JAR 包

ant-1.10.3.jar
ant-launcher-1.10.3.jar
asm-7.0.jar
cglib-3.2.10.jar
commons-logging-1.2.jar
javassist-3.24.1-GA.jar
log4j-1.2.17.jar
log4j-api-2.11.2.jar
log4j-core-2.11.2.jar
mybatis-3.5.2.jar
mysql-connector-java-8.0.20.jar
ognl-3.2.10.jar
slf4j-api-1.7.26.jar
slf4j-log4j12-1.7.26.jar

2)创建日志文件

# Global logging configuration
log4j.rootLogger=ERROR,stdout
# MyBatis logging configuration...
log4j.logger.com.mybatis=DEBUG
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

在日志文件中配置了全局的日志配置、MyBatis 的日志配置和控制台输出,其中 MyBatis 的日志配置用于将 com.mybatis 包下所有类的日志记录级别设置为 DEBUG。该配置文件内容不需要开发者全部手写,可以从 MyBatis 使用手册中的 Logging 小节复制,然后进行简单修改。

3)创建持久化类

package com.mybatis.po;
/**
 * springtest数据库中user表的持久类
 */
public class MyUser {
    private Integer uid; // 主键
    private String uname;
    private String usex;

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getUsex() {
        return usex;
    }

    public void setUsex(String usex) {
        this.usex = usex;
    }

    // 此处省略setter和getter方法
    @Override
    public String toString() { // 为了方便查看结果,重写了toString方法
        return "User[uid=" + uid + ",uname=" + uname + ",usex=" + usex + "]";
    }
}

4)创建映射文件

<?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.mybatis.mapper.UserMapper">
    <!-- 根据uid查询一个用户信息 -->
    <select id="selectUserById" parameterType="Integer"
            resultType="com.mybatis.po.MyUser">
        select * from user where uid = #{uid}
    </select>
    <!-- 查询所有用户信息 -->
    <select id="selectAllUser" resultType="com.mybatis.po.MyUser">
        select * from user
    </select>
    <!-- 添加一个用户,#{uname}为 com.mybatis.po.MyUser 的属性值 -->
    <insert id="addUser" parameterType="com.mybatis.po.MyUser">
        insert into user (uid,uname,usex)
        values(#{uid},#{uname},#{usex})
    </insert>
    <!--修改一个用户 -->
    <update id="updateUser" parameterType="com.mybatis.po.MyUser">
        update user set uname =
        #{uname},usex = #{usex} where uid = #{uid}
    </update>
    <!-- 删除一个用户 -->
    <delete id="deleteUser" parameterType="Integer">
        delete from user where uid
        = #{uid}
    </delete>
</mapper>

子元素 <select>、<insert>、<update> 以及 <delete> 中的信息是用于执行查询、添加、修改以及删除操作的配置。在定义的 SQL 语句中,“#{}”表示一个占位符,相当于“?”,而“#{uid}”表示该占位符待接收参数的名称为 uid。

5)创建 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="LOG4J" />
    </settings>
    <!-- 配置mybatis运行环境 -->
    <environments default="development">
        <environment id="development">
            <!-- 使用JDBC的事务管理 -->
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <!-- MySQL数据库驱动 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver" />
                <!-- 连接数据库的URL -->
                <property name="url"
                          value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>
    <!-- 将mapper文件加入到配置文件中 -->
    <mappers>
        <mapper resource="com/mybatis/mapper/UserMapper.xml" />
    </mappers>
</configuration>

6)创建测试类

package com.mybatis.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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 com.mybatis.po.MyUser;
public class MyBatisTest {
    public static void main(String[] args) {
        try {
            // 读取配置文件 mybatis-config.xml
            InputStream config = Resources
                    .getResourceAsStream("mybatis-config.xml");
            // 根据配置文件构建SqlSessionFactory
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder()
                    .build(config);
            // 通过 SqlSessionFactory 创建 SqlSession
            SqlSession ss = ssf.openSession();
            // SqlSession执行映射文件中定义的SQL,并返回映射结果
            /*
             * com.mybatis.mapper.UserMapper.selectUserById 为 UserMapper.xml
             * 中的命名空间+select 的 id
             */
            // 查询一个用户
            MyUser mu = ss.selectOne(
                    "com.mybatis.mapper.UserMapper.selectUserById", 1);
            System.out.println(mu);
            // 添加一个用户
            MyUser addmu = new MyUser();
            addmu.setUid(2);
            addmu.setUname("陈恒");
            addmu.setUsex("男");
            ss.insert("com.mybatis.mapper.UserMapper.addUser", addmu);
            // 修改一个用户
            MyUser updatemu = new MyUser();
            updatemu.setUid(1);
            updatemu.setUname("张三");
            updatemu.setUsex("女");
            ss.update("com.mybatis.mapper.UserMapper.updateUser", updatemu);
            // 删除一个用户
            ss.delete("com.mybatis.mapper.UserMapper.deleteUser", 3);
            // 查询所有用户
            List<MyUser> listMu = ss
                    .selectList("com.mybatis.mapper.UserMapper.selectAllUser");
            for (MyUser myUser : listMu) {
                System.out.println(myUser);
            }
            // 提交事务
            ss.commit();
            // 关闭 SqlSession
            ss.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

7)运行程序查看结果

DEBUG [main] - ==> Preparing: select * from user where uid = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User[uid=1,uname=张三,usex=女]
DEBUG [main] - ==> Preparing: insert into user (uid,uname,usex) values(?,?,?)
DEBUG [main] - ==> Parameters: 2(Integer), 陈恒(String), 男(String)
DEBUG [main] - <== Updates: 1
DEBUG [main] - ==> Preparing: update user set uname = ?,usex = ? where uid = ?
DEBUG [main] - ==> Parameters: 张三(String), 女(String), 1(Integer)
DEBUG [main] - <== Updates: 1
DEBUG [main] - ==> Preparing: delete from user where uid = ?
DEBUG [main] - ==> Parameters: 3(Integer)
DEBUG [main] - <== Updates: 0
DEBUG [main] - ==> Preparing: select * from user
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 2
User[uid=1,uname=张三,usex=女]
User[uid=2,uname=陈恒,usex=男]


第4章 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><!-- 配置 -->
    <properties /><!-- 属性 -->
    <settings /><!-- 设置 -->
    <typeAliases /><!-- 类型命名 -->
    <typeHandlers /><!-- 类型处理器 -->
    <objectFactory /><!-- 对象工厂 -->
    <plugins /><!-- 插件 -->
    <environments><!-- 配置环境 -->
        <environment><!-- 环境变量 -->
            <transactionManager /><!-- 事务管理器 -->
            <dataSource /><!-- 数据源 -->
        </environment>
    </environments>
    <databaseIdProvider /><!-- 数据库厂商标识 -->
    <mappers /><!-- 映射器 -->
</configuration>

4.1 properties(属性)元素

  1. property 子元素。
  2. properties 文件。
  3. 程序代码传递。

4.1.1 property 子元素

<?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="LOG4J" />
    </settings>
    <!-- 配置mybatis运行环境 -->
    <environments default="development">
        <environment id="development">
            <!-- 使用JDBC的事务管理 -->
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <!-- MySQL数据库驱动 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver" />
                <!-- 连接数据库的URL -->
                <property name="url"
                          value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>
    <!-- 将mapper文件加入到配置文件中 -->
    <mappers>
        <mapper resource="com/mybatis/mapper/UserMapper.xml" />
    </mappers>
</configuration>

这里使用了元素 下的子元素 定义,用字符串 database.username 定义数据库用户名,然后就可以在数据库定义中引入这个已经定义好的属性参数,如 ${database.username},这样定义一次就可以到处引用了。但是如果属性参数有成百上千个,显然使用这样的方式不是一个很好的选择,这个时候可以使用 properties 文件。

4.1.2 使用 properties 文件

database.driver=com.mysql.cj.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC
database.username=root
database.password=root
<properties resource="jdbc.properties"/>

4.1.3 使用程序传递方式传递参数

String resource = "mybatis-config.xml";
InputStream inputStream;
Inputstream in = Resources.getResourceAsStream("jdbc.properties");
Properties props = new Properties();
props.load(in);
String username = props.getProperty("database.username");
String password = props.getProperty("database.password");
//解密用户和密码,并在属性中重置
props.put("database.username", CodeUtils.decode(username));
props.put ("database.password", CodeUtils.decode(password)); 
inputstream = Resources.getResourceAsStream(resource);
//使用程序传递的方式覆盖原有的properties属性参数
SqlSessionFactory = new SqlSessionFactoryBuilder().build(inputstream, props);

4.2 settings(设置)元素

4.3 typeAliases(别名)元素

4.3.1 系统定义别名

别名 Java类型 是否支持数组
_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
ResultSet ResultSet
public TypeAliasRegistry() {
    registerAlias("string", String.class);
    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    ......
    registerAlias("byte[]",Byte[].class); registerAlias("long[]",Long[].class);
    ......
    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class); registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);
    registerAlias("ResultSet", ResultSet.class);
}
//事务方式别名
typeAliasRegistry.registerAlias("JDBC",JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED",ManagedTransactionFactory.class);
//数据源类型别名
typeAliasRegistry.registerAlias("JNDI",JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED",
PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED",UnpooledDataSourceFactory.class);
//缓存策略别名
typeAliasRegistry.registerAlias("PERPETUAL",PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO",FifoCache.class);
typeAliasRegistry.registerAlias("LRU",LruCache.class); 
typeAliasRegistry.registerAlias("SOFT", SoftCache.class); 
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
//数据库标识别名
typeAliasRegistry.registerAlias("DB_VENDOR",
VendorDatabaseIdProvider.class);
//语言驱动类别名
typeAliasRegistry.registerAlias("XML",XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW",RawLanguageDriver.class);
//日志类别名
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGTNG",JakartmCommonsLogginglmpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING",NoLoggingImpl.class);
//动态代理别名
typeAliasRegistry.registerAlias("CGLIB",CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST",JavassistProxyFactory.class);

4.3.2 自定义别名

<typeAliases><!--别名-->
    <typeAlias alias="role" type="com.mybatis.po.Role"/>
    <typeAlias alias="user" type="com.mybatis.po.User"/>
</typeAliases>
<typeAliases><!--别名-->
    <package name="com.mybatis.po"/>
</typeAliases>
package com.mybatis.po;
@Alias("user3")
public Class User {
    ......
}

4.4 TypeHandler(类型转换器)元素

image

<typeHandlers> 
    <typeHandler handler="com.mybatis.typehandler.CustomtypeHandler" />
</typeHandlers>
<typeHandlers> 
    <package name="com.mybatis.typehandler" />
</typeHandlers>

4.5 objectFactory(对象工厂)元素

4.6 environments(环境)元素

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

4.6.1 transactionManager(事务管理器)

JDBC:此配置直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务的作用域。
MANAGED:此配置从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。默认情况下,它会关闭连接,但一些容器并不希望这样可设置closeConnection=false。

4.6.2 dataSourc(数据源)

1)UNPOOLED(不推荐)

  1. driver 数据库驱动名,比如 MySQL 的 com.mysql.jdbc.Driver。
  2. url 连接数据库的 URL。
  3. username 用户名。
  4. password 密码。
  5. defaultTransactionIsolationLevel 默认的连接事务隔离级别。

2)POOLED(推荐)

名称 说明
poolMaximumActiveConnections  是在任意时间都存在的活动(也就是正在使用)连接数量,
默认值为 10
poolMaximumIdleConnections 是任意时间可能存在的空闲连接数
poolMaximumCheckoutTime 在被强制返回之前,池中连接被检出(checked out)的时间,
默认值为 20 000 毫秒(即 20 秒)
poolTimeToWait 是一个底层设置,
如果获取连接花费相当长的时间,
它会给连接池打印状态日志,
并重新尝试获取一个连接(避免在误配置的情况下一直失败),
默认值为 20 000 毫秒(即 20 秒)。
poolPingQuery 为发送到数据库的侦测查询,
用来检验连接是否处在正常工作秩序中,
并准备接受请求。
默认是“NO PING QUERY SET”,
这会导致多数数据库驱动失败时带有一个恰当的错误消息。
poolPingEnabled 为是否启用侦测查询。
若开启,
也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),
默认值为 false。
poolPingConnectionsNotUsedFor 为配置 poolPingQuery 的使用频度。
这可以被设置成匹配具体的数据库连接超时时间,
来避免不必要的侦测,
默认值为 0(即所有连接每一时刻都被侦测——仅当 poolPingEnabled 为 true 时适用)。

3)JNDI(不推荐)

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

第5章 MyBatis与Spring的整合

5.1 整合实例

1)创建应用并导入相关 JAR 包

commons-logging-1.2.jar
commons-dbcp2-2.1.1.jar
commons-pool2-2.4.2.jar
commons-pool2-2.8.1.jar
druid-1.1.9.jar
aspectjweaver-1.5.0.jar
log4j-1.2.17.jar
mybatis-3.5.2.jar
mybatis-spring-1.3.1.jar
mysql-connector-java-8.0.20.jar
spring-aop-4.3.6.RELEASE.jar
spring-beans-4.3.6.RELEASE.jar
spring-context-4.3.6.RELEASE.jar
spring-aspects-4.3.6.RELEASE.jar
spring-core-4.3.6.RELEASE.jar
spring-jdbc-4.1.2.RELEASE.jar
spring-expression-4.3.6.RELEASE.jar
spring-jdbc-4.3.2.RELEASE.jar
spring-test-3.2.13.RELEASE.jar
spring-test-4.3.2.RELEASE.jar
spring-tx-4.3.6.RELEASE.jar

2)创建持久化类

package com.pojo;

public class User {
    int uid;
    String uname;
    String usex;

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getUsex() {
        return usex;
    }

    public void setUsex(String usex) {
        this.usex = usex;
    }

    @Override
    public String toString() {
        return (uid + "  " + uname + "  " + usex);
    }
}

3)创建 SQL 映射文件和 数据访问接口

package com.mapper;
import com.pojo.User;
import java.util.List;

public interface UserMapper {
    /**
     * 接口方法对应的SQL映射文件UserMapper.xml中的id
     */
    public User selectUserById(Integer uid);
    public List<User> selectAllUser();
    public int addUser(User user);
    public int updateUser(User user);
    public int deleteUser(Integer uid);
}
<?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.mapper.UserMapper">
    <select id="selectUserById" parameterType="Integer" resultType="com.pojo.User">
        select * from user where uid = #{uid}
    </select>
    <!-- 查询所有用户信息 -->
    <select id="selectAllUser" resultType="com.pojo.User">
        select * from user
    </select>
    <!-- 添加一个用户,#{uname}为 com.mybatis.po.MyUser 的属性值 -->
    <insert id="addUser" parameterType="com.pojo.User">
    insert into user (uname,usex) values(#{uname},#{usex})
    </insert>
    <!--修改一个用户 -->
    <update id="updateUser" parameterType="com.pojo.User">
        update user set uname = #{uname},usex = #{usex} where uid = #{uid}
    </update>
    <!-- 删除一个用户 -->
    <delete id="deleteUser" parameterType="Integer">
        delete from user where uid = #{uid}
    </delete>
</mapper>

4)创建 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>
    <typeAliases>
        <typeAlias alias="User" type="com.pojo.User"/>
    </typeAliases>
</configuration>

5)创建 Spring 核心配置文件以及数据库资源文件

<?xml version="1.0" encoding="UTF-8"?>
<beans  xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <!-- 加载properties文件 -->
    <context:property-placeholder location="classpath:db.properties" />

    <!--配置数据源-->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
        <!-- 加载驱动 -->
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <!-- 数据库的名字 -->
        <property name="url" value="${jdbc.url}"></property>
        <!-- 用户名密码 -->
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <!-- 最大连接数 -->
        <property name="maxTotal" value="30"/>
        <!-- 最大空闲连接数 -->
        <property name="maxIdle" value="10"/>
        <!-- 初始化连接数 -->
        <property name="initialSize" value="5"/>
    </bean>

    <!-- 配置SqlSessionFactory对象 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!--配置userMapper对象-->
    <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.mapper.UserMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
</beans>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
jdbc.user=root
jdbc.password=root

6)创建日志文件

# Global logging configuration
log4j.rootLogger=ERROR,stdout
# MyBatis logging configuration...
log4j.logger.com.mybatis=DEBUG
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

7)创建测试类

package com.test;

import com.mapper.UserMapper;
import com.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DemoTest {
    public static void main(String[] args) {
        String xmlPath = "applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
                xmlPath);
        UserMapper userMapper = (UserMapper)applicationContext.getBean("userMapper");
        User user = userMapper.selectUserById(1);
        System.out.println(user);

    }
}

8)运行查看结果

1 张三 女


第6章 映射器元素详解

image

6.1 <mappers>元素

1)使用类路径引入:

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

2)使用本地文件路径引入:

<mappers>
    <mapper url="file:///D:/com/mybatis/mapper/UserMapper.xml"/>
</mappers>

3)使用接口类引入:

<mappers>
    <mapper class="com.mybatis.mapper.UserMapper"/>
</mappers>

4)使用包名引入(常用):

<mappers>
    <package name="com.mybatis.mapper"/>
</mappers>

6.1.1 <select>元素

<!--根据uid查询一个用户信息 -->
<select id="selectUserById" parameterType="Integer" resultType="com.mybatis.po.MyUser">
    select * from user where uid = #{uid}
</select>

在上述示例代码中,id 的值是唯一标识符,它接收一个 Integer 类型的参数,返回一个 MyUser 类型的对象,结果集自动映射到 MyUser 属性。

属性名称 描 述
id 它和 Mapper 的命名空间组合起来使用,
是唯一标识符,供 MyBatis 调用
parameterType 表示传入 SQL 语句的参数类型的全限定名或别名。
它是一个可选属性,MyBatis 能推断出具体传入语句的参数
resultType SQL 语句执行后返回的类型(全限定名或者别名)。
如果是集合类型,返回的是集合元素的类型,返回时可以使用 resultType 或 resultMap 之一
resultMap 它是映射集的引用,与 <resultMap> 元素一起使用,
返回时可以使用 resultType 或 resultMap 之一
flushCache 用于设置在调用 SQL 语句后是否要求 MyBatis 清空之前查询的本地缓存和二级缓存,
默认值为 false,如果设置为 true,
则任何时候只要 SQL 语句被调用都将清空本地缓存和二级缓存
useCache 启动二级缓存的开关,默认值为 true,表示将査询结果存入二级缓存中
timeout
用于设置超时参数,单位是秒(s),超时将抛出异常
fetchSize 获取记录的总条数设定
statementType 告诉 MyBatis 使用哪个 JDBC 的 Statement 工作,
取值为 STATEMENT(Statement)、 PREPARED(PreparedStatement)、
CALLABLE(CallableStatement)
resultSetType 这是针对 JDBC 的 ResultSet 接口而言,
其值可设置为 FORWARD_ONLY(只允许向前访问)、
SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新)

6.1.1.1 使用 Map 接口传递多个参数

1)在数据操作接口中实现查询陈姓男性用户信息功能的方法

public List<User> selectAllUser1(Map<String,Object> param);

2)SQL 映射文件代码

<!-- 查询陈姓男性用户信息 -->
<select id="selectAllUser1" resultType="com.pojo.User">
    select * from user
    where uname like concat('%',#{u_name},'%')
    and usex = #{u_sex}
</select>

3)测试类中的代码

//查询多个用户
    Map<String,Object> map = new HashMap<>();
    map.put("u_name","陈");
    map.put("u_sex","男");
    List<User> list = userMapper.selectAllUser1(map);
    for(User user : list) {
        System.out.println(user);
    }

4) 运行结果

2 陈恒 男
3 陈成 男

6.1.1.2 使用 Java Bean 传递多个参数

**1)创建 SeletUserParam **

package com.pojo;
public class SelectUserParam {
    private String u_name;
    private String u_sex;

    public String getU_name() {
        return u_name;
    }

    public void setU_name(String u_name) {
        this.u_name = u_name;
    }

    public String getU_sex() {
        return u_sex;
    }

    public void setU_sex(String u_sex) {
        this.u_sex = u_sex;
    }
}

2)修改 Dao 接口中的 selectAllUser 方法

public List<User> selectAllUser(SelectUserParam param);

3)修改 SQL 映射文件 UserMapper.xml

<select id="selectAllUser" resultType="com.pojo.User" parameterType="com.pojo.SelectUserParam">
    select * from user
    where uname like concat('%',#{u_name},'%')
    and usex=#{u_sex}
</select>

4)测试类中的代码

    SelectUserParam su = new SelectUserParam();
    su.setU_name("陈");
    su.setU_sex("男");
    List<User> list = userMapper.selectAllUser(su);
    for (User myUser : list) {
        System.out.println(myUser);
    }

5)运行结果

2 陈恒 男
3 陈成 男

6.1.2 <insert>元素

  1. keyProperty:该属性的作用是将插入或更新操作时的返回值赋给 PO 类的某个属性,通常会设置为主键对应的属性。如果是联合主键,可以将多个值用逗号隔开。
  2. keyColumn:该属性用于设置第几列是主键,当主键列不是表中的第 1 列时需要设置。如果是联合主键,可以将多个值用逗号隔开。
  3. useGeneratedKeys:该属性将使 MyBatis 使用 JDBC 的 getGeneratedKeys()方法获取由数据库内部产生的主键,例如 MySQL、SQL Server 等自动递增的字段,其默认值为 false。

6.1.2.1 主键(自动递增)回填

1)SQL 映射文件代码

<!--添加一个用户,成功后将主键值返回填给uid(po的属性)-->
<insert id="addUser" parameterType="com.po.MyUser" keyProperty="uid" useGeneratedKeys="true">
    insert into user (uname,usex) values(#{uname},#{usex})
</insert>

2)测试类代码

// 添加一个用户
    User addmu = new User();
    addmu.setUname("陈恒");
    addmu.setUsex("男");
    int add = userMapper.addUser(addmu);
    System.out.println("添加了" + add + "条记录");
    System.out.println("添加记录的主键是" + addmu.getUid());

3)运行结果

添加了1条记录
添加记录的主键是4

6.1.2.2 自定义主键

<!-- 添加一个用户,#{uname}为 com.pojo.User 的属性值 -->
<insert id="addUser" parameterType="com.pojo.User">
    <!-- 先使用selectKey元素定义主键,然后再定义SQL语句 -->
    <selectKey keyProperty="uid" resultType="Integer" order="BEFORE">
        select if(max(uid) is null,1,max(uid)+1) as newUid from user
    </selectKey>
    insert into user (uid,uname,usex) values(#{uid},#{uname},#{usex})
</insert>

order 属性可以设置为 BEFORE 或 AFTER。
BEFORE 表示先执行 元素然后执行插入语句。 AFTER 表示先执行插入语句再执行 元素。

6.1.3 <update>与<delete>元素

<!-- 修改一个用户 -->
<update id="updateUser" parameterType="com.pojo.User">
    update user set uname = #{uname},usex = #{usex} where uid = #{uid}
</update>

<!-- 删除一个用户 -->
<delete id="deleteUser" parameterType="Integer">
    delete from user where uid = #{uid}
</delete>

6.1.4 <sql> 元素

<sql id="comColumns">uid,uname,usex</sql>

<select id="selectUser" resultType="com.pojo.User">
    select <include refid="comColumns"/> from user
</select>

6.1.5 <resultMap> 元素

<resultMap id="" type="">
    <constructor><!-- 类再实例化时用来注入结果到构造方法 -->
        <idArg/><!-- ID参数,结果为ID -->
        <arg/><!-- 注入到构造方法的一个普通结果 -->  
    </constructor>
    <id/><!-- 用于表示哪个列是主键 -->
    <result/><!-- 注入到字段或JavaBean属性的普通结果 -->
    <association property=""/><!-- 用于一对一关联 -->
    <collection property=""/><!-- 用于一对多、多对多关联 -->
    <discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
        <case value=""/><!-- 基于某些值的结果映射 -->
    </discriminator>
</resultMap>

<resultMap> 元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识。
子元素 <constructor> 用于配置构造方法(当 POJO 未定义无参数的构造方法时使用)。
子元素 <id> 用于表示哪个列是主键。
子元素 <result> 用于表示POJO和数据表普通列的映射关系。
子元素 <association>、<collection> 和 <discriminator> 用在级联的情况下。

6.1.5.1 使用 Map 存储结果集

1)SQL 映射文件代码

<!-- 查询所有用户信息存到Map中 -->
<select id="selectAllUserMap" resultType="map">
    select * from user
</select>

2)UserMapper接口中添加方法

public List<Map<String,Object>> selectAllUserMap();

3)测试类代码

// 查询所有用户信息存到Map中
List<Map<String, Object>> lmp = userMapper.selectAllUserMap();
    for (Map<String, Object> map : lmp) {
        System.out.println(map);
    }

4)运行结果

{uid=1, uname=张三, usex=女}
{uid=2, uname=陈恒, usex=男}
{uid=3, uname=陈成, usex=男}
{uid=4, uname=陈恒, usex=男}

6.1.5.2 使用POJO存储结果集

1)创建 POJO 类

package com.pojo;

public class MapUser {
    private Integer m_uid;
    private String m_uname;
    private String m_usex;
    // 此处省略setter和getter方法
    @Override
    public String toString() {
        return "User[uid=" + m_uid + ",uname=" + m_uname + ",usex=" + m_usex
                + "]";
    }
}

2)配置 <resultMap> 元素

<!--使用自定义结果集类型-->
<resultMap type="com.pojo.MapUser" id="myResult">
    <!-- property 是 com.pojo.MapUser 类中的属性-->
    <!-- column是查询结果的列名,可以来自不同的表-->
    <id property="m_uid" column="uid"/>
    <result property="m_uname" column="uname"/>
    <result property="m_usex" column="usex"/>
</resultMap>

3)配置 <select> 元素

<!-- 使用自定义结果集类型查询所有用户 -->
<select id="selectResultMap" resultMap="myResult">
    select * from user
</select>

4)添加接口方法

public List<MapUser> selectResultMap();

5)测试类代码

// 使用resultMap映射结果集
List<MapUser> listResultMap = userMapper.selectResultMap();
for (MapUser myUser : listResultMap) {
    System.out.println(myUser);
}

6)运行结果

User[uid=1,uname=张三,usex=女]
User[uid=2,uname=陈恒,usex=男]
User[uid=3,uname=陈成,usex=男]
User[uid=4,uname=陈恒,usex=男]


第7章 MyBatis关联查询(级联查询)

7.1 一对一关联查询

property:指定映射到实体类的对象属性。
column:指定表中对应的字段(即查询返回的列名)。
javaType:指定映射到实体对象属性的类型。
select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

7.1.1 实例

1)创建数据表

CREATE TABLE idcard (
    id tinyint(2) NOT NULL AUTO_INCREMENT,
    code varchar(18) COLLATE utf8_unicode_ci DEFAULT NULL,
    PRIMARY KEY (id)
);
CREATE TABLE person (
    id tinyint(2) NOT NULL,
    name varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
    age int(11) DEFAULT NULL,
    idcard_id tinyint(2) DEFAULT NULL,
    PRIMARY KEY (id),
    KEY idcard_id (idcard_id),
    CONSTRAINT idcard_id FOREIGN KEY (idcard_id) REFERENCES idcard(id));

2)创建持久化类

package com.po;
public class Idcard {
    private Integer id;
    private String code;

    public Integer getId() {
        return id;
    }

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

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
    /**
     * 为方便测试,重写了toString方法
     */
    @Override
    public String toString() {
        return "Idcard [id=" + id + ",code=" + code + "]";
    }
}
package com.po;
public class Person {
    private Integer id;
    private String name;
    private Integer age;
    // 个人身份证关联
    private Idcard card;

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Idcard getCard() {
        return card;
    }

    public void setCard(Idcard card) {
        this.card = card;
    }

    @Override
    public String toString() {
        return "Person[id=" + id + ",name=" + name + ",age=" + age + ",card="
                + card + "]";
    }
}

3)创建映射文件

<?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>
    <!--在使用MyBatis嵌套查询方式进行关联查询时,使用MyBatis的延迟加载可以在一定程度上提高查询效率-->
    <settings>
        <!--打开延迟加载的开关-->
        <setting name= "lazyLoadingEnabled" value= "true"/>
        <!--将积极加载改为按需加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
</configuration>
<?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.mapper.IdCardMapper">
    <select id="selectCodeById" parameterType="Integer" resultType= "com.pojo.Idcard">
        select * from idcard where id=#{id}
    </select>
</mapper>
<?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.mapper.PersonMapper">
    <!-- 一对一根据id查询个人信息:级联查询的第一种方法(嵌套查询,执行两个SQL语句)-->
    <resultMap type="com.pojo.Person" id="cardAndPerson1">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <!-- 一对一级联查询-->
        <association property="card" column="idcard_id" javaType="com.pojo.Idcard"
                     select="com.mapper.IdCardMapper.selectCodeById"/>
    </resultMap>
    <select id="selectPersonById1" parameterType="Integer" resultMap=
            "cardAndPerson1">
        select * from person where id=#{id}
    </select>
    <!--对一根据id查询个人信息:级联查询的第二种方法(嵌套结果,执行一个SQL语句)-->
    <resultMap type="com.pojo.Person" id="cardAndPerson2">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <!-- 一对一级联查询-->
        <association property="card" javaType="com.pojo.Idcard">
            <id property="id" column="idcard_id"/>
            <result property="code" column="code"/>
        </association>
    </resultMap>
    <select id="selectPersonById2" parameterType="Integer" resultMap= "cardAndPerson2">
        select p.*,ic.code
        from person p, idcard ic
        where p.idcard_id=ic.id and p.id=#{id}
    </select>
    <!-- 一对一根据id查询个人信息:连接查询(使用POJO存储结果)-->
    <select id="selectPersonById3" parameterType="Integer" resultType= "com.pojo.SelectPersonById">
        select p.*,ic.code
        from person p, idcard ic
        where p.idcard_id = ic.id and p.id=#{id}
    </select>
</mapper>

4)创建 POJO 类

package com.pojo;
public class SelectPersonById {
    private Integer id;
    private String name;
    private Integer age;
    private String code;

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Override
    public String toString() {
        return "Person [id=" +id+",name=" +name+ ",age=" +age+ ",code=" +code+ "]";
    }
}

5)创建数据操作接口

package com.mapper;

import com.pojo.Idcard;

public interface IdCardMapper {
    public Idcard selectCodeById(Integer i);
}
package com.mapper;

import com.pojo.Person;
import com.pojo.SelectPersonById;

public interface PersonMapper {
    public Person selectPersonById1(Integer id);
    public Person selectPersonById2(Integer id);
    public SelectPersonById selectPersonById3(Integer id);
}

6)调用接口方法及测试

package com.controller;

import com.mapper.PersonMapper;
import com.pojo.Person;
import com.pojo.SelectPersonById;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller("oneToOneController")
public class OneToOneController {
    @Autowired
    private PersonMapper personMapper;
    public void test(){
        Person p1 = personMapper.selectPersonById1(1);
        System.out.println(p1);
        System.out.println("=============================");
        Person p2 = personMapper.selectPersonById2(1);
        System.out.println(p2);
        System.out.println("=============================");
        SelectPersonById p3 = personMapper.selectPersonById3(1);
        System.out.println(p3);
    }
}
package com.controller;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestOneToOne {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        OneToOneController oto = (OneToOneController)applicationContext.getBean("oneToOneController");
        oto.test();
    }
}

7)运行结果

Person[id=1,name=张三,age=20,card=Idcard [id=1,code=1111]]
= ============================
Person[id=1,name=张三,age=20,card=Idcard [id=1,code=1111]]
= ============================
Person [id=1,name=张三,age=20,code=1111]

7.2 一对多关联查询(级联查询)

7.2.1 实例

1)创建数据表

CREATE TABLE orders (
    id tinyint(2) NOT NULL AUTO_INCREMENT,
    ordersn varchar(10) DEFAULT NULL,
    user_id tinyint(2) DEFAULT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2)创建持久化类

// 一对多级联查询,用户关联的订单
private List<Orders> ordersList;
package com.po;
public class Orders {
    private Integer id;
    private String ordersn;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getOrdersn() {
        return ordersn;
    }
    public void setOrdersn(String ordersn) {
        this.ordersn = ordersn;
    }
    @Override
    public String toString() {
        return "Orders[id=" + id + ",ordersn=" + ordersn + "]";
    }
}

3)创建映射文件

<?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.mapper.UserMapper">
    <!-- 一对多 根据uid查询用户及其关联的订单信息:级联查询的第一种方法(嵌套查询) -->
    <resultMap type="com.po.MyUser" id="userAndOrders1">
        <id property="uid" column="uid" />
        <result property="uname" column="uname" />
        <result property="usex" column="usex" />
        <!-- 一对多级联查询,ofType表示集合中的元素类型,将uid传递给selectOrdersByld -->
        <collection property="ordersList" ofType="com.po.Orders"
                    column="uid" select="com.mapper.OrdersMapper.selectOrdersById" />
    </resultMap>

    <select id="selectOrdersById1" parameterType="Integer"
            resultMap="userAndOrders1">
        select * from user where uid = #{id}
    </select>

    <!--对多根据uid查询用户及其关联的订单信息:级联查询的第二种方法(嵌套结果) -->
    <resultMap type="com.po.MyUser" id="userAndOrders2">
        <id property="uid" column="uid" />
        <result property="uname" column="uname" />
        <result property="usex" column="usex" />
        <!-- 对多级联查询,ofType表示集合中的元素类型 -->
        <collection property="ordersList" ofType="com.po.Orders">
            <id property="id" column="id" />
            <result property="ordersn" column="ordersn" />
        </collection>
    </resultMap>

    <select id="selectOrdersById2" parameterType="Integer"
            resultMap="userAndOrders2">
        select u.*,o.id, o.ordersn from user u, orders o where u.uid
        = o.user_id and
        u.uid=#{id}
    </select>

    <!-- 一对多 根据uid查询用户及其关联的订单信息:连接查询(使用POJO存储结果) -->
    <select id="selectOrdersById3" parameterType="Integer"
            resultType="com.pojo.SelectUserOrdersById">
        select u.*, o.id, o.ordersn from user u, orders o where
        u.uid = o.user_id
        and u.uid=#{id}
    </select>
</mapper>
<?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.mapper.OrdersMapper">
    <!-- 根据用户uid查询订单信息 -->
    <select id="selectOrdersById" resultType="com.po.MyUser"
            parameterType="Integer">
        select * from orders where user_id=#{id}
    </select>
</mapper>

4)创建 POJO 类

package com.pojo;
public class SelectUserOrdersById {
    private Integer uid;
    private String uname;
    private String usex;
    private Integer id;
    private String ordersn;
    // 省略setter和getter方法
    @Override
    public String toString() { // 为了方便查看结果,重写了toString方法
        return "User[uid=" + uid + ",uname=" + uname + ",usex=" + usex
                + ",oid=" + id + ",ordersn=" + ordersn + "]";
    }
}

5)创建数据操作接口

package com.mapper;

import com.po.Orders;
import java.util.List;

public interface OrdersMapper {
    public List<Orders> selectOrdersById(Integer uid);
}
package com.mapper;

import com.po.MyUser;
import com.pojo.SelectUserOrdersById;
import java.util.List;

public interface UserMapper {
    public MyUser selectOrdersById1(Integer uid);
    public MyUser selectOrdersById2(Integer uid);
    public List<SelectUserOrdersById> selectOrdersById3(Integer uid);
}

6)调用接口方法及测试

package com.controller;

import com.mapper.UserMapper;
import com.po.MyUser;
import com.pojo.SelectUserOrdersById;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import java.util.List;

@Controller("oneToMoreController")
public class OneToMoreController {
    @Autowired
    private UserMapper userMapper;
    public void test(){
        //查询一个用户及订单信息
        MyUser auser1 = userMapper.selectOrdersById1(1);
        System.out.println(auser1);
        System.out.println("=============================");
        MyUser auser2 = userMapper.selectOrdersById2(1);
        System.out.println(auser2);
        System.out.println("=============================");
        List<SelectUserOrdersById> auser3 = userMapper.selectOrdersById3(1);
        System.out.println(auser3);
        System.out.println("=============================");
    }
}
package com.controller;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestOneToMore {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        OneToMoreController otm = (OneToMoreController)applicationContext.getBean("oneToMoreController");
        otm.test();
    }
}

6)配置 applicationContext.xml

<!--配置userMapper对象-->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="com.mapper.UserMapper"/>
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!--配置ordersMapper对象-->
<bean id="ordersMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="com.mapper.OrdersMapper"/>
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

7)运行结果

User[uid=1,uname=张三,usex=女]
= ============================
User[uid=1,uname=张三,usex=女]
= ============================
[User[uid=1,uname=张三,usex=女,oid=1,ordersn=order1]]
= ============================

7.3 多对多关联查询(级联查询)*

7.3.1 实例

1)创建数据表

CREATE TABLE product(
    id tinyint(2) NOT NULL,
    name varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
    price double DEFAULT NULL,
    PRIMARY KEY (id)
);
CREATE TABLE orders_detail(
    id tinyint(2) NOT NULL AUTO_INCREMENT,
    orders_id tinyint(2) DEFAULT NULL,
    product_id tinyint(2) DEFAULT NULL,
    PRIMARY KEY (id),
    KEY orders_id (orders_id),
    KEY product_id (product_id),
    CONSTRAINT orders_id FOREIGN KEY (orders_id) REFERENCES orders (id),
    CONSTRAINT product_id FOREIGN KEY (product_id) REFERENCES product (id)
);

第8章 动态SQL

8.1 if标签:条件判断

1)添加 SQL 映射语句

<!--使用 if 元素根据条件动态查询用户信息-->
<select id="selectUserByIf" resultType="com.pojo.User" parameterType="com.pojo.User">
    select * from user where 1=1
    <if test="uname!=null and uname!=''">
        and uname like concat('%',#{uname},'%')
    </if >
    <if test="usex !=null and usex !=''">
        and usex=#{usex}
    </if >
</select>

2)添加数据操作接口方法

public List<User> selectUserByIf(User user);

3)调用数据操作接口方法

// 使用 if 元素查询用户信息
    User ifmu=new User();
    ifmu.setUname ("张");
    ifmu.setUsex ("女");
    List<User> listByif=userMapper.selectUserByIf(ifmu);
    System.out.println ("if元素================");
    for (User user:listByif) {
        System.out.println(user);
    }

4)运行结果

if元素================
1 张三 女

8.2 choose、when、otherwise标签

1)添加 SQL 映射语句

<!--使用choose、when、otherwise元素根据条件动态查询用户信息-->
<select id="selectUserByChoose" resultType="com.pojo.User" parameterType= "com.pojo.User">
    select * from user where 1=1
    <choose>
        <when test="uname!=null and uname!=''">
            and uname like concat('%',#{uname},'%')
        </when>
        <when test="usex!=null and usex!=''">
            and usex=#{usex}
        </when>
        <otherwise>
            and uid > 1
        </otherwise>
    </choose>
</select>

2)添加数据操作接口方法

public List<User> selectUserByChoose(User user);

3)调用数据操作接口方法

// 使用 choose 元素查询用户信息
    User choosemu=new User();
    choosemu.setUname("");
    choosemu.setUsex("");
    List<User> listByChoose = userMapper.selectUserByChoose(choosemu);
    System.out.println ("choose 元素================");
    for (User user:listByChoose) {
        System.out.println(user);
    }

4)运行结果

choose 元素================
2 陈恒 男
3 陈成 男
4 陈恒 男

8.3 trim、where、set标签

8.3.1 trim标签

1)添加 SQL 映射语句

<!--使用trim元素根据条件动态查询用户信息-->
<select id="selectUserByTrim" resultType="com.pojo.User" parameterType="com.pojo.User">
    select * from user
    <trim prefix="where" prefixOverrides = "and | or">
        <if test="uname!=null and uname!=''">
            and uname like concat('%',#{uname},'%')
        </if>
        <if test="usex!=null and usex!=''">
            and usex=#{usex}
        </if>
    </trim>
</select>

2)添加数据操作接口方法

public List<User> selectUserByTrim(User user);

3)调用数据操作接口方法

// 使用trim元素查询用户信息
    User trimmu=new User();
    trimmu.setUname ("陈");
    trimmu.setUsex("男");
    List<User> listByTrim=userMapper.selectUserByTrim(trimmu);
    System.out.println ("trim 元素=========================");
    for (User user:listByTrim) {
        System.out.println(user);
    }

4)运行结果

trim 元素=========================
2 陈恒 男
3 陈成 男
4 陈恒 男

8.3.2 where标签

1)添加 SQL 映射语句

<!--使用where元素根据条件动态查询用户信息-->
    <select id="selectUserByWhere" resultType="com.pojo.User" parameterType="com.pojo.User">
        select * from user
        <where>
            <if test="uname!=null and uname!=''">
                and uname like concat('%',#{uname},'%')
            </if>
            <if test="usex!=null and usex!=''">
                and usex=#{usex}
            </if >
        </where>
    </select>

2)添加数据操作接口方法

public List<User> selectUserByWhere(User user);

3)调用数据操作接口方法

// 使用where元素查询用户信息
    User wheremu=new User();
    wheremu.setUname ("张");
    wheremu.setUsex("女");
    List<User> listByWhere=userMapper.selectUserByWhere(wheremu);
    System.out.println ("where 元素=========================");
    for (User user:listByWhere) {
        System.out.println(user);
    }

4)运行结果

where 元素=========================
1 张三 女

8.3.3 set标签

1)添加 SQL 映射语句

<!--使用set元素动态修改一个用户-->
<update id="updateUserBySet" parameterType="com.pojo.User">
    update user
    <set>
        <if test="uname!=null">uname=#{uname}</if>
        <if test="usex!=null">usex=#{usex}</if>
    </set>
    where uid=#{uid}
</update>

2)添加数据操作接口方法

public int updateUserBySet(User user);

3)调用数据操作接口方法

// 使用set元素查询用户信息
    User setmu=new User();
    setmu.setUid (1);
    setmu.setUname("张九");
    int setup=userMapper.updateUserBySet(setmu);
    System.out.println ("set 元素修改了"+setup+"条记录");
    System.out.println ("=========================");

4)运行结果

set 元素修改了1条记录

8.4 foreach标签

item 表示集合中每一个元素进行迭代时的别名。 index 指定一个名字,用于表示在迭代过程中每次迭代到的位置。 open 表示该语句以什么开始。 separator 表示在每次进行迭代之间以什么符号作为分隔符。 close 表示以什么结束。

  1. 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
  2. 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
  3. 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。

1)添加 SQL 映射语句

<!--使用foreach元素查询用户信息-->
<select id="selectUserByForeach" resultType="com.pojo.User" parameterType=
        "List">
    select * from user where uid in
    <foreach item="item" index="index" collection="list"
            open="(" separator="," close=")">
        #{item}
    </foreach>
</select>

2)添加数据操作接口方法

public List<User> selectUserByForeach(List<Integer> listId);

3)调用数据操作接口方法

//使用foreach元素查询用户信息
    List<Integer> listId=new ArrayList<Integer>();
    listId.add(1);
    listId.add(3);
    List<User> listByForeach = userMapper.selectUserByForeach(listId);
    System.out.println ("foreach元素================");
    for(User user : listByForeach) {
        System.out.println(user);
    }

4)运行结果

foreach元素================
1 张九 女
3 陈成 男

8.5 bind标签

例如 MySQL 的 concat 函数、Oracle 的连接符号“   ”,这样 SQL 映射文件就需要根据不同的数据库提供不同的实现,显然比较麻烦,且不利于代码的移植。幸运的是,MyBatis 提供了 <bind> 元素来解决这一问题。

1)添加 SQL 映射语句

<!--使用bind元素进行模糊查询-->
<select id="selectUserByBind" resultType="com.pojo.User" parameterType= "com.pojo.User">
    <!-- bind 中的 uname 是 com.po.MyUser 的属性名-->
    <bind name="paran_uname" value="'%' + uname + '%'"/>
    select * from user where uname like #{paran_uname}
</select>

2)添加数据操作接口方法

public List<User> selectUserByBind(User user);

3)调用数据操作接口方法

// 使用bind元素查询用户信息
    User bindmu=new User();
    bindmu.setUname ("张");
    List<User> listByBind=userMapper.selectUserByBind(bindmu);
    System.out.println ("bind 元素=========================");
    for (User user:listByBind) {
        System.out.println(user);
    }

4)运行结果

bind 元素=========================
1 张九 女