MyBatis学习笔记(1)—使用篇
MyBatis学习笔记(2)—映射关系篇
...敬请期待
通过上一篇的入门MyBatis学习笔记(1)—使用篇 ,可以发现除了一些基本配置外,Mybatis的精华就在于Mapper(映射)部分。Mybatis是针对映射器构造的SQL构建的轻量级框架,并且可以通过配置生成对应的JavaBean给调用者。在Mybatis中可以灵活使用SQL来满足各种不同场景的需求,所以在互联网企业内更偏爱于使用Mybatis来应对高速变化的需求,而传统企业更偏向于使用Hibernate。
映射器的主要元素
上一节我们仅仅定义了一个select语句:
<mapper namespace="com.shuqing28.dao.CommodityDao" >
<select id="getAllCommodity" resultType="com.shuqing28.pojo.Commodity" >
SELECT * FROM commodity
</select>
</mapper>
实际上增删改查在Mybatis映射器里面都有对应的元素
元素名称
描述
备注
select
查询语句,最常用的
可以自定义参数,返回结果集
insert
插入语句
执行后返回一个整数,代表插入的条数
update
更新语句
执行后返回一个整数,代表更新的条数
delete
删除语句
执行后返回一个整数,代表删除的条数
parameterMap
定义参数映射关系
即将被删除的元素,不建议使用
sql
允许定义一部分的SQL,然后在各个地方引用
例如,定义一个表名,在其它地方的SQL语句中使用
resultMap
用来描述从数据库结果集中加载对象
它将提供映射规则
cache
给定命名空间的缓存配置
————
cache-ref
其他命名空间缓存配置的引用
————
下面我们探究主要使用的几个元素
select
select元素是我们最常用的元素,正如在SQL里是查询功能也是最主要的功能之一。在我们常识里,查询语句通常都是根据一个或者多个 条件,查询数据库返回一个,多个或者所有字段 。之前我们举得getAllCommodity
没有查询条件,返回所有字段。
在Mybatis里,我们可以传入简单的参数类型,像int,float,String这些,也可以传入一些复杂的类型,比如JavaBean、Map之流的,Mybatis把这些参数转换成SQL语句需要的类型后,返回结果,并且通过映射,将结果集自动绑定到JavaBean中,所以JDBC里面那一套一个一个从ResultSet获取值的操作都被省略了。
下面我们看一个有参数的select语句:
<select id="getCommodityById" parameterType="int" resultType="com.shuqing28.pojo.Commodity" >
SELECT * FROM commodity where id=
</select>
同时我们定义了接口方法:
Commodity getCommodityById(Integer id);
不同于getAllCommodity
没有传入参数,getCommodityById
有一个int类型的传入参数,返回的还是Commodity。
上面的select语句包含以下属性:
id 标识了这条sql,id与接口名保持一致
parameterType 定义参数类型,这里就是int
resultType 定义了返回值类型,这里是我们定义的POJO类
这里看resultType,我们查询的是commodity的所有字段,再次回顾字段定义:
字段名
类型
id
int
name
varchar(255)
price
decimal(8,2)
description
varchar(255)
而我们的POJO类包含:
private Integer id;
private String name;
private Double price;
private String description;
这里Mybatis帮我们完成了自动映射 ,只要返回的SQL列名和JavaBean的属性一致,Mybatis就可以帮助我们自动回填这些字段而无需任何配置。我们可以再Mybatis的配置文件里,通过autoMappingBehavior来设置自动映射,它包含3个值:
NONE :取消自动映射
PARTIAL :这也是默认值,只会自动映射,没有定义嵌套结果集映射的结果集
FULL :会自动映射任意复杂的结果集(无论是否嵌套)
刚才我们举例的是只传递了一个参数,如果传递多个参数呢?这时候就要说说输入映射了。
输入映射
传递多个参数通常来说有3种方式,通过Map,通过注解,以及通过POJO对象
通过Map传递参数
假设我们想查询带有饼 的食物,并且价格低于80的。那么我们可以这么写SQL语句
<select id="getCommodityByMap" parameterType="map" resultType="com.shuqing28.pojo.Commodity" >
SELECT * FROM commodity WHERE name like '%${name}%' AND price<
</select>
因为mybatis使用xml定义Mapper,所以这里'<'用了转义符<
表示,其实还是一样的。
parameterType定义为map,我们定义的接口也是接受Map作为参数
List<Commodity> getCommodityByMap(Map<String, Object> params);
使用:
CommodityDao commodityDao = sqlSession.getMapper(CommodityDao.class);
Map<String, Object> paramsMap = new HashMap<String , Object>();
paramsMap.put("name" , "饼" );
paramsMap.put("price" , 80.0);
List<Commodity> commodities = commodityDao.getCommodityByMap(paramsMap);
最后打印commodities:
[Commodity{id=1001, name='野葡萄烤饼' , price=10.0, description='吃完还有点饿' }, Commodity{id=1003, name='南瓜百吉饼' , price=50.0, description='份大量足,可以去很远的地方' }]
虽然Map很简单,但是光是看传入参数Map,你不知道包含了怎样的内容,若是深入还得看如何设置Map的,而且使用时还要一项项指定Map的Key的名字,所以不怎么提倡使用了。
使用注解方式传递参数
注解方式使用了**@Param**注解,我们这样定义接口:
List<Commodity> getCommodityByAnnation(@Param("name" ) String name, @Param("price" ) Double price);
Mybatis根据@Param提供的名称,把变量值传到SQL语句中对应的参数中,参数的可读性大大提高,但是不足之处在于,若是参数很多,一个一个写很麻烦,降低了可读性。
所以最经常使用的还是利用POJO传递参数。
使用POJO传递参数
通常我们只需要传递简单的POJO对象就可以了,比如我们这样定义select元素:
<select id="getCommodityByPOJO" parameterType="com.shuqing28.pojo.Commodity" resultType="com.shuqing28.pojo.Commodity" >
SELECT * FROM commodity WHERE name like '%${name}%' AND price<
</select>
测试:
@Test
public void getLowPriceCommodityByPOJO (){
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
CommodityDao commodityDao = sqlSession.getMapper(CommodityDao.class);
Commodity commodity = new Commodity();
commodity.setName("饼" );
commodity.setPrice(80.0);
List<Commodity> commodities = commodityDao.getCommodityByPOJO(commodity);
System.out.println(commodities);
} finally {
sqlSession.close();
}
}
也会返回和上面一样的结果,这里Mybatis会把POJO里对应的属性值传到SQL里,然后返回结果,注意看传入参数就是POJO的完全限定名,和ResultType是一样的,其实可以通过别名减少名称的长度,我们只需要在mybatis-config.xml
中的configuration中定义:
<type Aliases>
<type Alias alias ="commodity" type ="com.shuqing28.pojo.Commodity" />
</type Aliases>
就可以用commodity替换所有的全称了,但是不能有重名的,否则会出错。
有些负责的情况,我们需要定义POJO作为查询参数,比如上面的例子,你只需要两个输入参数,你也可以定制一个只包含这两个参数的POJO。
public class CommodityCustom {
private String name;
private Double price;
当然使用过程还是一样的,定义自己包装过的POJO通常用来解决传入查询条件很复杂的情况,比如设计到几张表联查的,这时候原先定义的POJO解决不了问题,就需要定义一些复合的POJO。
在输入参数中还有个经常被提到的问题,即#和$的区别
#和$的区别
Mybatis本身是基于JDBC封装的。所以#{para}
是预编译处理(PreparedStatement
),相当于原来的?
,而${para}
是字符串替换。Mybatis在处理#时,会调用PreparedStatement
的set
系列方法来赋值;处理$
时,就是把${para}
替换成变量的值。#
方式能够很大程度防止sql
注入,$
方式一般用于传入数据库对象,例如传入表名,一般能用#
的就别用$
.
说完输入我们说输出映射。
输出映射
MyBatis的输出映射分为两种方式,resultType和resultMap
resultType
我们上面所有的例子里都是定义的resultType,resultType可以是简单类型,比如我们想获取商品数量啊之类的,还有就是输出POJO对象或者POJO列表。
不管是POJO对象还是POJO列表,我们在resultType中的定义都是一样的,只不过接口定义不一样:
单个对象
Commodity getCommodityById(Integer id);
Mybatis根据接口返回值判断返回一条对象,如果用过ibatis的可以知道内部调用了session.selectOne。
返回列表
List<Commodity> getCommodityByPOJO(Commodity commodity);
内部使用session.selectList,Mapper接口使用List<XXX>
作为返回值。
查询出来的列名和POJO属性只要有一个一致就会创建POJO对象,顶多别的字段为默认值,但是如果全部不一致,就不会创建该POJO对象了。
resultMap
上面的resultType在查询的列名和POJO属性值一致的时候才可以映射成功,如果不一致的话,就需要resultMap登场表演了。
如果查询出来的列名和POJO的属性名不一致,通过定义一个resultMap对列名和POJO属性名之间作一个映射关系。
在使用resultMap前我们需要定义resultMap
假设我们查询商品类的两个字段id和name,但是查询的时候定义为id_
和name_
。列名和属性名不一致了,先定义resultMap
<resultMap id="commodityResultMap" type ="commodity" >
<id column="id_" property="id" />
<result column="name_" property="name" />
</resultMap>
其中
id 标识这个resultMap
type 标识映射哪个POJO里面的属性,这里因为上面说过别名了,所以就是映射的Commodity这个POJO
id 元素标识这个对象的主键,result就是普通字段
property 代表POJO的属性名称
column 表示数据库SQL的列名
再看我们的select元素:
<select id="getCommodityByPrice" parameterType="double" resultMap="commodityResultMap" >
SELECT id id_, name name_ FROM USER WHERE price=
</select>
使用resultType进行输出映射时,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。 此外,resultMap还可以做一对多、多对多等高级映射,这些内容将在后续文章中介绍。
insert
insert相对于select简单很多,下面是往商品表插入一个商品的实例:
<insert id="insertCommodity" useGeneratedKeys="true"
keyProperty="id" >
insert into commodity (name,price,description)
values (
</insert>
接口为:
void insertCommodity(Commodity commodity);
这里我们注意到了insert语句里多了两个属性useGeneratedKeys
和keyProperty
,这里涉及到的是主键回填的概念。
useGeneratedKeys :告诉Mybatis是否使用数据库内置策略生成主键
keyProperty :告诉Mybatis哪一列是主键
这样我们无须传入id值,Mybatis就可以帮我们设置主键,同时还会回填POJO内的id值,当我们像下面这样调用时:
@Test
public void insertCommodity (){
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
CommodityDao commodityDao = sqlSession.getMapper(CommodityDao.class);
Commodity commodity = new Commodity();
//commodity.setId(1005);
commodity.setName("艾蒿小麦饼" );
commodity.setPrice(100.0);
commodity.setDescription("像在艳阳下恋爱" );
commodityDao.insertCommodity(commodity);
System.out.println(commodity.getId());
} finally {
sqlSession.close();
}
}
通过打印我们能得到1005
,说明commodity已经具备了id,这就是回填的效果。
在这里,系统默认的方式是给主键值加1,如果我们想要定义自己的主键生成方式,可以使用selectKey进行自定义:
<insert id="insertCommodity" useGeneratedKeys="true"
keyProperty="id" >
<selectKey keyProperty="id" resultType="int" order="BEFORE" >
select if (max(id) is null, 1, max(id) + 2) as newId from commodity
</selectKey>
insert into commodity (name,price,description)
values (
</insert>
这里我们定义主键值是最大id加2,如果还没有记录,则初始化为1。
update和delete
update和的delete都会返回影响的条目数
下面仅仅列出配置的实例:
<update id="updateCommodity" parameterType="commodity" >
update commodity set
name=
price=
description=
where id=
</update>
<delete id="deleteCommodity" parameterType="int" >
delete from commodity where id=
</delete>
这一节主要介绍了主要的几个语句的使用,着重介绍了select语句,同时结合select语句说明了Mybatis中输入映射和输出映射的内容,其它一些高级的映射内容,留到后面的文章介绍。