MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
Mybatis的jar包 和 mysql数据库驱动可到 maven公库中找到。
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings)和属性(properties)信息。现在先做个简单的实例,稍后再作讲解。
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver">property>
<property name="url" value="jdbc:mysql://localhost:3306/hibernate">property>
<property name="username" value="root">property>
<property name="password" value="qwertyuio">property>
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/mybatis/dao/impl/UserMapper.xml" />
mappers>
configuration>
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。但是也可以使用任意的输入流(InputStream)实例,包括字符串形式的文件路径或者 file:// 的 URL 形式的文件路径来配置。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,可使从 classpath 或其他位置加载资源文件更加容易。
我们编写一个Mybatis工具:
package com.mybatis.utils;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/**
* Mybatis工具类
* 获取SqlSession实例,关闭SqlSession会话
*
* @author lee
*
*/
public class MybatisUtils {
private static SqlSessionFactory fac;
private static InputStream input = null;
private static ThreadLocal local;
static {
//创建本地线程,用于单例化一个SqlSession实例
local = new ThreadLocal<>();
try {
//加载配置文件,传入的参数是 配置文件的路径
input = Resources.getResourceAsStream("mybatis.xml");
//构建SqlSessionFactory 实例
fac = new SqlSessionFactoryBuilder().build(input);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//关闭输入流
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @return session 返回一个单例化SqlSession实例
*/
public static SqlSession openSession() {
//从本地线程中获取一个 SqlSession实例,假如没有则创建一个
SqlSession session = (SqlSession)local.get();
if(session == null) {
session = fac.openSession();
local.set(session);
}
return session;
}
/**
* 关闭session会话
*
* @param session
*/
public static void closeSession(SqlSession session) {
session.close();
local.set(null);
}
}
MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。
<mapper namespace="com.mybatis.dao.IUserDao">
<select id="getAll" resultType="com.mybatis.entity.User">
select * from user
select>
mapper>
在配置映射文件中,我们在
元素中配置的namespace 就是对应着我们的dao层类,即是数据操作类。在 元素中配置的 id属性就是对应就是 该类的方法。
package com.mybatis.dao;
import com.mybatis.entity.User;
public interface IUserDao {
//Mybatis 会自动地根据 namespace命名空间找到给类,根据
public List getAll();
}
在配置映射文件中,我们在 元素中配置的resultType就是对应这个实体类。
package com.mybatis.entity;
import java.io.Serializable;
import java.util.Date;
/**
* 用来封装数据的实体类
*
* @author lee
*
*/
public class User implements Serializable{
private int id;
private String name;
private Date birthday;
public User() {};
public int getId(){return id;}
public void setId(int id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Date getBirthday() {return birthday;}
public void setBirthday(Date birthday) {this.birthday = birthday;}
}
运行无措,可以取出数据。
public void testUserDao{
@Test
public void test() {
//使用MybatisUtils工具类,获取SqlSession实例
SqlSession session = MybatisUtils.openSession();
//获取session通过映射文件,帮我们 实现了IUserDao的方法
IUserDao userDao = session.getMapper(IUserDao.class);
//返回所有的 user的数据
List users = userDao.getAll();
System.out.println(users);
//提交事务
session.commte();
//关闭session
MybatisUtils.closeSession(session);
}
}
SqlSession接口类似于JDBC中的Connection接口对象,我们需要保证每次用完都关闭它,让连接资源还给数据库。
SqlSession session = null;
try {
session = MybatisUtils.openSession();
//业务处理
//提交业务
session.commit();
}catch(Exception e) {
//出现异常事务回滚
session.rollback();
throw new RuntimeException(e);
}finally {
//关闭session
MybatisUtils.closeSession(session);
}
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在以保证所有的 XML 解析资源开放给更重要的事情。
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次。因此 SqlSessionFactory 的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理作用域中,比如 Servlet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。下面的示例就是一个确保 SqlSession 关闭的标准模式:
PS:配置文件编写可以下载 mybatis插件,来帮助我们编写其配置、映射文件。
Mybatis没有显示执行的sql语句的功能,我们需要加入log4j2来帮助我们查看Mybatis执行的sql语句。
加入log4j2也十分简单,只要导入jar包, 添加配置文件即可
将 log4j2.xml放到src下(maven工程的src/main/resource)
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
Console>
Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
Root>
<Logger name="com.mybatis.dao" level="TRACE" additivity="false">
<AppenderRef ref="Console"/>
Logger>
Loggers>
Configuration>
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings)和属性(properties)信息。文档的顶层结构如下:
环境元素定义了如何配置环境。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver">property>
<property name="url" value="jdbc:mysql://localhost:3306/hibernate">property>
<property name="username" value="root">property>
<property name="password" value="qwertyuio">property>
dataSource>
environment>
environments>
数据源类型:
这个数据源的实现只是每次被请求时打开和关闭连接。虽然一点慢,它对在及时可用连接方面没有性能要求的简单应用程序是一个很好的选择。 不同的数据库在这方面表现也是不一样的,所以对某些数据库来说使用连接池并不重要,这个配置也是理想的。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:
这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。
除了上述提到 UNPOOLED 下的属性外,会有更多属性用来配置 POOLED 的数据源(没必要一个个记住):
这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性:
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:
<typeAliases>
<typeAlias alias="User" type="com.mybatis.entity.User" />
typeAliases>
Mybatis已经为许多常见的 Java 类型内建了相应的类型别名。它们都是大小写不敏感的,需要注意的是由基本类型名称重复导致的特殊处理。(没必要一个个记住)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。下表描述了设置中各项的意图、默认值等。(没必要一个个记住)
当设置该值为 true时,Mybatis在映射时会自动帮我们处理,数据库中字段的 下划线 转变为 Java成员变量的 驼峰型。
例如: 数据中的字段为 user_id 转变为 userId , user_name 转变为 userName
MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。
SQL 映射文件有很少的几个顶级元素:
查询语句是 MyBatis 中最常用的元素之一,对每个插入、更新或删除操作,通常对应多个查询操作。这是 MyBatis 的基本原则之一,也是Mybatis将焦点和努力放到查询和结果映射的原因。简单查询的 select 元素是非常简单的。现在以一个主键查询:
<select id="getUserById" resultType="com.mybatis.entity.User">
select * from user where id = #{id}
select>
对应着dao层类:
package com.mybatis.dao;
import com.mybatis.entity.User;
public interface IUserDao {
//这样该类的 getUserById 对应着id ,参数也对应着
public User getUserById(int id);
}
测试运行
public void testUserDao{
@Test
public void test() {
//使用MybatisUtils工具类,获取SqlSession实例
SqlSession session = MybatisUtils.openSession();
//获取session通过映射文件,帮我们 实现了IUserDao的方法
IUserDao userDao = session.getMapper(IUserDao.class);
//返回所有的 user的数据
User users = userDao.getAll();
System.out.println(user);
//提交事务
session.commte();
//关闭session
MybatisUtils.closeSession(session);
}
}
对应着dao层类:
package com.mybatis.dao;
import com.mybatis.entity.User;
public interface IUserDao {
//这里通过对象来传递参数
public void insert(User user);
public void delete(int id);
//这里通过对象来传递参数
public void update(User user);
}
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into user (id,name,birthday)
values (#{id},#{name},#{birthday})
insert>
<update id="update" >
update user set name = #{name},birthday=#{birthday} where id=#{id}
update>
<delete id="delete">
delete from user where id = #{id}
delete>
对应着dao层类:
package com.mybatis.dao;
import com.mybatis.entity.User;
public interface IUserDao {
public void insert(User user);
public void delete(int id);
public void update(User user);
}
测试运行
public void testUserDao{
//insert
@Test
public void testInsert() {
SqlSession session = MybatisUtils.openSession();
IUserDao dao = session.getMapper(IUserDao.class);
User user = new User();
user.setBirthday(new Date());
user.setName("lee");
dao.insert(user);
session.commit();
MybatisUtils.closeSession(session);
}
//update
@Test
public void testUpdate() {
SqlSession session = MybatisUtils.openSession();
IUserDao dao = session.getMapper(IUserDao.class);
User user = new User();
user.setId(1);
user.setBirthday(new Date());
user.setName("lee");
dao.update(user);
session.commit();
MybatisUtils.closeSession(session);
}
//delete
@Test
public void testDelete() {
SqlSession session = MybatisUtils.openSession();
IUserDao dao = session.getMapper(IUserDao.class);
dao.delete(100);
session.commit();
MybatisUtils.closeSession(session);
}
}
PS:映射文件中,我们可以看到 映射语句元素 的id都对应着一个方法名。因此,在IUserDao 层的方法是不能重写的。
这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化。不同的属性值通过包含的实例变化。比如:
<sql id="columns">
#{param}.id,#{param}.name
sql>
<select id="getIdAndNameFromUser">
select <include refid="columns"><property name="param" value="user" />include>
from user
select>
缓存是互联网系统常用到的,其特点是将数据保存在内存当中。缓存实在计算机内存上保存的数据,在读取的时候无需再从磁盘读入,因此具备快速读取和使用的特点。如果缓存命中率高,那么可以极大地提高系统的性能。如果缓存命中率很低,那么缓存就不存在使用的意义,所以使用缓存的关键在于存储内容的命中率。
Mybatis对缓存提高支持,在没有配置的情况下,只开启一级缓存。一级缓存只存在于一个SqlSession内.
不同SqlSession的一级缓存是不会共享的。
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:
<cache/>
这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句将会被缓存。
- 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
- 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
- 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
- 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
- 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用- - 者修改,而不干扰其他调用者或线程所做的潜在修改。
添加了这个配置后,我要对需要存储在二级缓存的POJO对象实现序列化。否则会抛出异常。
所有的这些属性都可以通过缓存元素的属性来修改。比如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
1、可用的收回策略有(eviction):
- LRU – 最近最少使用的:移除最长时间不被使用的对象。(默认)
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
2、flushInterval(刷新间隔)
可以被设置为任意的正整数,而且它们代表一个合理的毫秒 形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
3、size(引用数目)
可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
4、readOnly(只读)属性
可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
PS:也可以通过实现你自己的缓存或为其他第三方缓存方案 创建适配器来完全覆盖缓存行为。
type="com.domain.something.MyCustomCache"/>
PSPS:想在命名空间中共享相同的缓存配置和实例。在这样的 情况下你可以使用 cache-ref 元素来引用另外一个缓存。
<cache-ref namespace="com.someone.application.data.SomeMapper"/>
上面的 select 元素,传入了一个参数进去。但是当我们需要传入多个参数的时候就需要特殊处理了。我们就是使用上面update元素提供的例子,来尝试三种传递参数的方法。
<update id="update" >
update user set name = #{name},birthday=#{birthday} where id=#{id}
update>
//只要User实体类中的 成员变量 对应着相应 字段 即可
public void update(User user);
//使用:
User user = new User();
user.setId(2);
user.setName("lee");
user.setBirthday(new Date());
userDao.update(user)
这个方法虽然简单易用,但是Map需要键值对应,由于业务关联性不强,造成可读性下降。
//只要 key 对应着相应的 字段 即可
public void update(Map params);
//使用:
Map<String,Object> params = new HashMap<>();
params.put("id", 6);
params.put("name", "lee");
params.put("birthday", new Date());
userDao.update(params);
使用注解@Param()来时实现传递参数
//@Param()注解的传入的参数,就是对应的字段名
public void update(@Param("id")int id,@Param("name")String name,@Param("birthday")Date birthday);
//使用
userDao.update(1,"lee",new Date());
当我们在学习
元素的时候,有学到主键的自增,可以选择使用数据库内置策略生成。但是在插入后,我们也往往需要获得这个主键,以便于未来的操作。而Mybatis提供两个实现方法。
首先,我们要使用keyProperty属性指定那个 那个是主键字段,同时使用useGeneratedKeys属性告诉Mybatis这个主键是否使用数据库内置策略生成。
只要我们在该实体类(javabean)中有该主键的getter和setter方法,在使用
对数据进行插入后(插入数据时,即是添加一行新数据时,是没有指定主键值的),Mybatis会自动帮我进行主键回填,即是将该实力类的实例的主键值通过setter进行赋值。
//例如
User user = new User();
user.setName("lee");
user.setBirthday(new Date());
//插入新数据
userDao.insert(user);
//此时输出 user实例的id值,不为null
System.out.println(user.id);
resultMap 元素是 MyBatis 中最重要最强大的元素。它就是让你远离 90%的需要从结果 集中取出数据的 JDBC 代码的那个东西, 而且在一些情形下允许你做一些 JDBC 不支持的事 情。 事实上, 编写相似于对复杂语句联合映射这些等同的代码, 也许可以跨过上千行的代码。 ResultMap 的设计就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们 的关系。
一般,任何的select语句都可以使用map集合存储。使用map集合原则上是可以匹配所有结果集,但是使用Map集合接口以为着可读性的下降。
<select id="getUserById" resultType="map">
select * from user where id = #{id}
select>
一般,我们的应用程序会使用 JavaBeans(实体类) 或 POJOs(Plain Old Java Objects,普通 Java 对象)来作为 模型。基于 JavaBean 的规范实体类的属性 在 select 语句中会精确匹配到相应的 字段。
POJO使我们最常用的方式。使用select语句的属性resultMap配置 外部 resultMap映射集合。当属性和字段名字不同时,外部的 resultMap 也能帮我们解决这个问题。
<typeAlias type="com.mybatis.entity.User" alias="User"/>
<resultMap type="User" id="userMap" >
<id property="id" column="id"/>
<result property="name" column="name"/>
resultMap>
<select id="getNameAndBirthdayByMap" resultMap="userMap">
select id,name from user where id=#{id}
select>
resultMap 元素有很多子元素和一个值得讨论的结构。
这些是结果映射最基本内容。id 和 result 都映射一个单独列的值到简单数据类型(字符 串,整型,双精度浮点数,日期等)的单独属性或字段。
这两者之间的唯一不同是 id 表示的结果将是当比较对象实例时用到的标识属性。这帮 助来改进整体表现,特别是缓存和嵌入结果映射(也就是联合映射) 。
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
为了未来的参考,MyBatis 通过包含的 jdbcType 枚举型,支持下面的 JDBC 类型。
MyBatis 支持私有属性和私有 JavaBeans 属 性来达到这个目的,但是一些人更青睐构造方法注入。
看看下面这个构造方法:
public class User {
//...
public User(Integer id, String username, int age) {
//...
}
//...
}
为了将结果注入构造方法,MyBatis需要通过某种方式定位相应的构造方法。 在下面的例子中,MyBatis搜索一个声明了三个形参的的构造方法,以 java.lang.Integer, java.lang.String and int 的顺序排列。
<constructor>
<idArg column="id" javaType="int"/>
<arg column="username" javaType="String"/>
<arg column="age" javaType="_int"/>
constructor>
剩余的属性和规则和固定的 id 和 result 元素是相同的。
在数据库包含着一对一、一对多、多对一、多对多的关系。
例如,一个“部门”可以有 多个“员工” 。有时候,我们希望获取 “员工” 的信息时,同时获取其所在的 “部门”信息。
select r.*,e.* from role r, empt e where r.empt_id = e.empt_id
在Mybatis中级联分别为三种:
例如,一个“部门”可以有 多个“员工” 。有时候,我们希望获取 “员工” 的信息时,同时获取其所在的 “部门”信息。
实际上,一个 “员工” 只会在一个 “部门”当中,因此多对一,我们也可以理解成 一对一。
现在,我们来实现 希望获取 “员工” 的信息时,同时获取其所在的 “部门”信息 :
我们来建立 “部门” 和 “员工”的数据库表:
(1)员工表
(2)部门表
员工实体类
package com.mybatis.entity;
import java.util.Date;
/**
* 封装 员工信息 实体类
*
* @author lee
*
*/
public class Role {
private int roleId;
private String roleName;
private Date roleBirthday;
/**
* 这里引用一个 部门的 实体类,
* 这样,我们就可以通过这样引用来获取,其所在 部门的信息
*
*/
private Empt empt;
public Empt getEmpt() {
return empt;
}
public void setEmpt(Empt empt) {
this.empt = empt;
}
public int getRoleId() {
return roleId;
}
public void setRoleId(int roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public Date getRoleBirthday() {
return roleBirthday;
}
public void setRoleBirthday(Date roleBirthday) {
this.roleBirthday = roleBirthday;
}
}
部门 的实体类
package com.mybatis.entity;
import java.util.List;
/**
* 封装着 部门信息 的实体类
*
* @author lee
*
*/
public class Empt {
private int emptId;
private String emptName;
/**
* 这里引用了该部门 所有的员工 的一个集合
* 这样,我们就可以通过这个引用,来获取该部门 所有的员工
*/
private List roles;
public List getRoles() {
return roles;
}
public void setRoles(List roles) {
this.roles = roles;
}
public int getEmptId() {
return emptId;
}
public void setEmptId(int emptId) {
this.emptId = emptId;
}
public String getEmptName() {
return emptName;
}
public void setEmptName(String emptName) {
this.emptName = emptName;
}
}
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
<setting name="logImpl" value="LOG4J2"/>
settings>
<typeAliases>
<typeAlias alias="Role" type="com.mybatis.entity.Role" />
<typeAlias alias="Empt" type="com.mybatis.entity.Empt" />
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver">property>
<property name="url" value="jdbc:mysql://localhost:3306/hibernate">property>
<property name="username" value="root">property>
<property name="password" value="qwertyuio">property>
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/mybatis/dao/impl/EmptMapper.xml" />
<mapper resource="com/mybatis/dao/impl/RoleMapper.xml" />
mappers>
configuration>
RoleMapper.xml
<mapper namespace="com.mybatis.dao.IRoleDao">
<resultMap type="Role" id="roleMap">
<id property="roleId" column="role_id"/>
<result property="roleName" column="role_name"/>
<result property="roleBirthday" column="role_birthday" />
<association property="empt" javaType="Empt">
<id property="emptId" column="empt_id"/>
<result property="emptName" column="empt_name"/>
association>
resultMap>
<select id="getRoleInOneEmpt" resultMap="roleMap">
select r.*,e.* from role r ,empt e where r.empt_id = e.empt_id
select>
<resultMap type="Role" id="userMap1">
<id property="roleId" column="role_id"/>
<result property="roleName" column="role_name"/>
<result property="roleBirthday" column="role_birthday" />
<association property="empt" column="empt_id" javaType="Empt" select="com.mybatis.dao.IEmptDao.getEmptById" />
resultMap>
<select id="getRoleInOneEmpt1" resultMap="userMap1">
select * from Role
select>
mapper>
EmptMapper.xml
<mapper namespace="com.mybatis.dao.IEmptDao">
<select id="getEmptById" resultType="Empt">
select * from empt where empt_id = #{emptId}
select>
mapper>
IRoleDao
package com.mybatis.dao;
import java.util.List;
import com.mybatis.entity.Role;
public interface IRoleDao {
//方式一
public List getRoleInOneEmpt();
//方式二
public List getRoleInOneEmpt1();
}
package com.mybatis.dao;
import java.util.List;
import com.mybatis.entity.Empt;
public interface IEmptDao {
//方式二使用的方法
public Empt getEmptById(int emptId) ;
}
测试
package com.mybatis.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.mybatis.entity.Role;
import com.mybatis.utils.MybatisUtils;
public class RoleDaoTest {
@Test
public void test() {
SqlSession session = MybatisUtils.openSession();
IRoleDao dao = session.getMapper(IRoleDao.class);
List list = dao.getRoleInOneEmpt();
session.commit();
MybatisUtils.closeSession(session);
System.out.println(list.get(0).getRoleName()+" "+list.get(0).getEmpt().getEmptName() );
}
@Test
public void test1() {
SqlSession session = MybatisUtils.openSession();
IRoleDao dao = session.getMapper(IRoleDao.class);
List roles = dao.getRoleInOneEmpt1();
session.commit();
MybatisUtils.closeSession(session);
System.out.println(roles.get(0).getRoleName()+" "+roles.get(0).getEmpt().getEmptName() );
}
}
可以看出我们查询的语句为:Preparing: select r.*,e.* from role r ,empt e where r.empt_id = e.empt_id
查询出来的字段分别为:role_id, role_name, role_birthday, empt_id, empt_id, empt_name
查询出来四条数据,并且以 部门 为分类
//方式一:
12:49:33.848 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleInOneEmpt - ==> Preparing: select r.*,e.* from role r ,empt e where r.empt_id = e.empt_id
12:49:34.090 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleInOneEmpt - ==> Parameters:
12:49:34.148 [main] TRACE com.mybatis.dao.IRoleDao.getRoleInOneEmpt - <== Columns: role_id, role_name, role_birthday, empt_id, empt_id, empt_name
12:49:34.171 [main] TRACE com.mybatis.dao.IRoleDao.getRoleInOneEmpt - <== Row: 1, lee, 2017-08-22, 1, 1, 设计部
12:49:34.181 [main] TRACE com.mybatis.dao.IRoleDao.getRoleInOneEmpt - <== Row: 2, wang, 2017-08-22, 1, 1, 设计部
12:49:34.187 [main] TRACE com.mybatis.dao.IRoleDao.getRoleInOneEmpt - <== Row: 3, yang, 2017-08-22, 2, 2, 人事部
12:49:34.192 [main] TRACE com.mybatis.dao.IRoleDao.getRoleInOneEmpt - <== Row: 4, huang, 2017-08-22, 3, 3, 研发部
12:49:34.197 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleInOneEmpt - <== Total: 4
lee 设计部
方式二不同点是,就是通过先查找员工,再通过部门id 来获取该员工所在的部门
//方式二
14:24:06.209 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleInOneEmpt1 - ==> Preparing: select * from Role
14:24:06.413 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleInOneEmpt1 - ==> Parameters:
14:24:06.522 [main] TRACE com.mybatis.dao.IRoleDao.getRoleInOneEmpt1 - <== Columns: role_id, role_name, role_birthday, empt_id
14:24:06.525 [main] TRACE com.mybatis.dao.IRoleDao.getRoleInOneEmpt1 - <== Row: 1, lee, 2017-08-22, 1
14:24:06.537 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptById - ====> Preparing: select * from empt where empt_id = ?
14:24:06.542 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptById - ====> Parameters: 1(Integer)
14:24:06.547 [main] TRACE com.mybatis.dao.IEmptDao.getEmptById - <==== Columns: empt_id, empt_name
14:24:06.547 [main] TRACE com.mybatis.dao.IEmptDao.getEmptById - <==== Row: 1, 设计部
14:24:06.552 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptById - <==== Total: 1
14:24:06.556 [main] TRACE com.mybatis.dao.IRoleDao.getRoleInOneEmpt1 - <== Row: 2, wang, 2017-08-22, 1
14:24:06.561 [main] TRACE com.mybatis.dao.IRoleDao.getRoleInOneEmpt1 - <== Row: 3, yang, 2017-08-22, 2
14:24:06.568 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptById - ====> Preparing: select * from empt where empt_id = ?
14:24:06.569 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptById - ====> Parameters: 2(Integer)
14:24:06.577 [main] TRACE com.mybatis.dao.IEmptDao.getEmptById - <==== Columns: empt_id, empt_name
14:24:06.579 [main] TRACE com.mybatis.dao.IEmptDao.getEmptById - <==== Row: 2, 人事部
14:24:06.581 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptById - <==== Total: 1
14:24:06.589 [main] TRACE com.mybatis.dao.IRoleDao.getRoleInOneEmpt1 - <== Row: 4, huang, 2017-08-22, 3
14:24:06.593 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptById - ====> Preparing: select * from empt where empt_id = ?
14:24:06.597 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptById - ====> Parameters: 3(Integer)
14:24:06.599 [main] TRACE com.mybatis.dao.IEmptDao.getEmptById - <==== Columns: empt_id, empt_name
14:24:06.600 [main] TRACE com.mybatis.dao.IEmptDao.getEmptById - <==== Row: 3, 研发部
14:24:06.602 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptById - <==== Total: 1
14:24:06.603 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleInOneEmpt1 - <== Total: 4
lee 设计部
PS:两种方式的不同,在于 方式一 是直接在 resultMap的 association 中直接 构建 引用的实体。方式二是引用另外映射,通过传入 字段 来获取 引用的实体。
同样的,以上面数据库和实体类为例。当我们 获取所有 部门 的同时,如何获取 部门下的所有员工。
RoleMapper.xml
<mapper namespace="com.mybatis.dao.IRoleDao">
<select id="getRoleByEmpt" resultType="Role">
select * from role where empt_id = #{emptId}
select>
mapper>
EmptMapper.xml
<resultMap type="Empt" id="emptMap">
<id property="emptId" column="role_id">id>
<result property="emptName" column="empt_name">result>
<collection property="roles" javaType="ArrayList" ofType="Role">
<id property="roleId" column="role_id"/>
<result property="roleName" column="role_name"/>
<result property="roleBirthday" column="role_birthday"/>
collection>
resultMap>
<select id="getEmptAndItsRoles" resultMap="emptMap">
select e.*,r.* from empt e,role r where e.empt_id = r.empt_id
select>
<resultMap type="Empt" id="emptMap1">
<id property="emptId" column="role_id">id>
<result property="emptName" column="empt_name">result>
<collection property="roles" javaType="ArrayList" ofType="Role" column="empt_id" select="com.mybatis.dao.IRoleDao.getRoleByEmpt"/>
resultMap>
<select id="getEmptAndItsRoles1" resultMap="emptMap1">
select * from empt
select>
mapper>
IRoleDao
package com.mybatis.dao;
import java.util.List;
import com.mybatis.entity.Role;
public interface IRoleDao {
//方式二使用的方法
public List getRoleByEmpt(int emptId);
}
IEmptDao
package com.mybatis.dao;
import java.util.List;
import com.mybatis.entity.Empt;
public interface IEmptDao {
public List getEmptAndItsRoles();
public List getEmptAndItsRoles1();
}
package com.mybatis.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.mybatis.entity.Role;
import com.mybatis.utils.MybatisUtils;
public class RoleDaoTest {
@Test
public void test() {
SqlSession session = MybatisUtils.openSession();
IEmptDao dao = session.getMapper(IEmptDao.class);
List empts = dao.getEmptAndItsRole();
session.commit();
MybatisUtils.closeSession(session);
}
@Test
public void test1() {
SqlSession session = MybatisUtils.openSession();
IEmptDao dao = session.getMapper(IEmptDao.class);
List empts = dao.getEmptAndItsRole1();
session.commit();
MybatisUtils.closeSession(session);
}
}
可以看出我们查询的语句为:select e.*,r.* from empt e,role r where e.empt_id = r.empt_id
查询出来的字段分别为:empt_id, empt_name, role_id, role_name, role_birthday, empt_id
查询出来四条数据,并且以 部门 为分类
//方式一:
14:17:40.524 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptAndItsRoles - ==> Preparing: select e.*,r.* from empt e,role r where e.empt_id = r.empt_id
14:17:40.730 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptAndItsRoles - ==> Parameters:
14:17:40.851 [main] TRACE com.mybatis.dao.IEmptDao.getEmptAndItsRoles - <== Columns: empt_id, empt_name, role_id, role_name, role_birthday, empt_id
14:17:40.855 [main] TRACE com.mybatis.dao.IEmptDao.getEmptAndItsRoles - <== Row: 1, 设计部, 1, lee, 2017-08-22, 1
14:17:40.866 [main] TRACE com.mybatis.dao.IEmptDao.getEmptAndItsRoles - <== Row: 1, 设计部, 2, wang, 2017-08-22, 1
14:17:40.877 [main] TRACE com.mybatis.dao.IEmptDao.getEmptAndItsRoles - <== Row: 2, 人事部, 3, yang, 2017-08-22, 2
14:17:40.894 [main] TRACE com.mybatis.dao.IEmptDao.getEmptAndItsRoles - <== Row: 3, 研发部, 4, huang, 2017-08-22, 3
14:17:40.901 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptAndItsRoles - <== Total: 4
设计部 lee
方式二的不同的是,先查找部门,再根据部门ID中来获取其部门中所有人员
//方式二
14:19:32.307 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptAndItsRoles1 - ==> Preparing: select * from empt
14:19:32.477 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptAndItsRoles1 - ==> Parameters:
14:19:32.665 [main] TRACE com.mybatis.dao.IEmptDao.getEmptAndItsRoles1 - <== Columns: empt_id, empt_name
14:19:32.666 [main] TRACE com.mybatis.dao.IEmptDao.getEmptAndItsRoles1 - <== Row: 1, 设计部
14:19:32.675 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleByEmpt - ====> Preparing: select * from role where empt_id = ?
14:19:32.677 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleByEmpt - ====> Parameters: 1(Integer)
14:19:32.679 [main] TRACE com.mybatis.dao.IRoleDao.getRoleByEmpt - <==== Columns: role_id, role_name, role_birthday, empt_id
14:19:32.682 [main] TRACE com.mybatis.dao.IRoleDao.getRoleByEmpt - <==== Row: 1, lee, 2017-08-22, 1
14:19:32.687 [main] TRACE com.mybatis.dao.IRoleDao.getRoleByEmpt - <==== Row: 2, wang, 2017-08-22, 1
14:19:32.688 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleByEmpt - <==== Total: 2
14:19:32.695 [main] TRACE com.mybatis.dao.IEmptDao.getEmptAndItsRoles1 - <== Row: 2, 人事部
14:19:32.696 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleByEmpt - ====> Preparing: select * from role where empt_id = ?
14:19:32.697 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleByEmpt - ====> Parameters: 2(Integer)
14:19:32.705 [main] TRACE com.mybatis.dao.IRoleDao.getRoleByEmpt - <==== Columns: role_id, role_name, role_birthday, empt_id
14:19:32.706 [main] TRACE com.mybatis.dao.IRoleDao.getRoleByEmpt - <==== Row: 3, yang, 2017-08-22, 2
14:19:32.709 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleByEmpt - <==== Total: 1
14:19:32.713 [main] TRACE com.mybatis.dao.IEmptDao.getEmptAndItsRoles1 - <== Row: 3, 研发部
14:19:32.716 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleByEmpt - ====> Preparing: select * from role where empt_id = ?
14:19:32.720 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleByEmpt - ====> Parameters: 3(Integer)
14:19:32.723 [main] TRACE com.mybatis.dao.IRoleDao.getRoleByEmpt - <==== Columns: role_id, role_name, role_birthday, empt_id
14:19:32.724 [main] TRACE com.mybatis.dao.IRoleDao.getRoleByEmpt - <==== Row: 4, huang, 2017-08-22, 3
14:19:32.739 [main] DEBUG com.mybatis.dao.IRoleDao.getRoleByEmpt - <==== Total: 1
14:19:32.742 [main] DEBUG com.mybatis.dao.IEmptDao.getEmptAndItsRoles1 - <== Total: 3
设计部 lee
例子: 现在有两个“项目” ,每个项目都有几个 “员工” 负责。而一个 “员工”负责 一个或多个 “项目”。因此,当我们获取 一个“员工”需要获得多个“项目”,获取一个“项目”需要获得多个“员工”。这就是多对多关系,同时我们需要一个独立的关系映射表。实际上,多对多和一对多很相似,映射也是相同的,就是有个 中间 关系映射 的过渡而已。
(1)项目表
(2)员工表
(3)关系映射表
(4)关系模型
package com.mybatis.entity;
import java.util.List;
/**
* 封装项目信息的实体类
*
* @author lee
*/
public class Project {
private int proId;
private String proName;
/**
* 引用了 负责该项目 的所有员工集合
*
*/
private List pers;
public int getProId() {
return proId;
}
public void setProId(int proId) {
this.proId = proId;
}
public String getProName() {
return proName;
}
public void setProName(String proName) {
this.proName = proName;
}
public List getPers() {
return pers;
}
public void setPers(List pers) {
this.pers = pers;
}
}
package com.mybatis.entity;
import java.util.List;
/**
* 封装员工的信息的 实体类
*
* @author lee
*
*/
public class Person {
private int perId;
private String perName;
/**
* 引用 该员工 负责的 项目集合
*
*/
private List pros;
public int getPerInt() {
return perId;
}
public void setPerInt(int perInt) {
this.perId = perInt;
}
public String getPerName() {
return perName;
}
public void setPerName(String perName) {
this.perName = perName;
}
public List getPros() {
return pros;
}
public void setPros(List pros) {
this.pros = pros;
}
}
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
<setting name="logImpl" value="LOG4J2"/>
settings>
<typeAliases>
<typeAlias alias="Pro" type="com.mybatis.entity.Project" />
<typeAlias alias="Per" type="com.mybatis.entity.Person"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver">property>
<property name="url" value="jdbc:mysql://localhost:3306/hibernate">property>
<property name="username" value="root">property>
<property name="password" value="qwertyuio">property>
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/mybatis/dao/impl/PersonMapper.xml" />
<mapper resource="com/mybatis/dao/impl/ProjectMapper.xml" />
mappers>
configuration>
PersonMapper.xml
<mapper namespace="com.mybatis.dao.IPersonDao">
<select id="getPersonById" resultType="Per">
select * from per where per_id = #{perId}
select>
<resultMap type="Per" id="perMap1">
<id property="perId" column="per_id" />
<result property="perName" column="per_Name" />
<collection property="pros" javaType="ArrayList" ofType="Pro">
<id property="proId" column="pro_id" />
<result property="proName" column="pro_name" />
collection>
resultMap>
<select id="getPersons" resultMap="perMap1">
select pr.*,pe.* from pro pr,per pe,pro_per pp
where pr.pro_id = pp.pro_id and pe.per_id = pp.per_id
select>
<resultMap type="Per" id="perMap2">
<id property="perId" column="per_id" />
<result property="perName" column="per_Name" />
<collection property="pros" javaType="List" ofType="Pro" column="pro_id" select="com.mybatis.dao.IProjectDao.getProjectById" />
resultMap>
<select id="getPersons1" resultMap="perMap2">
select pr.*,pe.* from pro pr,per pe,pro_per pp
where pe.per_id = pp.per_id
select>
mapper>
ProjectMapper.xml
<mapper namespace="com.mybatis.dao.IProjectDao">
<select id="getProjectById" resultType="Pro">
select * from pro where pro_id = #{proId}
select>
<resultMap type="Pro" id="proMap1">
<id property="proId" column="pro_id" />
<result property="proName" column="pro_Name" />
<collection property="pers" javaType="ArrayList" ofType="Per">
<id property="perId" column="per_id" />
<result property="perName" column="per_name" />
collection>
resultMap>
<select id="getProjects" resultMap="proMap1">
select pr.*,pe.* from pro pr,per pe,pro_per pp
where pr.pro_id = pp.pro_id and pe.per_id = pp.per_id
select>
<resultMap type="Pro" id="proMap2">
<id property="proId" column="pro_id" />
<result property="proName" column="pro_Name" />
<collection property="pers" javaType="List" ofType="Per" column="pro_id" select="com.mybatis.dao.IPersonDao.getPersonById" />
resultMap>
<select id="getProjects1" resultMap="proMap2">
select pr.*,pe.* from pro pr,per pe,pro_per pp
where pr.pro_id = pp.pro_id
select>
mapper>
IPersonDao
package com.mybatis.dao;
import java.util.List;
import com.mybatis.entity.Person;
public interface IPersonDao {
public List getPersons();
public List getPersons1();
public Person getPersonById(int perId);
}
IProjectDao
package com.mybatis.dao;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.mybatis.entity.Role;
@Repository
public interface IRoleDao {
public List getRoleByEmpt(int emptId);
public List getRoleInOneEmpt();
public List getRoleInOneEmpt1();
}
package com.mybatis.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.mybatis.entity.Project;
import com.mybatis.utils.MybatisUtils;
public class ProjectDaoTest {
//方式1
@Test
public void test() {
SqlSession session = MybatisUtils.openSession();
IProjectDao proDao = session.getMapper(IProjectDao.class);
List pros = proDao.getProjects();
session.commit();
MybatisUtils.closeSession(session);
}
//方式2
@Test
public void test1() {
SqlSession session = MybatisUtils.openSession();
IProjectDao proDao = session.getMapper(IProjectDao.class);
List pros = proDao.getProjects1();
session.commit();
MybatisUtils.closeSession(session);
}
}
可以看到,我们在查询 项目的时候, 同时把 该项目的员工们一起查询出来了。
//方式一:
20:31:23.395 [main] DEBUG com.mybatis.dao.IProjectDao.getProjects - ==> Preparing: select pr.*,pe.* from pro pr,per pe,pro_per pp where pr.pro_id = pp.pro_id and pe.per_id = pp.per_id
20:31:23.632 [main] DEBUG com.mybatis.dao.IProjectDao.getProjects - ==> Parameters:
20:31:23.890 [main] TRACE com.mybatis.dao.IProjectDao.getProjects - <== Columns: pro_id, pro_name, per_id, per_name
20:31:23.897 [main] TRACE com.mybatis.dao.IProjectDao.getProjects - <== Row: 1, project1, 1, lee
20:31:23.917 [main] TRACE com.mybatis.dao.IProjectDao.getProjects - <== Row: 1, project1, 2, wang
20:31:23.930 [main] TRACE com.mybatis.dao.IProjectDao.getProjects - <== Row: 1, project1, 4, qiang
20:31:23.943 [main] TRACE com.mybatis.dao.IProjectDao.getProjects - <== Row: 2, project2, 1, lee
20:31:23.949 [main] TRACE com.mybatis.dao.IProjectDao.getProjects - <== Row: 2, project2, 3, yang
20:31:23.953 [main] TRACE com.mybatis.dao.IProjectDao.getProjects - <== Row: 2, project2, 4, qiang
20:31:23.955 [main] DEBUG com.mybatis.dao.IProjectDao.getProjects - <== Total: 6
可以看到,我们先把项目查询出来, 再根据项目的id将该项目员工查询出
//方式二
20:32:02.570 [main] DEBUG com.mybatis.dao.IProjectDao.getProjects1 - ==> Preparing: select pr.*,pe.* from pro pr,per pe,pro_per pp where pr.pro_id = pp.pro_id
20:32:02.797 [main] DEBUG com.mybatis.dao.IProjectDao.getProjects1 - ==> Parameters:
20:32:02.932 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Columns: pro_id, pro_name, per_id, per_name
20:32:02.937 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 1, lee
20:32:02.960 [main] DEBUG com.mybatis.dao.IPersonDao.getPersonById - ====> Preparing: select * from per where per_id = ?
20:32:02.967 [main] DEBUG com.mybatis.dao.IPersonDao.getPersonById - ====> Parameters: 1(Integer)
20:32:02.979 [main] TRACE com.mybatis.dao.IPersonDao.getPersonById - <==== Columns: per_id, per_name
20:32:02.990 [main] TRACE com.mybatis.dao.IPersonDao.getPersonById - <==== Row: 1, lee
20:32:02.999 [main] DEBUG com.mybatis.dao.IPersonDao.getPersonById - <==== Total: 1
20:32:03.003 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 1, lee
20:32:03.007 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 1, lee
20:32:03.009 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 1, lee
20:32:03.015 [main] DEBUG com.mybatis.dao.IPersonDao.getPersonById - ====> Preparing: select * from per where per_id = ?
20:32:03.016 [main] DEBUG com.mybatis.dao.IPersonDao.getPersonById - ====> Parameters: 2(Integer)
20:32:03.028 [main] TRACE com.mybatis.dao.IPersonDao.getPersonById - <==== Columns: per_id, per_name
20:32:03.029 [main] TRACE com.mybatis.dao.IPersonDao.getPersonById - <==== Row: 2, wang
20:32:03.032 [main] DEBUG com.mybatis.dao.IPersonDao.getPersonById - <==== Total: 1
20:32:03.036 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 1, lee
20:32:03.039 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 1, lee
20:32:03.136 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 2, wang
20:32:03.159 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 2, wang
20:32:03.167 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 2, wang
20:32:03.176 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 2, wang
20:32:03.182 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 2, wang
20:32:03.202 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 2, wang
20:32:03.213 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 3, yang
20:32:03.264 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 3, yang
20:32:03.270 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 3, yang
20:32:03.276 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 3, yang
20:32:03.293 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 3, yang
20:32:03.296 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 3, yang
20:32:03.303 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 4, qiang
20:32:03.306 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 4, qiang
20:32:03.308 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 1, project1, 4, qiang
20:32:03.314 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 4, qiang
20:32:03.315 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 4, qiang
20:32:03.318 [main] TRACE com.mybatis.dao.IProjectDao.getProjects1 - <== Row: 2, project2, 4, qiang
20:32:03.333 [main] DEBUG com.mybatis.dao.IProjectDao.getProjects1 - <== Total: 24
关联元素处理“有一个”类型的关系。比如,在我们的示例中,一个博客有一个用户。 关联映射就工作于这种结果之上。你指定了目标属性,来获取值的列,属性的 java 类型(很 多情况下 MyBatis 可以自己算出来) ,如果需要的话还有 jdbc 类型,如果你想覆盖或获取的 结果值还需要类型控制器。
关联中不同的是你需要告诉 MyBatis 如何加载关联。MyBatis 在这方面会有两种不同的 方式:
我们有两个查询语句:一个来加载博客,另外一个来加载作者,而且博客的结果映射描 述了“selectAuthor”语句应该被用来加载它的 author 属性。其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。这种方式很简单, 但是对于大型数据集合和列表将不会表现很好。 问题就是我们熟知的 “N+1 查询问题”。概括地讲,N+1 查询问题可以是这样引起的:
MyBatis 能延迟加载这样的查询就是一个好处,因此你可以分散这些语句同时运行的消 耗。然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所有的延迟加 载,这样的行为可能是很糟糕的。
通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。
动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。
动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分。比如:
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE 1 = 1
<if test="title != null">
AND title like #{title}
if>
select>
有些时候,我们不想用到所有的条件语句,而只想从中择其一二。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE 1 = 1
<choose>
<when test="title != null">
AND title like #{title}
when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
when>
<otherwise>
AND featured = 1
otherwise>
choose>
select>
现在考虑回到“if”示例,为什么我们在where 后面有个 “1=1”的条件呢?
先来看看这个例子:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
if>
<if test="title != null">
AND title like #{title}
if>
select>
为了防止,出现这种情况,Mybatis提供了trim, where, set 来帮助我们解决这个问题。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
if>
<if test="title != null">
AND title like #{title}
if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
if>
where>
select>
如果 where 元素没有按正常套路出牌,我们还是可以通过自定义 trim 元素来定制我们想要的功能。
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
trim>
类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的。
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},if>
<if test="password != null">password=#{password},if>
<if test="email != null">email=#{email},if>
<if test="bio != null">bio=#{bio}if>
set>
where id=#{id}
update>
动态 SQL 的另外一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
foreach>
select>
bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。比如:
<select id="selectBlogsLike" resultType="Blog">
"pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
select>