映射器时MyBatis最复杂最重要的组件,由一个接口和一个XML文件组成。本文档主要讲解映射器的主要配置元素。
在数据库里创建一个表,并插入几条数据用于举例。
create table `t_heroes` (
`id` INT(11) NOT NULL,
`hero_name` VARCHAR(30),
`hero_title` VARCHAR(30),
`main_property` ENUM('力量','敏捷','智力'),
`note` VARCHAR(100),
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
insert into `t_heroes` (`id`, `hero_name`, `hero_tilte`,`main_property`,`note`)
values
(1, '孙悟空','齐天大圣','力量','大圣的攻击距离相比其它近战要远擅长四号位游走辅助。'),
(2, '维洛格罗斯','孽主','力量','召唤成波深渊之焰,焚祭在恶意掌控中无力还手的敌人'),
(3, '亚巴顿','地狱领主','力量','内心坚信自己的使命是誓死保护世袭的传统和领域的风俗'),
(4, '伊卡洛斯','凤凰','力量','永世的黑暗中,光之守护者的第一个太阳之光轻微闪过'),
(5, '美杜莎','蛇发女妖','敏捷','睥睨万物才是值得拥有的美貌—这才是能够改变世界的力量'),
(6, '乌尔萨','熊战士','敏捷','战士乌尔萨是熊怪部落最强大的一员,保护着他的家园和人民');
select元素
select元素代表SQL的select语句,用于查询。
看一个简单的例子。
定义一个Hero类实体。
package com.wyk.mybatisDemo.pojo;
import java.io.Serializable;
import org.apache.ibatis.type.Alias;
@Alias("hero")
public class Hero implements Serializable{
private static final long serialVersionUID = 133743049674009786L;
private int id;
private String heroName;
private String heroTitle;
private String mainProperty;
private String note;
public Hero() {}
public Hero(int id, String heroName, String heroTitle, String mainProperty,
String note) {
this.id = id;
this.heroName = heroName;
this.heroTitle = heroTitle;
this.mainProperty = mainProperty;
this.note = note;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getHeroName() {
return heroName;
}
public void setHeroName(String heroName) {
this.heroName = heroName;
}
public String getHeroTitle() {
return heroTitle;
}
public void setHeroTitle(String heroTitle) {
this.heroTitle = heroTitle;
}
public String getMainProperty() {
return mainProperty;
}
public void setMainProperty(String mainProperty) {
this.mainProperty = mainProperty;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public String toString() {
return String.format("[id={%d}, heroName={%s}, heroTitle={%s}," +
"mainProperty={%s}, note={%s}]", this.id, this.heroName,
this.heroTitle, this.mainProperty, this.note);
}
}
<select id="getHeroById" parameterType="int" resultType="com.wyk.mybatisDemo.pojo.Hero">
select id, hero_name as heroName, hero_title as heroTitle, main_property as mainProperty,
note from t_heroes where id = #{id}
</select>
简单介绍一下各个元素:
- id用于标识这条SQL。
- parameterType表示这条SQL的参数类型。
- resultType表示这条SQL返回的结果类型。
另外,还有一个接口方法。
public Hero getHeroById(int id);
自动映射
settings元素中的选项autoMappingBehavior用于自动映射。它的取值范围:
- NONE, 不进行自动映射。
- PARTIAL,默认值,只对没有嵌套结果集进行映射。
- FULL, 对所有结果进行映射,包括嵌套结果集。
一般情况下使用默认值就行。
继续看前面的例子,如果编写的SQL列名和属性名一致,就会形成自动映射。原来的列名都使用别名代替,保持二者一致。
如果将mapUnderscoreToCamelCase设置为true,可以开启驼峰映射,但会降低灵活性,使用较少。
传递多个参数
还是上面的例子,改为根据英雄姓名(heroName)和英雄称号(heroTitle)查找英雄信息,有多个参数。
使用map接口传递参数
接口方法定义为:
public Hero getHeroByMap(Map<String, Object> parameterType);
其中参数是一个map,SQL如下:
<select id="getHeroByMap" parameterType="map" resultType="hero">
select id, hero_name as heroName, hero_title as heroTitle, main_property as mainProperty,
note from t_heroes where hero_name = #{heroName} and hero_title = #{heroTitle}
</select>
调用方式如下:
SqlSession sqlSession = DataConnection.openSqlSession();
HeroMapper heroMapper = sqlSession.getMapper(HeroMapper.class);
Map<String, Object> parameterMap = new HashMap<String, Object>();
parameterMap.put("heroName", "伊卡洛斯");
parameterMap.put("heroTitle", "凤凰");
Hero hero = heroMapper.getHeroByMap(parameterMap);
System.out.println(hero);
将参数名称和内容一一放入map即可。map很少使用,原因是:
1.可读性差,使用者只有阅读它的键,才能明白作用;2.map无法限定参数的数据类型。
使用注解传递多个参数
使用注解@Param(org.apache.ibatis.annotations.Param)可以定义映射器的参数名称,并且使其拥有良好的可读性。
public Hero getHeroByAnnotation(@Param("heroName") String heroName, @Param("heroTitle") String heroTitle);
映射文件中的SQL如下, 并不需要指明parameterType,MyBatis可以自动识别。
<select id="getHeroByAnnotation" resultType="hero">
select id, hero_name as heroName, hero_title as heroTitle, main_property as mainProperty,
note from t_heroes where hero_name = #{heroName} and hero_title = #{heroTitle}
</select>
这种方法在参数数量不是很多的时候比较适用。但如果参数过多,使用起来就很不容易。
通过Java Bean传递参数
定义一个参数集的实体类HeroParams。
package com.wyk.mybatisDemo.pojo;
public class HeroParams {
private String heroName;
private String heroTitle;
public String getHeroName() {
return heroName;
}
public void setHeroName(String heroName) {
this.heroName = heroName;
}
public String getHeroTitle() {
return heroTitle;
}
public void setHeroTitle(String heroTitle) {
this.heroTitle = heroTitle;
}
}
接口方法:
public Hero getHeroByBean(HeroParams heroParams);
查询语句:
<select id="getHeroByBean" resultType="hero">
select id, hero_name as heroName, hero_title as heroTitle, main_property as mainProperty,
note from t_heroes where hero_name = #{heroName} and hero_title = #{heroTitle}
</select>
使用resultMap映射结果集
前面讲了自动映射和驼峰映射。当结果集比较复杂的时候,更建议使用resultMap属性。前面例子中的结果可以表示为如下,其中id代表resultMap主键,resultMap代表其属性,把java类的属性和SQL列名做一一对应。
<resultMap type="hero" id="heroMap">
<id property="id" column="id" />
<result property="heroName" column="hero_name" />
<result property="heroTitle" column="hero_title" />
<result property="mainProperty" column="main_property" />
<result property="note" column="note" />
</resultMap>
结果集同样可以使用map存储,也是不推荐的,理由和参数部分相同,可读性差。
分页参数RowBounds
MyBatis支持分页,有一个专门处理分页的类RowBounds。使用十分简单,在接口增加一个RowBounds参数。
public List<Hero> findByRowBounds(RowBounds rowBounds);
映射文件无需变化。
<select id="findByRowBounds" resultMap="heroMap">
select id, hero_name as heroName, hero_title as heroTitle, main_property as mainProperty,
note from t_heroes
</select>
测试RowBounds。他有2个参数,偏移量和限制条数。
@Test
public void testfindByRowBounds() {
SqlSession sqlSession = null;
try {
sqlSession = DataConnection.openSqlSession();
HeroMapper heroMapper = sqlSession.getMapper(HeroMapper.class);
//分页,偏移量和限制条数
RowBounds rowBounds = new RowBounds(0, 5);
List<Hero> heroes = heroMapper.findByRowBounds(rowBounds);
for(int i = 0; i < heroes.size(); i++) {
System.out.println(heroes.get(i));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
}
insert元素
insert语句用来插入元素,返回受影响的记录数(即插入的记录数)。
一个简单的例子。
<insert id="insertHero" parameterType="hero">
insert into t_heroes(hero_name, hero_title, main_property, note)
values(#{heroName}, #{heroTitle}, #{mainProperty}, #{note})
</insert>
主键回填
继续看上面的例子,由于主键是递增的,所以不需要输入。但在一些业务场景下我们又需要知道主键是什么,在JDBC中可以使用getGenerateKeys方法获取数据库生成的主键(需要数据库驱动支持),在MyBatis的insert语句中,可以通过useGeneratedKeys属性打开这个功能,它的默认值是false。还需要配置keyProperty或者keyColumn,告诉系统把主键放到哪个属性中。如果存在多个主键,就要用逗号隔开。
写个例子。
<insert id="insertHeroAndGetKey" parameterType="hero"
useGeneratedKeys="true" keyProperty="id">
insert into t_heroes(hero_name, hero_title, main_property, note)
values(#{heroName}, #{heroTitle}, #{mainProperty}, #{note})
</insert>
创建接口。
public int insertHeroAndGetKey(Hero hero);
写个测试用例测试一下。
@Test
public void testInsertHeroAndGetKey() {
SqlSession sqlSession = DataConnection.openSqlSession();
HeroMapper heroMapper = sqlSession.getMapper(HeroMapper.class);
Hero hero = new Hero();
hero.setHeroName("傲洛斯");
hero.setHeroTitle("寒冬飞龙");
hero.setMainProperty("智力");
hero.setNote("就像许多伟大的诗人,傲洛斯只想用她的一生书写诗篇");
int c = heroMapper.insertHeroAndGetKey(hero);
//insert语句需要commit
sqlSession.commit();
System.out.println(c);
System.out.println(hero.getId());
}
自定义主键
如果主键依赖其它生成规则,则可以使用selectKey元素进行支持。
如下示例:
<insert id="insertHero" parameterType="hero">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select if (max(id) = null, 1, max(id) + 3) from t_heroes
</selectKey>
insert into t_heroes(hero_name, hero_title, main_property, note)
values(#{heroName}, #{heroTitle}, #{mainProperty}, #{note})
</insert>
示例中的规则是如果表中不存在id,则id设置为1,如果存在,则在当前最大id的基础上加3。keyProperty指明使用的属性,order指明它在当前SQL执行之前执行。
update元素和delete元素
更新和删除语句,也是返回影响的行数,比较简单,不再详细说明。
<update id="updateHero" parameterType="hero">
update t_heroes set hero_name = #{heroName} where id = #{id}
</update>
<delete id="deleteHero" parameterType="int">
delete from t_heroes where id = #{id}
</delete>
sql元素
sql语句可以定义一条SQL的一部分,方便其它SQL引用,比如列名,还支持变量传递。
<sql id="heroCols">
id, hero_name, hero_title, main_property, note
</sql>
<select id="getHeroById2" parameterType="int" resultMap="heroMap">
select <include refid="heroCols" /> where id = #{id}
</select>
本文档主要讲解了MyBatis映射器的基本使用,比较复杂的使用留到下一章。